Skip to content

Commit

Permalink
[SYNC] Moved AI logics to separate folders. (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
Neloreck authored Aug 25, 2023
2 parents eb61dd3 + 3ea540c commit 1d58f77
Show file tree
Hide file tree
Showing 217 changed files with 2,550 additions and 2,046 deletions.
2 changes: 1 addition & 1 deletion src/engine/core/database/stalker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
unregisterStalker,
} from "@/engine/core/database/stalker";
import { StalkerBinder } from "@/engine/core/objects";
import { StalkerStateManager } from "@/engine/core/objects/ai/state/StalkerStateManager";
import { EStalkerState } from "@/engine/core/objects/animation";
import { StalkerStateManager } from "@/engine/core/objects/state/StalkerStateManager";
import { createEmptyVector } from "@/engine/core/utils/vector";
import { mockClientGameObject } from "@/fixtures/xray";

Expand Down
2 changes: 1 addition & 1 deletion src/engine/core/database/stalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { registerObject, unregisterObject } from "@/engine/core/database/objects
import { registry } from "@/engine/core/database/registry";
import { IRegistryObjectState } from "@/engine/core/database/types";
import { StalkerBinder } from "@/engine/core/objects";
import { StalkerStateManager } from "@/engine/core/objects/ai/state/StalkerStateManager";
import {
EStalkerState,
ILookTargetDescriptor,
IStateManagerCallbackDescriptor,
ITargetStateDescriptorExtras,
} from "@/engine/core/objects/animation/state_types";
import { StalkerStateManager } from "@/engine/core/objects/state/StalkerStateManager";
import { ClientObject, Optional, TDuration } from "@/engine/lib/types";

/**
Expand Down
4 changes: 2 additions & 2 deletions src/engine/core/database/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StalkerMoveManager } from "@/engine/core/objects/state/StalkerMoveManager";
import { StalkerStateManager } from "@/engine/core/objects/state/StalkerStateManager";
import { StalkerMoveManager } from "@/engine/core/objects/ai/state/StalkerMoveManager";
import { StalkerStateManager } from "@/engine/core/objects/ai/state/StalkerStateManager";
import { IBaseSchemeState, ObjectRestrictionsManager } from "@/engine/core/schemes";
import { ISchemePostCombatIdleState } from "@/engine/core/schemes/combat_idle/ISchemePostCombatIdleState";
import { IActionSchemeHearState } from "@/engine/core/schemes/hear";
Expand Down
5 changes: 2 additions & 3 deletions src/engine/core/managers/debug/DebugManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { alife, cast_planner, relation_registry, stalker_ids } from "xray16";

import { IRegistryObjectState, registry } from "@/engine/core/database";
import { AbstractCoreManager } from "@/engine/core/managers/base/AbstractCoreManager";
import { EStateActionId } from "@/engine/core/objects/animation";
import { StalkerStateManager } from "@/engine/core/objects/state/StalkerStateManager";
import { EActionId } from "@/engine/core/schemes";
import { StalkerStateManager } from "@/engine/core/objects/ai/state/StalkerStateManager";
import { EActionId, EStateActionId } from "@/engine/core/objects/ai/types";
import { gameTimeToString } from "@/engine/core/utils/game/game_time";
import { LuaLogger } from "@/engine/core/utils/logging";
import { getObjectActiveWeaponSlot } from "@/engine/core/utils/object";
Expand Down
3 changes: 3 additions & 0 deletions src/engine/core/objects/ai/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "@/engine/core/objects/ai/setup";
export * from "@/engine/core/objects/ai/types";
export * from "@/engine/core/objects/ai/state";
2 changes: 2 additions & 0 deletions src/engine/core/objects/ai/setup/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "@/engine/core/objects/ai/setup/motivation_planner";
export * from "@/engine/core/objects/ai/setup/state_planner";
99 changes: 99 additions & 0 deletions src/engine/core/objects/ai/setup/motivation_planner.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { describe, expect, it } from "@jest/globals";

import { setupStalkerMotivationPlanner } from "@/engine/core/objects/ai/setup/motivation_planner";
import { StalkerAnimationManager } from "@/engine/core/objects/ai/state/StalkerAnimationManager";
import { StalkerStateManager } from "@/engine/core/objects/ai/state/StalkerStateManager";
import { EvaluatorStateIdleAlife } from "@/engine/core/objects/ai/state/state/EvaluatorStateIdleAlife";
import { EvaluatorStateIdleCombat } from "@/engine/core/objects/ai/state/state/EvaluatorStateIdleCombat";
import { EvaluatorStateIdleItems } from "@/engine/core/objects/ai/state/state/EvaluatorStateIdleItems";
import { EvaluatorStateLogicActive } from "@/engine/core/objects/ai/state/state/EvaluatorStateLogicActive";
import { EActionId, EEvaluatorId } from "@/engine/core/objects/ai/types";
import { EAnimationType } from "@/engine/core/objects/animation/animation_types";
import { ActionPlanner, ClientObject } from "@/engine/lib/types";
import { checkPlannerAction } from "@/fixtures/engine";
import { MockActionPlanner, mockClientGameObject } from "@/fixtures/xray";
import { mockStalkerIds } from "@/fixtures/xray/mocks/constants";

describe("motivation_planner setup util", () => {
it("should correctly setup object motivation planner evaluators", () => {
const object: ClientObject = mockClientGameObject();
const planner: ActionPlanner = object.motivation_action_manager();
const stateManager: StalkerStateManager = new StalkerStateManager(object);

setupStalkerMotivationPlanner(planner, stateManager);

expect(Object.keys((planner as unknown as MockActionPlanner).evaluators)).toHaveLength(4);

expect(stateManager.animstate instanceof StalkerAnimationManager).toBeTruthy();
expect(stateManager.animation instanceof StalkerAnimationManager).toBeTruthy();
expect(stateManager.animstate.type).toBe(EAnimationType.ANIMSTATE);
expect(stateManager.animation.type).toBe(EAnimationType.ANIMATION);

expect(planner.evaluator(EEvaluatorId.IS_STATE_IDLE_COMBAT) instanceof EvaluatorStateIdleCombat).toBeTruthy();
expect(planner.evaluator(EEvaluatorId.IS_STATE_IDLE_ALIFE) instanceof EvaluatorStateIdleAlife).toBeTruthy();
expect(planner.evaluator(EEvaluatorId.IS_STATE_IDLE_ITEMS) instanceof EvaluatorStateIdleItems).toBeTruthy();
expect(planner.evaluator(EEvaluatorId.IS_STATE_LOGIC_ACTIVE) instanceof EvaluatorStateLogicActive).toBeTruthy();
});

it("should correctly setup motivation planner actions", () => {
const object: ClientObject = mockClientGameObject();
const planner: ActionPlanner = object.motivation_action_manager();
const stateManager: StalkerStateManager = new StalkerStateManager(object);

setupStalkerMotivationPlanner(planner, stateManager);

checkPlannerAction(
planner.action(EActionId.STATE_TO_IDLE_COMBAT),
"ActionToIdleCombat",
[[EEvaluatorId.IS_STATE_IDLE_COMBAT, false]],
[[EEvaluatorId.IS_STATE_IDLE_COMBAT, true]]
);

checkPlannerAction(
planner.action(EActionId.STATE_TO_IDLE_ITEMS),
"ActionToIdleItems",
[
[EEvaluatorId.IS_STATE_IDLE_ITEMS, false],
[mockStalkerIds.property_items, true],
[mockStalkerIds.property_enemy, false],
],
[[EEvaluatorId.IS_STATE_IDLE_ITEMS, true]]
);

checkPlannerAction(
planner.action(EActionId.STATE_TO_IDLE_ALIFE),
"ActionToIdleAlife",
[
[mockStalkerIds.property_alive, true],
[mockStalkerIds.property_enemy, false],
[mockStalkerIds.property_danger, false],
[mockStalkerIds.property_items, false],
[EEvaluatorId.IS_STATE_LOGIC_ACTIVE, false],
[EEvaluatorId.IS_STATE_IDLE_ALIFE, false],
],
[[EEvaluatorId.IS_STATE_IDLE_ALIFE, true]]
);
});

it("should correctly setup update planner default actions", () => {
const object: ClientObject = mockClientGameObject();
const planner: ActionPlanner = object.motivation_action_manager();
const stateManager: StalkerStateManager = new StalkerStateManager(object);

setupStalkerMotivationPlanner(planner, stateManager);

checkPlannerAction(planner.action(EActionId.ALIFE), "generic", [[EEvaluatorId.IS_STATE_IDLE_ALIFE, true]], []);

checkPlannerAction(
planner.action(EActionId.GATHER_ITEMS),
"generic",
[[EEvaluatorId.IS_STATE_IDLE_ITEMS, true]],
[]
);
checkPlannerAction(planner.action(EActionId.COMBAT), "generic", [[EEvaluatorId.IS_STATE_IDLE_COMBAT, true]], []);

checkPlannerAction(planner.action(EActionId.ANOMALY), "generic", [[EEvaluatorId.IS_STATE_IDLE_COMBAT, true]], []);

checkPlannerAction(planner.action(EActionId.DANGER), "generic", [[EEvaluatorId.IS_STATE_IDLE_COMBAT, true]], []);
});
});
62 changes: 62 additions & 0 deletions src/engine/core/objects/ai/setup/motivation_planner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { stalker_ids, world_property } from "xray16";

import { StalkerStateManager } from "@/engine/core/objects/ai/state";
import { ActionStateToIdle } from "@/engine/core/objects/ai/state/state/ActionStateToIdle";
import { EvaluatorStateIdleAlife } from "@/engine/core/objects/ai/state/state/EvaluatorStateIdleAlife";
import { EvaluatorStateIdleCombat } from "@/engine/core/objects/ai/state/state/EvaluatorStateIdleCombat";
import { EvaluatorStateIdleItems } from "@/engine/core/objects/ai/state/state/EvaluatorStateIdleItems";
import { EvaluatorStateLogicActive } from "@/engine/core/objects/ai/state/state/EvaluatorStateLogicActive";
import { EActionId, EEvaluatorId } from "@/engine/core/objects/ai/types";
import { LuaLogger } from "@/engine/core/utils/logging";
import { ActionPlanner } from "@/engine/lib/types";

const logger: LuaLogger = new LuaLogger($filename);

/**
* Setup GOAP planner of stalker motivation.
* Merges existing C++ logic with custom scripted logics and scripted state manager.
*
* @param planner - motivation action planner to modify logics
* @param stateManager - state manager controlling animation/state of objects from lua side
*/
export function setupStalkerMotivationPlanner(planner: ActionPlanner, stateManager: StalkerStateManager): void {
planner.add_evaluator(EEvaluatorId.IS_STATE_IDLE_COMBAT, new EvaluatorStateIdleCombat(stateManager));
planner.add_evaluator(EEvaluatorId.IS_STATE_IDLE_ALIFE, new EvaluatorStateIdleAlife(stateManager));
planner.add_evaluator(EEvaluatorId.IS_STATE_IDLE_ITEMS, new EvaluatorStateIdleItems(stateManager));
planner.add_evaluator(EEvaluatorId.IS_STATE_LOGIC_ACTIVE, new EvaluatorStateLogicActive(stateManager));

const actionCombatStateToIdle: ActionStateToIdle = new ActionStateToIdle(stateManager, "ActionToIdleCombat");

actionCombatStateToIdle.add_precondition(new world_property(EEvaluatorId.IS_STATE_IDLE_COMBAT, false));
actionCombatStateToIdle.add_effect(new world_property(EEvaluatorId.IS_STATE_IDLE_COMBAT, true));

planner.add_action(EActionId.STATE_TO_IDLE_COMBAT, actionCombatStateToIdle);

const actionItemsToIdle: ActionStateToIdle = new ActionStateToIdle(stateManager, "ActionToIdleItems");

actionItemsToIdle.add_precondition(new world_property(EEvaluatorId.IS_STATE_IDLE_ITEMS, false));
actionItemsToIdle.add_precondition(new world_property(stalker_ids.property_items, true));
actionItemsToIdle.add_precondition(new world_property(stalker_ids.property_enemy, false));
actionItemsToIdle.add_effect(new world_property(EEvaluatorId.IS_STATE_IDLE_ITEMS, true));

planner.add_action(EActionId.STATE_TO_IDLE_ITEMS, actionItemsToIdle);

const actionAlifeToIdle: ActionStateToIdle = new ActionStateToIdle(stateManager, "ActionToIdleAlife");

actionAlifeToIdle.add_precondition(new world_property(stalker_ids.property_alive, true));
actionAlifeToIdle.add_precondition(new world_property(stalker_ids.property_enemy, false));
actionAlifeToIdle.add_precondition(new world_property(stalker_ids.property_danger, false));
actionAlifeToIdle.add_precondition(new world_property(stalker_ids.property_items, false));
actionAlifeToIdle.add_precondition(new world_property(EEvaluatorId.IS_STATE_LOGIC_ACTIVE, false));
actionAlifeToIdle.add_precondition(new world_property(EEvaluatorId.IS_STATE_IDLE_ALIFE, false));
actionAlifeToIdle.add_effect(new world_property(EEvaluatorId.IS_STATE_IDLE_ALIFE, true));

planner.add_action(EActionId.STATE_TO_IDLE_ALIFE, actionAlifeToIdle);

planner.action(EActionId.ALIFE).add_precondition(new world_property(EEvaluatorId.IS_STATE_IDLE_ALIFE, true));
planner.action(EActionId.COMBAT).add_precondition(new world_property(EEvaluatorId.IS_STATE_IDLE_COMBAT, true));
planner.action(EActionId.ANOMALY).add_precondition(new world_property(EEvaluatorId.IS_STATE_IDLE_COMBAT, true));
planner.action(EActionId.DANGER).add_precondition(new world_property(EEvaluatorId.IS_STATE_IDLE_COMBAT, true));

planner.action(EActionId.GATHER_ITEMS).add_precondition(new world_property(EEvaluatorId.IS_STATE_IDLE_ITEMS, true));
}
62 changes: 62 additions & 0 deletions src/engine/core/objects/ai/setup/state/animation_planner.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, it } from "@jest/globals";

import { setupStalkerAnimationStatePlanner } from "@/engine/core/objects/ai/setup/state/animation_planner";
import { ActionAnimationStart, ActionAnimationStop } from "@/engine/core/objects/ai/state/animation";
import { StalkerStateManager } from "@/engine/core/objects/ai/state/StalkerStateManager";
import { EStateActionId, EStateEvaluatorId } from "@/engine/core/objects/ai/types";
import { ActionPlanner, ClientObject } from "@/engine/lib/types";
import { checkPlannerAction } from "@/fixtures/engine";
import { mockClientGameObject } from "@/fixtures/xray";

describe("setup_state_manager util", () => {
it("should correctly setup state planner animation actions", () => {
const object: ClientObject = mockClientGameObject();
const stateManager: StalkerStateManager = new StalkerStateManager(object);
const planner: ActionPlanner = stateManager.planner;

setupStalkerAnimationStatePlanner(planner, stateManager);

checkPlannerAction(
planner.action(EStateActionId.ANIMATION_START),
ActionAnimationStart,
[
[EStateEvaluatorId.LOCKED, false],
[EStateEvaluatorId.ANIMSTATE_LOCKED, false],
[EStateEvaluatorId.LOCKED_EXTERNAL, false],
[EStateEvaluatorId.ANIMSTATE, true],
[EStateEvaluatorId.SMARTCOVER, true],
[EStateEvaluatorId.IN_SMARTCOVER, false],
[EStateEvaluatorId.DIRECTION, true],
[EStateEvaluatorId.WEAPON, true],
[EStateEvaluatorId.MOVEMENT, true],
[EStateEvaluatorId.MENTAL, true],
[EStateEvaluatorId.BODYSTATE, true],
[EStateEvaluatorId.ANIMATION, false],
[EStateEvaluatorId.ANIMATION_PLAY_NOW, false],
],
[[EStateEvaluatorId.ANIMATION, true]]
);

checkPlannerAction(
planner.action(EStateActionId.ANIMATION_STOP),
ActionAnimationStop,
[
[EStateEvaluatorId.LOCKED, false],
[EStateEvaluatorId.LOCKED_EXTERNAL, false],
[EStateEvaluatorId.ANIMATION_PLAY_NOW, true],
],
[
[EStateEvaluatorId.ANIMATION, true],
[EStateEvaluatorId.ANIMATION_PLAY_NOW, false],
[EStateEvaluatorId.ANIMATION_NONE_NOW, true],
]
);

checkPlannerAction(
planner.action(EStateActionId.LOCKED_ANIMATION),
"ActionStateLockedAnimation",
[[EStateEvaluatorId.ANIMATION_LOCKED, true]],
[[EStateEvaluatorId.ANIMATION_LOCKED, false]]
);
});
});
53 changes: 53 additions & 0 deletions src/engine/core/objects/ai/setup/state/animation_planner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { world_property } from "xray16";

import { StalkerStateManager } from "@/engine/core/objects/ai/state";
import { ActionAnimationStart, ActionAnimationStop } from "@/engine/core/objects/ai/state/animation";
import { ActionStateLocked } from "@/engine/core/objects/ai/state/state";
import { EStateActionId, EStateEvaluatorId } from "@/engine/core/objects/ai/types";
import { ActionPlanner } from "@/engine/lib/types";

/**
* Setup GOAP logics related to animation execution of stalkers.
*
* @param planner - action planner to configure
* @param stateManager - target object state manager
*/
export function setupStalkerAnimationStatePlanner(planner: ActionPlanner, stateManager: StalkerStateManager): void {
// -- START
const animationStartAction: ActionAnimationStart = new ActionAnimationStart(stateManager);

animationStartAction.add_precondition(new world_property(EStateEvaluatorId.LOCKED, false));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.ANIMSTATE_LOCKED, false));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.LOCKED_EXTERNAL, false));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.ANIMSTATE, true));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.SMARTCOVER, true));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.IN_SMARTCOVER, false));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.DIRECTION, true));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.WEAPON, true));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.MOVEMENT, true));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.MENTAL, true));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.BODYSTATE, true));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.ANIMATION, false));
animationStartAction.add_precondition(new world_property(EStateEvaluatorId.ANIMATION_PLAY_NOW, false));
animationStartAction.add_effect(new world_property(EStateEvaluatorId.ANIMATION, true));
planner.add_action(EStateActionId.ANIMATION_START, animationStartAction);

// -- STOP
const animationStopAction: ActionAnimationStop = new ActionAnimationStop(stateManager);

animationStopAction.add_precondition(new world_property(EStateEvaluatorId.LOCKED, false));
animationStopAction.add_precondition(new world_property(EStateEvaluatorId.LOCKED_EXTERNAL, false));
// --action.add_precondition (new world_property(EStateManagerProperty.animstate, true))
// --action.add_precondition (new world_property(EStateManagerProperty.animation, false))
animationStopAction.add_precondition(new world_property(EStateEvaluatorId.ANIMATION_PLAY_NOW, true));
animationStopAction.add_effect(new world_property(EStateEvaluatorId.ANIMATION, true));
animationStopAction.add_effect(new world_property(EStateEvaluatorId.ANIMATION_PLAY_NOW, false));
animationStopAction.add_effect(new world_property(EStateEvaluatorId.ANIMATION_NONE_NOW, true));
planner.add_action(EStateActionId.ANIMATION_STOP, animationStopAction);

const lockedAnimationAction: ActionStateLocked = new ActionStateLocked(stateManager, "ActionStateLockedAnimation");

lockedAnimationAction.add_precondition(new world_property(EStateEvaluatorId.ANIMATION_LOCKED, true));
lockedAnimationAction.add_effect(new world_property(EStateEvaluatorId.ANIMATION_LOCKED, false));
planner.add_action(EStateActionId.LOCKED_ANIMATION, lockedAnimationAction);
}
Loading

0 comments on commit 1d58f77

Please sign in to comment.