Skip to content

Commit 337c975

Browse files
Merge dev
* dev: Bugfix: Collision starts outside zone but then enters zone wasn't causing a penalty Update fission/src/mirabuf/ProtectedZoneSceneObject.ts Lint Fix Format & Build Fix Merge Fix Better Naming Better Dropdown Selection Biome & Build Fix Protected Zones Better Tests Selection of type of robot contact required Simple Protected Zone Tests Protected Zone Select When Active Multi Select Dropdown
2 parents 37567c2 + 4a893bd commit 337c975

File tree

8 files changed

+627
-108
lines changed

8 files changed

+627
-108
lines changed

fission/src/mirabuf/ProtectedZoneSceneObject.ts

Lines changed: 113 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ import {
1515
} from "@/util/TypeConversions"
1616
import { deltaFieldTransformsPhysicalProp } from "@/util/threejs/MeshCreation"
1717
import { MiraType } from "./MirabufLoader"
18+
import MatchMode, { MatchModeType } from "@/systems/match_mode/MatchMode"
19+
20+
export enum ContactType {
21+
ROBOT_ENTERS = "Opponent Robot Enters",
22+
ANY_ROBOT_INSIDE = "Collision with Any Robot Inside",
23+
BOTH_ROBOTS_INSIDE = "Collision with Both Robots Inside",
24+
RED_ROBOT_INSIDE = "Collision with Red Robot Inside",
25+
BLUE_ROBOT_INSIDE = "Collision with Blue Robot Inside",
26+
}
1827
import MirabufSceneObject, { RigidNodeAssociate } from "./MirabufSceneObject"
1928

2029
class ProtectedZoneSceneObject extends SceneObject {
@@ -30,7 +39,7 @@ class ProtectedZoneSceneObject extends SceneObject {
3039
shininess: 0.0,
3140
opacity: 0.8,
3241
transparent: true,
33-
}) //0x0000ff
42+
})
3443
static transparentMaterial = new THREE.MeshPhongMaterial({
3544
color: 0x0000,
3645
shininess: 0.0,
@@ -46,14 +55,27 @@ class ProtectedZoneSceneObject extends SceneObject {
4655
private _prefs?: ProtectedZonePreferences
4756
private _joltBodyId?: Jolt.BodyID
4857
private _mesh?: THREE.Mesh
49-
private _collision?: (event: OnContactAddedEvent) => void
50-
private _collisionPersisted?: (event: OnContactPersistedEvent) => void
58+
private _collision?: (event: OnContactAddedEvent | OnContactPersistedEvent) => void
5159
private _collisionRemoved?: (event: OnContactRemovedEvent) => void
5260

5361
private _robotsInside: Map<MirabufSceneObject, number> = new Map()
5462

5563
private _lastRobotCollisionTime: number = 0
5664

65+
private isZoneActive(): boolean {
66+
if (!this._prefs?.activeDuring) {
67+
return [MatchModeType.AUTONOMOUS, MatchModeType.TELEOP, MatchModeType.ENDGAME].includes(
68+
MatchMode.getInstance().getMatchModeType()
69+
)
70+
}
71+
return this._prefs.activeDuring.includes(MatchMode.getInstance().getMatchModeType())
72+
}
73+
74+
private isRobotInside(robot: MirabufSceneObject): boolean {
75+
const timeInside = this._robotsInside.get(robot) ?? 0
76+
return Date.now() - timeInside < 100
77+
}
78+
5779
public constructor(parentAssembly: MirabufSceneObject, index: number, render?: boolean) {
5880
super()
5981

@@ -104,8 +126,8 @@ class ProtectedZoneSceneObject extends SceneObject {
104126
this._mesh?.scale.set(props.scale.x, props.scale.y, props.scale.z)
105127
}
106128

107-
// Detect when something enters the zone
108-
this._collision = (event: OnContactAddedEvent) => {
129+
// Detect when something enters or persists in the zone
130+
this._collision = (event: OnContactAddedEvent | OnContactPersistedEvent) => {
109131
const body1 = event.message.body1
110132
const body2 = event.message.body2
111133

@@ -115,55 +137,12 @@ class ProtectedZoneSceneObject extends SceneObject {
115137
this.zoneCollision(body1)
116138
}
117139

118-
// If the preference is set to require robot contact, we want to penalize robots here
119-
if (!this._prefs?.requireRobotContact) return
120-
const [collisionObjectBody1, collisionObjectBody2] = [body1, body2].map(body => {
121-
const associate = World.physicsSystem.getBodyAssociation(body) as RigidNodeAssociate | undefined
122-
return associate?.sceneObject as MirabufSceneObject | undefined
123-
})
124-
if (!collisionObjectBody1 || !collisionObjectBody2) return
125-
// Makes sure that both robots are from opposing alliances
126-
if (collisionObjectBody1.alliance === collisionObjectBody2.alliance) return
127-
// Ensure that both bodies are robots are inside the zone
128-
if (
129-
Date.now() - (this._robotsInside.get(collisionObjectBody1) ?? 0) > 500 ||
130-
Date.now() - (this._robotsInside.get(collisionObjectBody2) ?? 0) > 500
131-
) {
132-
return
133-
}
134-
// Ensures that infinite collisions do not occur
135-
if (Date.now() - this._lastRobotCollisionTime < 1000) return
136-
this._lastRobotCollisionTime = Date.now()
137-
138-
// Penalize the robot that entered the opposing alliance protected zone
139-
if (collisionObjectBody1.alliance === this._prefs?.alliance) {
140-
SimulationSystem.robotPenalty(
141-
collisionObjectBody2,
142-
this._prefs?.penaltyPoints ?? 0,
143-
`Touched robot in protected zone`
144-
)
145-
} else {
146-
SimulationSystem.robotPenalty(
147-
collisionObjectBody1,
148-
this._prefs?.penaltyPoints ?? 0,
149-
`Touched robot in protected zone`
150-
)
151-
}
140+
// Handle contact-based penalties based on the configured contact type
141+
if (this._prefs?.contactType == ContactType.ROBOT_ENTERS || !this.isZoneActive()) return
142+
this.handleContactPenalty(body1, body2)
152143
}
153144
OnContactAddedEvent.addListener(this._collision)
154-
155-
// Detects when something persists in the zone
156-
this._collisionPersisted = (event: OnContactPersistedEvent) => {
157-
const body1 = event.message.body1
158-
const body2 = event.message.body2
159-
160-
if (body1.GetIndexAndSequenceNumber() == this._joltBodyId?.GetIndexAndSequenceNumber()) {
161-
this.zoneCollision(body2)
162-
} else if (body2.GetIndexAndSequenceNumber() == this._joltBodyId?.GetIndexAndSequenceNumber()) {
163-
this.zoneCollision(body1)
164-
}
165-
}
166-
OnContactPersistedEvent.addListener(this._collisionPersisted)
145+
OnContactPersistedEvent.addListener(this._collision)
167146

168147
// Detects when something leaves the zone
169148
this._collisionRemoved = (event: OnContactRemovedEvent) => {
@@ -224,31 +203,102 @@ class ProtectedZoneSceneObject extends SceneObject {
224203
}
225204
}
226205

227-
if (this._collision) OnContactAddedEvent.removeListener(this._collision)
206+
if (this._collision) {
207+
OnContactAddedEvent.removeListener(this._collision)
208+
OnContactPersistedEvent.removeListener(this._collision)
209+
}
228210
if (this._collisionRemoved) OnContactRemovedEvent.removeListener(this._collisionRemoved)
229211
}
230212

231213
private zoneCollision(collisionID: Jolt.BodyID) {
214+
if (!this.isZoneActive()) return
215+
232216
const associate = <RigidNodeAssociate>World.physicsSystem.getBodyAssociation(collisionID)
233217
const collisionObject = associate.sceneObject as MirabufSceneObject
234-
if (collisionObject.miraType === MiraType.ROBOT && collisionObject.alliance !== this._prefs?.alliance) {
235-
const timeInside = this._robotsInside.get(collisionObject) ?? 0
236-
if (!this._prefs?.requireRobotContact && Date.now() - timeInside > 500) {
237-
SimulationSystem.robotPenalty(
238-
collisionObject,
239-
this._prefs?.penaltyPoints ?? 0,
240-
`Entered protected zone`
241-
)
242-
}
243-
this._robotsInside.set(collisionObject, Date.now())
218+
if (collisionObject.miraType !== MiraType.ROBOT) return
219+
220+
if (
221+
this._prefs?.contactType === ContactType.ROBOT_ENTERS &&
222+
collisionObject.alliance !== this._prefs?.alliance &&
223+
!this.isRobotInside(collisionObject)
224+
) {
225+
SimulationSystem.robotPenalty(collisionObject, this._prefs?.penaltyPoints ?? 0, `Entered protected zone`)
244226
}
227+
228+
this._robotsInside.set(collisionObject, Date.now())
245229
}
246230

247231
private zoneCollisionRemoved(collisionID: Jolt.BodyID) {
248232
const associate = <RigidNodeAssociate>World.physicsSystem.getBodyAssociation(collisionID)
249233
const collisionObject = associate.sceneObject as MirabufSceneObject
250234
this._robotsInside.set(collisionObject, Date.now())
251235
}
236+
237+
private handleContactPenalty(body1: Jolt.BodyID, body2: Jolt.BodyID) {
238+
const [collisionObjectBody1, collisionObjectBody2] = [body1, body2].map(body => {
239+
const associate = World.physicsSystem.getBodyAssociation(body) as RigidNodeAssociate | undefined
240+
return associate?.sceneObject as MirabufSceneObject | undefined
241+
})
242+
243+
if (!collisionObjectBody1 || !collisionObjectBody2) return
244+
if (collisionObjectBody1.miraType !== MiraType.ROBOT || collisionObjectBody2.miraType !== MiraType.ROBOT) return
245+
246+
// Only penalize collisions between robots from different alliances
247+
if (collisionObjectBody1.alliance === collisionObjectBody2.alliance) return
248+
249+
// Ensures that infinite collisions do not occur
250+
if (Date.now() - this._lastRobotCollisionTime < 500) return
251+
252+
let shouldPenalize = false
253+
254+
// Find the robot that has the opposite alliance from the zone
255+
const opposingRobot = [collisionObjectBody1, collisionObjectBody2].find(
256+
robot => robot.alliance !== this._prefs?.alliance
257+
)
258+
if (!opposingRobot) return
259+
switch (this._prefs?.contactType) {
260+
case ContactType.BOTH_ROBOTS_INSIDE:
261+
// Penalize opposing robot if both robots are inside the zone and colliding
262+
if (this.isRobotInside(collisionObjectBody1) && this.isRobotInside(collisionObjectBody2)) {
263+
shouldPenalize = true
264+
}
265+
break
266+
267+
case ContactType.ANY_ROBOT_INSIDE:
268+
// Penalize if any robot is inside the zone when collision occurs
269+
if (this.isRobotInside(collisionObjectBody1) || this.isRobotInside(collisionObjectBody2)) {
270+
shouldPenalize = true
271+
}
272+
break
273+
274+
case ContactType.RED_ROBOT_INSIDE: {
275+
// Penalize if the red robot is inside the zone when collision occurs
276+
const redRobot = [collisionObjectBody1, collisionObjectBody2].find(robot => robot.alliance === "red")
277+
if (redRobot && this.isRobotInside(redRobot)) {
278+
shouldPenalize = true
279+
}
280+
break
281+
}
282+
283+
case ContactType.BLUE_ROBOT_INSIDE: {
284+
// Penalize if the blue robot is inside the zone when collision occurs
285+
const blueRobot = [collisionObjectBody1, collisionObjectBody2].find(robot => robot.alliance === "blue")
286+
if (blueRobot && this.isRobotInside(blueRobot)) {
287+
shouldPenalize = true
288+
}
289+
break
290+
}
291+
}
292+
293+
if (shouldPenalize) {
294+
this._lastRobotCollisionTime = Date.now()
295+
SimulationSystem.robotPenalty(
296+
opposingRobot,
297+
this._prefs?.penaltyPoints ?? 0,
298+
`Contact penalty in protected zone`
299+
)
300+
}
301+
}
252302
}
253303

254304
export default ProtectedZoneSceneObject

fission/src/systems/match_mode/MatchMode.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import RobotDimensionTracker from "./RobotDimensionTracker"
88
import MatchStart from "@/assets/sound-files/MatchStart.wav"
99

1010
export enum MatchModeType {
11-
SANDBOX = 0,
12-
AUTONOMOUS = 1,
13-
TELEOP = 2,
14-
MATCH_ENDED = 3,
11+
SANDBOX = "Sandbox",
12+
AUTONOMOUS = "Autonomous",
13+
TELEOP = "Teleop",
14+
ENDGAME = "Endgame",
15+
MATCH_ENDED = "Match Ended",
1516
}
1617

1718
// Default match mode timing values
@@ -105,6 +106,7 @@ class MatchMode {
105106

106107
endgameStart() {
107108
SoundPlayer.play(beep)
109+
this._matchModeType = MatchModeType.ENDGAME
108110
this._endgame = true
109111
}
110112

fission/src/systems/preferences/PreferenceTypes.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Vector3Tuple } from "three"
22
import { SimConfigData } from "@/ui/panels/simulation/SimConfigShared"
33
import { InputScheme } from "../input/InputSchemeManager"
4+
import { ContactType } from "@/mirabuf/ProtectedZoneSceneObject"
5+
import { MatchModeType } from "@/systems/match_mode/MatchMode"
46

57
/** Names of all global preferences. */
68

@@ -160,7 +162,8 @@ export type ProtectedZonePreferences = {
160162
alliance: Alliance
161163
penaltyPoints: number
162164
parentNode: string | undefined
163-
requireRobotContact: boolean
165+
contactType: ContactType
166+
activeDuring: MatchModeType[]
164167

165168
deltaTransformation: number[]
166169
}
@@ -196,7 +199,11 @@ export function defaultRobotPreferences(): RobotPreferences {
196199
}
197200

198201
export function defaultFieldPreferences(): FieldPreferences {
199-
return { defaultSpawnLocation: [0, 1, 0], scoringZones: [], protectedZones: [] }
202+
return {
203+
defaultSpawnLocation: [0, 1, 0],
204+
scoringZones: [],
205+
protectedZones: [],
206+
}
200207
}
201208

202209
export function defaultMotorPreferences(name: string): MotorPreferences {

fission/src/test/PreferencesSystem.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
MotorPreferences,
77
RobotPreferences,
88
} from "@/systems/preferences/PreferenceTypes"
9+
import { MatchModeType } from "@/systems/match_mode/MatchMode"
10+
import { ContactType } from "@/mirabuf/ProtectedZoneSceneObject"
911

1012
describe("Preferences System Global Values", () => {
1113
test("Setting values", () => {
@@ -202,7 +204,8 @@ describe("Preference System Robot/Field", () => {
202204
parentNode: undefined,
203205
deltaTransformation: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
204206
penaltyPoints: 2,
205-
requireRobotContact: false,
207+
contactType: ContactType.ROBOT_ENTERS,
208+
activeDuring: [MatchModeType.AUTONOMOUS, MatchModeType.TELEOP],
206209
},
207210
],
208211
}

0 commit comments

Comments
 (0)