Skip to content

Commit

Permalink
Tests for object conditions.
Browse files Browse the repository at this point in the history
  • Loading branch information
Neloreck committed Mar 27, 2024
1 parent e864da0 commit c94122c
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 90 deletions.
96 changes: 93 additions & 3 deletions src/engine/scripts/declarations/conditions/object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
mockRegisteredActor,
mockSchemeState,
MockSmartTerrain,
MockSquad,
resetRegistry,
} from "@/fixtures/engine";
import { replaceFunctionMock, replaceFunctionMockOnce, resetFunctionMock } from "@/fixtures/jest";
Expand Down Expand Up @@ -808,11 +809,100 @@ describe("object conditions implementation", () => {
expect(callXrCondition("mob_was_hit", MockGameObject.mockActor(), object)).toBe(true);
});

it.todo("squad_in_zone should check if squad is in zone");
it("squad_in_zone should check if squad is in zone", () => {
const squad: MockSquad = MockSquad.createRegistered();
const zone: GameObject = MockGameObject.mock();

const first: GameObject = MockGameObject.mock();
const firstServer: ServerHumanObject = MockAlifeHumanStalker.mock({ id: first.id() });
const second: GameObject = MockGameObject.mock();
const secondServer: ServerHumanObject = MockAlifeHumanStalker.mock({ id: second.id() });

registerStoryLink(squad.id, "test-sid");
registerZone(zone);

expect(() => callXrCondition("squad_in_zone", MockGameObject.mockActor(), zone)).toThrow(
"Incorrect 'squad_in_zone' condition parameters: storyId 'nil', zoneName 'nil'."
);
expect(callXrCondition("squad_in_zone", MockGameObject.mockActor(), zone, "not-existing")).toBe(false);
expect(callXrCondition("squad_in_zone", MockGameObject.mockActor(), zone, "test-sid")).toBe(false);

jest.spyOn(zone, "inside").mockImplementation((position) => position === secondServer.position);

squad.mockAddMember(firstServer);
expect(callXrCondition("squad_in_zone", MockGameObject.mockActor(), zone, "test-sid")).toBe(false);

squad.mockAddMember(secondServer);
expect(callXrCondition("squad_in_zone", MockGameObject.mockActor(), zone, "test-sid")).toBe(true);

registerObject(first);
registerObject(second);

jest.spyOn(zone, "inside").mockImplementation((position) => position === second.position());

expect(callXrCondition("squad_in_zone", MockGameObject.mockActor(), zone, "test-sid", zone.name())).toBe(true);
});

it("squad_has_enemy should check if squad has enemy", () => {
const squad: MockSquad = MockSquad.createRegistered();
const zone: GameObject = MockGameObject.mock();

const first: GameObject = MockGameObject.mock();
const firstServer: ServerHumanObject = MockAlifeHumanStalker.mock({ id: first.id() });
const second: GameObject = MockGameObject.mock();
const secondServer: ServerHumanObject = MockAlifeHumanStalker.mock({ id: second.id() });

registerStoryLink(squad.id, "test-sid");
registerZone(zone);

expect(() => callXrCondition("squad_in_zone", MockGameObject.mockActor(), zone)).toThrow(
"Incorrect 'squad_in_zone' condition parameters: storyId 'nil', zoneName 'nil'."
);
expect(callXrCondition("squad_has_enemy", MockGameObject.mockActor(), zone, "not-existing")).toBe(false);
expect(callXrCondition("squad_has_enemy", MockGameObject.mockActor(), zone, "test-sid")).toBe(false);

jest.spyOn(second, "best_enemy").mockImplementation(() => MockGameObject.mock());

it.todo("squad_has_enemy should check if squad has enemy");
squad.mockAddMember(firstServer);
expect(callXrCondition("squad_has_enemy", MockGameObject.mockActor(), zone, "test-sid")).toBe(false);

it.todo("squad_in_zone_all should check if squad members are in zone");
squad.mockAddMember(secondServer);
expect(callXrCondition("squad_has_enemy", MockGameObject.mockActor(), zone, "test-sid")).toBe(true);
});

it("squad_in_zone_all should check if squad members are in zone", () => {
const squad: MockSquad = MockSquad.createRegistered();
const zone: GameObject = MockGameObject.mock();

const first: GameObject = MockGameObject.mock();
const firstServer: ServerHumanObject = MockAlifeHumanStalker.mock({ id: first.id() });
const second: GameObject = MockGameObject.mock();
const secondServer: ServerHumanObject = MockAlifeHumanStalker.mock({ id: second.id() });

registerStoryLink(squad.id, "test-sid");
registerZone(zone);

expect(() => callXrCondition("squad_in_zone_all", MockGameObject.mockActor(), zone)).toThrow(
"Incorrect params in 'squad_in_zone_all' condition: storyId 'nil', zoneName 'nil'"
);
expect(callXrCondition("squad_in_zone_all", MockGameObject.mockActor(), zone, "not-existing", "test")).toBe(false);
expect(callXrCondition("squad_in_zone_all", MockGameObject.mockActor(), zone, "test-sid", zone.name())).toBe(true);

jest.spyOn(zone, "inside").mockImplementation((position) => position === firstServer.position);

squad.mockAddMember(firstServer);
expect(callXrCondition("squad_in_zone_all", MockGameObject.mockActor(), zone, "test-sid", zone.name())).toBe(true);

squad.mockAddMember(secondServer);
expect(callXrCondition("squad_in_zone_all", MockGameObject.mockActor(), zone, "test-sid", zone.name())).toBe(false);

registerObject(first);
registerObject(second);

jest.spyOn(zone, "inside").mockImplementation(() => true);

expect(callXrCondition("squad_in_zone_all", MockGameObject.mockActor(), zone, "test-sid", zone.name())).toBe(true);
});

it.todo("squads_in_zone_b41 should check if squad members are in zone b41");

Expand Down
148 changes: 67 additions & 81 deletions src/engine/scripts/declarations/conditions/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ import {
TRate,
TSection,
TStringId,
Vector,
} from "@/engine/lib/types";

/**
Expand Down Expand Up @@ -616,114 +615,101 @@ extern("xr_conditions.mob_was_hit", (_: GameObject, object: GameObject): boolean
});

/**
* todo;
* Check if at least one squad member is in target zone.
*
* Where:
* - storyId - story ID of the squad
* - zoneName - name of the zone to check (current object will be checked by default)
*/
extern("xr_conditions.squad_in_zone", (_: GameObject, object: GameObject, p: [string, string]) => {
const storyId: TStringId = p[0];
let zoneName: TName = p[1];

if (storyId === null) {
abort(
"Insufficient params in squad_in_zone function. story_id[%s], zone_name[%s]",
tostring(storyId),
tostring(zoneName)
);
}

if (zoneName === null) {
zoneName = object.name();
}
extern(
"xr_conditions.squad_in_zone",
(_: GameObject, object: GameObject, [storyId, zoneName]: [Optional<TStringId>, Optional<TName>]): boolean => {
const squad: Optional<Squad> = storyId
? getServerObjectByStoryId(storyId)
: abort("Incorrect 'squad_in_zone' condition parameters: storyId '%s', zoneName '%s'.", storyId, zoneName);

const squad: Optional<Squad> = getServerObjectByStoryId(storyId);
if (!squad) {
return false;
}

if (squad === null) {
return false;
}
const zone: Optional<GameObject> = registry.zones.get(zoneName ?? object.name()) as Optional<GameObject>;

const zone: GameObject = registry.zones.get(zoneName);
if (zone) {
for (const squadMember of squad.squad_members()) {
if (zone.inside(registry.objects.get(squadMember.id)?.object?.position() ?? squadMember.object.position)) {
return true;
}
}
}

if (zone === null) {
return false;
}

for (const squadMember of squad.squad_members()) {
const position: Vector = registry.objects.get(squadMember.id)?.object?.position() || squadMember.object.position;

if (zone.inside(position)) {
return true;
}
}

return false;
});
);

/**
* todo;
* Check whether all squad members are in zone.
*
* Params:
* - storyId - story ID of the squad to check
* - zoneName - target zone name
*
* Notes:
* - Returns true is squad is empty
*/
extern("xr_conditions.squad_has_enemy", (_: GameObject, __: GameObject, p: [Optional<TStringId>]): boolean => {
const storyId: Optional<TStringId> = p[0];

if (storyId === null) {
abort("Insufficient params in squad_has_enemy function. story_id [%s]", tostring(storyId));
}

const squad: Optional<Squad> = getServerObjectByStoryId(storyId);

if (squad === null) {
return false;
}
extern(
"xr_conditions.squad_in_zone_all",
(_: GameObject, __: GameObject, [storyId, zoneName]: [TStringId, TName]): boolean => {
if (!storyId || !zoneName) {
abort("Incorrect params in 'squad_in_zone_all' condition: storyId '%s', zoneName '%s'", storyId, zoneName);
}

for (const squadMember of squad.squad_members()) {
const npcObject: Optional<GameObject> = level.object_by_id(squadMember.object.id);
const squad: Optional<Squad> = getServerObjectByStoryId(storyId);

if (npcObject === null) {
if (!squad) {
return false;
}

if (npcObject.best_enemy() !== null) {
return true;
const zone: Optional<GameObject> = registry.zones.get(zoneName);

if (zone) {
for (const squadMember of squad.squad_members()) {
if (!zone.inside(registry.objects.get(squadMember.id)?.object?.position() ?? squadMember.object.position)) {
return false;
}
}
}
}

return false;
});
return true;
}
);

/**
* todo;
* Check whether any squad member has any enemy.
*
* Params:
* - storyId - story ID of the squad to check
*/
extern("xr_conditions.squad_in_zone_all", (_: GameObject, __: GameObject, p: [TStringId, TName]): boolean => {
const storyId: TStringId = p[0];
const zoneName: TName = p[1];

if (storyId === null || zoneName === null) {
abort(
"Insufficient params in squad_in_zone_all function. story_id[%s], zone_name[%s]",
tostring(storyId),
tostring(zoneName)
);
extern("xr_conditions.squad_has_enemy", (_: GameObject, __: GameObject, [storyId]: [Optional<TStringId>]): boolean => {
if (!storyId) {
abort("Incorrect params in 'squad_has_enemy' condition: storyId '%s'.", storyId);
}

const squad: Optional<Squad> = getServerObjectByStoryId(storyId);

if (squad === null) {
return false;
}

const zone: Optional<GameObject> = registry.zones.get(zoneName);

if (zone === null) {
return false;
}

for (const squadMember of squad.squad_members()) {
const position: Vector = registry.objects.get(squadMember.id)?.object?.position() || squadMember.object.position;
if (squad) {
for (const squadMember of squad.squad_members()) {
// todo: Check from registry?
const squadObject: Optional<GameObject> = level.object_by_id(squadMember.object.id);

if (!zone.inside(position)) {
return false;
if (!squadObject) {
return false;
} else if (squadObject.best_enemy()) {
return true;
}
}
}

return true;
return false;
});

/**
Expand Down
16 changes: 10 additions & 6 deletions src/fixtures/engine/mocks/squad.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,23 @@ interface IMockSquadConfig extends IMockAlifeObjectConfig {
* Class based mock of squad group.
*/
export class MockSquad extends Squad {
public static mock(config: IMockSquadConfig = {}): MockSquad {
return new MockSquad(config);
}

public static mockRegistered(config: IMockSquadConfig = {}): Squad {
const squad: Squad = MockSquad.mock(config);
public static createRegistered(config: IMockSquadConfig = {}): MockSquad {
const squad: MockSquad = MockSquad.mock(config);

squad.on_before_register();
squad.on_register();

return squad;
}

public static mock(config: IMockSquadConfig = {}): MockSquad {
return new MockSquad(config);
}

public static mockRegistered(config: IMockSquadConfig = {}): Squad {
return this.createRegistered(config);
}

public constructor(config: IMockSquadConfig) {
const section: TSection = config.section ?? "test_squad";

Expand Down

0 comments on commit c94122c

Please sign in to comment.