-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SYNC] Moved AI logics to separate folders. (#22)
- Loading branch information
Showing
217 changed files
with
2,550 additions
and
2,046 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
99
src/engine/core/objects/ai/setup/motivation_planner.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]], []); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
62
src/engine/core/objects/ai/setup/state/animation_planner.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
53
src/engine/core/objects/ai/setup/state/animation_planner.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Oops, something went wrong.