Skip to content

Commit

Permalink
Actor relation to faction utils moved. Added tests for actor relation…
Browse files Browse the repository at this point in the history
… utils. Added mock actor in registry/client/server utility. Mocks extended.
  • Loading branch information
Neloreck committed Jun 25, 2023
1 parent abd6e94 commit e4003bb
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 49 deletions.
40 changes: 11 additions & 29 deletions src/engine/core/utils/check/check.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { alife, danger_object, device, game_graph, relation_registry } from "xray16";
import { alife, danger_object, device, game_graph } from "xray16";

import { getObjectIdByStoryId, getServerObjectByStoryId, IRegistryObjectState, registry } from "@/engine/core/database";
import { SimulationBoardManager } from "@/engine/core/managers/interaction/SimulationBoardManager";
Expand All @@ -11,10 +11,9 @@ import { ISchemeWoundedState } from "@/engine/core/schemes/wounded";
import { isStalker } from "@/engine/core/utils/check/is";
import { pickSectionFromCondList } from "@/engine/core/utils/ini/config";
import { getCharacterCommunity } from "@/engine/core/utils/object/object_general";
import { EGoodwill } from "@/engine/core/utils/relation/types";
import { logicsConfig } from "@/engine/lib/configs/LogicsConfig";
import { surgeConfig } from "@/engine/lib/configs/SurgeConfig";
import { communities, TCommunity } from "@/engine/lib/constants/communities";
import { communities } from "@/engine/lib/constants/communities";
import { ACTOR_ID } from "@/engine/lib/constants/ids";
import { lootableTableExclude, TLootableExcludeItem } from "@/engine/lib/constants/items/lootable_table";
import { TLevel } from "@/engine/lib/constants/levels";
Expand All @@ -40,14 +39,20 @@ import {
} from "@/engine/lib/types";

/**
* todo;
* Check whether story object exists.
*
* @param storyId - story ID to check existing
* @returns whether story object exists
*/
export function isSquadExisting(squadStoryId: TStringId): boolean {
return getServerObjectByStoryId(squadStoryId) !== null;
export function isStoryObjectExisting(storyId: TStringId): boolean {
return getServerObjectByStoryId(storyId) !== null;
}

/**
* Is provided target stalker and alive.
*
* @param targetObject - client/server object or story ID to check
* @returns whether target stalker object is alive
*/
export function isStalkerAlive(targetObject: ClientObject | ServerHumanObject | TStringId): boolean {
let targetId: Optional<TNumberId> = null;
Expand All @@ -69,29 +74,6 @@ export function isStalkerAlive(targetObject: ClientObject | ServerHumanObject |
}
}

/**
* todo;
*/
export function isActorEnemyWithFaction(faction: TCommunity, actor: ClientObject = registry.actor): boolean {
return relation_registry.community_goodwill(faction, actor.id()) <= EGoodwill.ENEMIES;
}

/**
* todo;
*/
export function isActorFriendWithFaction(faction: TCommunity, actor: ClientObject = registry.actor): boolean {
return relation_registry.community_goodwill(faction, actor.id()) >= EGoodwill.FRIENDS;
}

/**
* todo;
*/
export function isActorNeutralWithFaction(faction: TCommunity, actor: ClientObject = registry.actor): boolean {
const goodwill: number = relation_registry.community_goodwill(faction, actor.id());

return goodwill > EGoodwill.ENEMIES && goodwill < EGoodwill.FRIENDS;
}

/**
* @returns whether provided object is on a provided level.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/engine/core/utils/object/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "@/engine/core/utils/object/object_general";
export * from "@/engine/core/utils/object/object_find";
38 changes: 36 additions & 2 deletions src/engine/core/utils/relation/check.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { beforeEach, describe, expect, it } from "@jest/globals";

import { registerStoryLink, registry } from "@/engine/core/database";
import { registerActor, registerStoryLink, registry } from "@/engine/core/database";
import { Squad } from "@/engine/core/objects";
import {
areCommunitiesEnemies,
areCommunitiesFriendly,
isActorEnemyWithFaction,
isActorFriendWithFaction,
isActorNeutralWithFaction,
isAnySquadMemberEnemyToActor,
isAnySquadMemberFriendToActor,
} from "@/engine/core/utils/relation/check";
Expand All @@ -13,7 +16,7 @@ import { ERelation } from "@/engine/core/utils/relation/types";
import { communities } from "@/engine/lib/constants/communities";
import { ACTOR_ID } from "@/engine/lib/constants/ids";
import { ServerGroupObject } from "@/engine/lib/types";
import { mockRelationsSquads } from "@/fixtures/engine";
import { mockRegisteredActor, mockRelationsSquads } from "@/fixtures/engine";
import { MockAlifeSimulator, mockServerAlifeCreatureActor, mockServerAlifeOnlineOfflineGroup } from "@/fixtures/xray";

describe("'relation/check' utils", () => {
Expand All @@ -24,6 +27,37 @@ describe("'relation/check' utils", () => {
MockAlifeSimulator.removeFromRegistry(ACTOR_ID);
});

it("'isActorEnemyWithFaction' should check object faction relation", () => {
mockRegisteredActor();

expect(isActorEnemyWithFaction(communities.army)).toBe(false);
expect(isActorEnemyWithFaction(communities.stalker)).toBe(false);
expect(isActorEnemyWithFaction(communities.bandit)).toBe(false);
expect(isActorEnemyWithFaction(communities.monolith)).toBe(true);
expect(isActorEnemyWithFaction(communities.monster)).toBe(true);
});

it("'isActorFriendWithFaction' should check object faction relation", () => {
mockRegisteredActor();

expect(isActorFriendWithFaction(communities.actor)).toBe(true);
expect(isActorFriendWithFaction(communities.army)).toBe(true);
expect(isActorFriendWithFaction(communities.stalker)).toBe(false);
expect(isActorFriendWithFaction(communities.bandit)).toBe(false);
expect(isActorFriendWithFaction(communities.monolith)).toBe(false);
expect(isActorFriendWithFaction(communities.monster)).toBe(false);
});

it("'isActorNeutralWithFaction' should check object faction relation", () => {
mockRegisteredActor();

expect(isActorNeutralWithFaction(communities.army)).toBe(false);
expect(isActorNeutralWithFaction(communities.stalker)).toBe(true);
expect(isActorNeutralWithFaction(communities.bandit)).toBe(true);
expect(isActorNeutralWithFaction(communities.monolith)).toBe(false);
expect(isActorNeutralWithFaction(communities.monster)).toBe(false);
});

it("'isSquadCommunityEnemyToActor' should correctly check relation", () => {
expect(() => getSquadCommunityRelationToActor("not-existing")).toThrow(
"Squad with story id 'not-existing' was not found."
Expand Down
38 changes: 37 additions & 1 deletion src/engine/core/utils/relation/check.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,47 @@
import { relation_registry } from "xray16";

import { registry } from "@/engine/core/database/registry";
import { Squad } from "@/engine/core/objects";
import { getSquadCommunityRelationToActor } from "@/engine/core/utils/relation/get";
import { EGoodwill, ERelation } from "@/engine/core/utils/relation/types";
import { communities, TCommunity } from "@/engine/lib/constants/communities";
import { ACTOR_ID } from "@/engine/lib/constants/ids";
import { Optional, TStringId } from "@/engine/lib/types";
import { ClientObject, Optional, TCount, TStringId } from "@/engine/lib/types";

/**
* Check whether is enemy with faction.
*
* @param faction - target faction to check
* @param actor - optional actor object override
* @returns whether actor is enemy with faction
*/
export function isActorEnemyWithFaction(faction: TCommunity, actor: ClientObject = registry.actor): boolean {
return relation_registry.community_goodwill(faction, actor.id()) <= EGoodwill.ENEMIES;
}

/**
* Check whether is friend with faction.
*
* @param faction - target faction to check
* @param actor - optional actor object override
* @returns whether actor is friend with faction
*/
export function isActorFriendWithFaction(faction: TCommunity, actor: ClientObject = registry.actor): boolean {
return relation_registry.community_goodwill(faction, actor.id()) >= EGoodwill.FRIENDS;
}

/**
* Check whether is neutral with faction.
*
* @param faction - target faction to check
* @param actor - optional actor object override
* @returns whether actor is neutral with faction
*/
export function isActorNeutralWithFaction(faction: TCommunity, actor: ClientObject = registry.actor): boolean {
const goodwill: TCount = relation_registry.community_goodwill(faction, actor.id());

return goodwill > EGoodwill.ENEMIES && goodwill < EGoodwill.FRIENDS;
}

/**
* Check whether squad is enemy to actor.
Expand Down
4 changes: 2 additions & 2 deletions src/engine/scripts/declarations/conditions/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
isObjectInZone,
isObjectWounded,
isPlayingSound,
isSquadExisting,
isStoryObjectExisting,
} from "@/engine/core/utils/check/check";
import { isMonster, isStalker } from "@/engine/core/utils/check/is";
import { hasAlifeInfo } from "@/engine/core/utils/info_portion";
Expand Down Expand Up @@ -804,7 +804,7 @@ extern("xr_conditions.squad_exist", (actor: ClientObject, npc: ClientObject, p:
if (storyId === null) {
abort("Wrong parameter story_id[%s] in squad_exist function", tostring(storyId));
} else {
return isSquadExisting(storyId);
return isStoryObjectExisting(storyId);
}
});

Expand Down
8 changes: 3 additions & 5 deletions src/engine/scripts/declarations/conditions/relation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import { Squad } from "@/engine/core/objects";
import { ISchemeDeathState } from "@/engine/core/schemes/death";
import { abort, assert } from "@/engine/core/utils/assertion";
import { extern, getExtern } from "@/engine/core/utils/binding";
import {
isActorEnemyWithFaction,
isActorFriendWithFaction,
isActorNeutralWithFaction,
} from "@/engine/core/utils/check/check";
import { LuaLogger } from "@/engine/core/utils/logging";
import { getCharacterCommunity } from "@/engine/core/utils/object/object_general";
import {
areCommunitiesEnemies,
areCommunitiesFriendly,
isActorEnemyWithFaction,
isActorFriendWithFaction,
isActorNeutralWithFaction,
isAnySquadMemberEnemyToActor,
isAnySquadMemberFriendToActor,
} from "@/engine/core/utils/relation";
Expand Down
3 changes: 1 addition & 2 deletions src/engine/scripts/declarations/dialogs/dialogs_jupiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import { getPortableStoreValue } from "@/engine/core/database/portable_store";
import { TreasureManager } from "@/engine/core/managers/world/TreasureManager";
import { AnomalyZoneBinder } from "@/engine/core/objects/binders/zones/AnomalyZoneBinder";
import { extern, getExtern } from "@/engine/core/utils/binding";
import { isActorEnemyWithFaction } from "@/engine/core/utils/check/check";
import { disableInfo, giveInfo, hasAlifeInfo } from "@/engine/core/utils/info_portion";
import { LuaLogger } from "@/engine/core/utils/logging";
import { getObjectsRelationSafe } from "@/engine/core/utils/relation/get";
import { getObjectsRelationSafe, isActorEnemyWithFaction } from "@/engine/core/utils/relation";
import {
getNpcSpeaker,
giveItemsToActor,
Expand Down
6 changes: 3 additions & 3 deletions src/engine/scripts/declarations/dialogs/dialogs_zaton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getPortableStoreValue, setPortableStoreValue } from "@/engine/core/data
import { ENotificationDirection, NotificationManager } from "@/engine/core/managers/interface/notifications";
import { TreasureManager } from "@/engine/core/managers/world/TreasureManager";
import { extern, getExtern } from "@/engine/core/utils/binding";
import { isSquadExisting } from "@/engine/core/utils/check/check";
import { isStoryObjectExisting } from "@/engine/core/utils/check/check";
import { disableInfo, giveInfo, hasAlifeInfo } from "@/engine/core/utils/info_portion";
import { LuaLogger } from "@/engine/core/utils/logging";
import {
Expand Down Expand Up @@ -545,15 +545,15 @@ extern(
return false;
}

return !isSquadExisting("zat_b7_stalkers_victims_1");
return !isStoryObjectExisting("zat_b7_stalkers_victims_1");
}
);

/**
* todo;
*/
extern("dialogs_zaton.zat_b7_squad_alive", (firstSpeaker: ClientObject, secondSpeaker: ClientObject): boolean => {
return isSquadExisting("zat_b7_stalkers_victims_1");
return isStoryObjectExisting("zat_b7_stalkers_victims_1");
});

/**
Expand Down
3 changes: 2 additions & 1 deletion src/fixtures/engine/mocks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "@/fixtures/engine/mocks/registry.mocks";
export * from "@/fixtures/engine/mocks/scheme.mocks";
export * from "@/fixtures/engine/mocks/squads.mocks";
export * from "@/fixtures/engine/mocks/scheme.mocks";
export * from "@/fixtures/engine/mocks/squads.mocks";
23 changes: 23 additions & 0 deletions src/fixtures/engine/mocks/registry.mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { IRegistryObjectState, registerActor } from "@/engine/core/database";
import { ClientObject, ServerActorObject } from "@/engine/lib/types";
import { mockActorClientGameObject, mockServerAlifeCreatureActor } from "@/fixtures/xray/mocks/objects";

export interface IMockActorDetails {
actorClientObject: ClientObject;
actorServerObject: ServerActorObject;
actorState: IRegistryObjectState;
}

/**
* Mock actor client/server side.
*/
export function mockRegisteredActor(
actorClientPartial: Partial<ClientObject> = {},
actorServerPartial: Partial<ServerActorObject> = {}
): IMockActorDetails {
const actorClientObject: ClientObject = mockActorClientGameObject(actorClientPartial);
const actorServerObject: ServerActorObject = mockServerAlifeCreatureActor(actorServerPartial);
const actorState: IRegistryObjectState = registerActor(actorClientObject);

return { actorClientObject, actorServerObject, actorState };
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { jest } from "@jest/globals";
import { IXR_relation_registry } from "xray16";

import { TName, TNumberId } from "@/engine/lib/types";
import { Optional, ServerHumanObject, TName, TNumberId } from "@/engine/lib/types";
import { MockAlifeSimulator } from "@/fixtures/xray/mocks/objects/AlifeSimulator.mock";
import { charactersGoodwill, communityGoodwill } from "@/fixtures/xray/mocks/relations";

/**
* todo;
*/
export const mockRelationRegistryInterface: IXR_relation_registry = {
change_community_goodwill: jest.fn((community_a: string, value2: number, value3: number): void => {}),
community_goodwill: jest.fn((community: string, object_id: number): number => {
return -1;
community_goodwill: jest.fn((community: string, objectId: TNumberId): number => {
const object: Optional<ServerHumanObject> = MockAlifeSimulator.getFromRegistry(objectId);

if (!object) {
throw new Error(`Object is not registered: '${objectId}'.`);
}

return mockRelationRegistryInterface.community_relation(community, object.community());
}),
community_relation: jest.fn((from: TName, to: TName): number => {
const descriptor = communityGoodwill[from];
Expand Down
4 changes: 4 additions & 0 deletions src/fixtures/xray/mocks/objects/AlifeSimulator.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export class MockAlifeSimulator {
delete MockAlifeSimulator.registry[id];
}

public static getFromRegistry<T extends ServerObject = ServerObject>(id: number): Optional<T> {
return (MockAlifeSimulator.registry[id] as T) || null;
}

public static getInstance(): MockAlifeSimulator {
if (!MockAlifeSimulator.simulator) {
MockAlifeSimulator.simulator = new MockAlifeSimulator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "@/fixtures/xray/mocks/objects/server/cse_alife_dynamic_object_visual.mock";

/**
* todo;
* Class based server object mock.
*/
export class MockAlifeCreatureActor extends MockAlifeDynamicObjectVisual {
public override id: TNumberId = 0;
Expand Down

0 comments on commit e4003bb

Please sign in to comment.