From 1ed8d3e8d13777b7a786d29ab2c8e3fb31f7286e Mon Sep 17 00:00:00 2001 From: Neloreck Date: Thu, 6 Jul 2023 00:17:33 +0300 Subject: [PATCH 1/2] Run builds for `main` branch instead of `master`. --- .github/workflows/build_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 413d5e786..5168e5a28 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -2,9 +2,9 @@ name: build-and-test on: push: - branches: [ "master", "dev" ] + branches: [ "main", "dev" ] pull_request: - branches: [ "master", "dev" ] + branches: [ "main", "dev" ] jobs: test: From 519e644dee84c42ed49c3195a7cf02a66bef1fab Mon Sep 17 00:00:00 2001 From: Neloreck Date: Thu, 6 Jul 2023 02:11:29 +0300 Subject: [PATCH 2/2] Updated types submodule. Sharded object_check utils. Added new object_* modules. Added unit tests for object utils. Adding tests for object_find and object_get utils. Fixtures extension. Added tests for object set/location utils. Adding tests for object set/sound utils. Add tests for object_spawn utils. Moved utils related to scheme logics. Test utils related to alife updating. Add unit tests for monster command util. More unit tests for ini_sections. --- src/README.md | 8 +- .../core/database/story_objects.test.ts | 25 ++ src/engine/core/database/story_objects.ts | 37 ++- .../interaction/SimulationBoardManager.ts | 12 +- .../managers/interaction/TravelManager.ts | 4 +- .../interaction/dialog/DialogManager.ts | 6 +- .../notifications/NotificationManager.ts | 6 +- .../managers/sounds/GlobalSoundManager.ts | 4 +- src/engine/core/managers/world/DropManager.ts | 14 +- .../core/managers/world/SurgeManager.ts | 13 +- .../objects/binders/creature/MonsterBinder.ts | 9 +- .../objects/binders/creature/StalkerBinder.ts | 23 +- .../sounds/playable_sounds/NpcSound.ts | 4 +- .../core/objects/state/StalkerMoveManager.ts | 6 +- .../evaluators/EvaluatorNeedAnimpoint.ts | 2 +- .../schemes/base/evaluators/EvaluatorEnd.ts | 4 +- .../camper/actions/ActionCamperPatrol.ts | 4 +- .../camper/evaluators/EvaluatorCloseCombat.ts | 2 +- .../core/schemes/combat/SchemeCombat.ts | 4 +- .../evaluators/EvaluatorCombatZombied.ts | 4 +- .../evaluators/EvaluatorNeedCompanion.ts | 2 +- .../corpse_detection/SchemeCorpseDetection.ts | 4 +- .../evaluators/EvaluatorCorpseDetect.ts | 4 +- .../cover/evaluators/EvaluatorNeedCover.ts | 4 +- .../core/schemes/mob_jump/MobJumpManager.ts | 5 +- .../schemes/mob_remark/MobRemarkManager.ts | 18 +- .../schemes/mob_walker/MobWalkerManager.ts | 23 +- .../patrol/evaluators/EvaluatorPatrolEnd.ts | 2 +- .../ph_button/PhysicalButtonManager.ts | 2 +- .../core/schemes/ph_minigun/MinigunManager.ts | 3 +- .../remark/evaluators/EvaluatorNeedRemark.ts | 2 +- .../sleeper/evaluators/EvaluatorNeedSleep.ts | 4 +- .../evaluators/EvaluatorNeedSmartCover.ts | 2 +- .../EvaluatorUseSmartCoverInCombat.ts | 2 +- .../core/schemes/sr_monster/MonsterManager.ts | 11 +- .../walker/evaluators/EvaluatorNeedWalker.ts | 4 +- .../core/schemes/wounded/SchemeWounded.ts | 4 +- src/engine/core/ui/menu/load/LoadDialog.ts | 4 +- src/engine/core/utils/game/game_check.test.ts | 45 +++- src/engine/core/utils/game/game_check.ts | 15 +- src/engine/core/utils/game/game_console.ts | 7 + src/engine/core/utils/game/game_save.test.ts | 48 +++- src/engine/core/utils/game/game_save.ts | 16 +- src/engine/core/utils/game/game_time.test.ts | 29 +- src/engine/core/utils/game/game_time.ts | 145 +++++----- src/engine/core/utils/game/game_wait.ts | 37 +++ src/engine/core/utils/game/index.ts | 1 + .../core/utils/ini/ini_sections.test.ts | 41 +++ src/engine/core/utils/ini/ini_sections.ts | 14 + src/engine/core/utils/ini/ini_types.ts | 15 +- src/engine/core/utils/object/index.ts | 12 +- src/engine/core/utils/object/object_action.ts | 32 --- .../core/utils/object/object_alife.test.ts | 18 ++ src/engine/core/utils/object/object_alife.ts | 25 +- .../core/utils/object/object_check.test.ts | 228 ++++++++++++++-- src/engine/core/utils/object/object_check.ts | 231 +++------------- .../core/utils/object/object_class.test.ts | 102 +++++++ src/engine/core/utils/object/object_class.ts | 81 ++++++ src/engine/core/utils/object/object_danger.ts | 4 +- .../core/utils/object/object_find.test.ts | 249 ++++++++++++++++++ src/engine/core/utils/object/object_find.ts | 118 ++++++--- .../core/utils/object/object_general.ts | 24 -- .../core/utils/object/object_get.test.ts | 107 ++++++++ src/engine/core/utils/object/object_get.ts | 56 ++-- .../utils/object/object_info_portion.test.ts | 8 +- .../core/utils/object/object_info_portion.ts | 31 ++- .../core/utils/object/object_location.test.ts | 114 +++++++- .../core/utils/object/object_location.ts | 92 ++++--- .../core/utils/object/object_section.test.ts | 56 ++++ .../core/utils/object/object_section.ts | 41 +++ .../core/utils/object/object_set.test.ts | 66 +++++ src/engine/core/utils/object/object_set.ts | 95 ++----- .../core/utils/object/object_sound.test.ts | 22 ++ .../core/utils/object/object_spawn.test.ts | 233 ++++++++++++++++ src/engine/core/utils/object/object_spawn.ts | 60 +++-- src/engine/core/utils/object/object_state.ts | 89 +------ .../utils/object/object_task_reward.test.ts | 43 +-- .../core/utils/object/object_task_reward.ts | 33 ++- src/engine/core/utils/scheme/index.ts | 1 + .../core/utils/scheme/scheme_logic.test.ts | 53 ++-- src/engine/core/utils/scheme/scheme_logic.ts | 47 +++- .../core/utils/scheme/scheme_monster.test.ts | 43 ++- .../core/utils/scheme/scheme_monster.ts | 40 ++- .../scheme_object_initialization.test.ts | 196 ++++++++++++++ .../scheme/scheme_object_initialization.ts | 183 +++++++++++++ src/engine/core/utils/scheme/scheme_setup.ts | 6 +- src/engine/core/utils/scheme/scheme_switch.ts | 10 +- src/engine/lib/configs/SurgeConfig.ts | 4 + src/engine/lib/constants/class_ids.ts | 2 +- src/engine/lib/types/xray.ts | 4 + .../scripts/declarations/conditions/object.ts | 17 +- .../scripts/declarations/conditions/quests.ts | 6 +- .../declarations/conditions/relation.ts | 12 +- .../declarations/dialogs/dialog_manager.ts | 4 +- .../scripts/declarations/dialogs/dialogs.ts | 12 +- .../declarations/dialogs/dialogs_zaton.ts | 7 +- src/fixtures/lua/mocks/LuaTable.mock.ts | 18 +- src/fixtures/lua/mocks/lua_math.mocks.ts | 2 +- src/fixtures/utils/function_mock.ts | 14 +- src/fixtures/xray/index.ts | 3 + src/fixtures/xray/mockXRay16.ts | 33 ++- src/fixtures/xray/mocks/actions/cond.mocks.ts | 9 + .../xray/mocks/actions/entity_action.mock.ts | 5 + src/fixtures/xray/mocks/actions/index.ts | 2 + src/fixtures/xray/mocks/device.mock.ts | 5 +- .../xray/mocks/fs/CSavedGameWrapper.mock.ts | 15 ++ src/fixtures/xray/mocks/fs/FileSystem.mock.ts | 16 ++ .../xray/mocks/fs/FileSystemList.mock.ts | 16 ++ src/fixtures/xray/mocks/fs/index.ts | 4 + src/fixtures/xray/mocks/ini/IniFile.mock.ts | 4 + src/fixtures/xray/mocks/ini/files.mock.ts | 31 +++ .../mocks/interface/levelInterface.mock.ts | 21 +- .../xray/mocks/objects/AlifeSimulator.mock.ts | 74 ++++-- .../mocks/objects/client/game_object.mock.ts | 20 +- .../xray/mocks/objects/path/patrols.ts | 18 ++ .../server/cse_alife_human_stalker.mock.ts | 14 +- .../objects/server/cse_alife_object.mock.ts | 5 +- .../cse_alife_online_offline_group.mock.ts | 25 +- .../server/cse_alife_smart_zone.mock.ts | 5 +- .../xray/mocks/objects/server/index.ts | 2 + src/fixtures/xray/mocks/vector.mock.ts | 32 ++- src/typedefs/xray16 | 2 +- 122 files changed, 3012 insertions(+), 1004 deletions(-) create mode 100644 src/engine/core/utils/game/game_wait.ts create mode 100644 src/engine/core/utils/ini/ini_sections.test.ts delete mode 100644 src/engine/core/utils/object/object_action.ts create mode 100644 src/engine/core/utils/object/object_class.test.ts create mode 100644 src/engine/core/utils/object/object_class.ts create mode 100644 src/engine/core/utils/object/object_find.test.ts delete mode 100644 src/engine/core/utils/object/object_general.ts create mode 100644 src/engine/core/utils/object/object_get.test.ts create mode 100644 src/engine/core/utils/object/object_section.test.ts create mode 100644 src/engine/core/utils/object/object_section.ts create mode 100644 src/engine/core/utils/object/object_set.test.ts create mode 100644 src/engine/core/utils/object/object_sound.test.ts create mode 100644 src/engine/core/utils/object/object_spawn.test.ts create mode 100644 src/engine/core/utils/scheme/scheme_object_initialization.test.ts create mode 100644 src/engine/core/utils/scheme/scheme_object_initialization.ts create mode 100644 src/fixtures/xray/mocks/actions/cond.mocks.ts create mode 100644 src/fixtures/xray/mocks/actions/entity_action.mock.ts create mode 100644 src/fixtures/xray/mocks/fs/CSavedGameWrapper.mock.ts create mode 100644 src/fixtures/xray/mocks/fs/FileSystemList.mock.ts create mode 100644 src/fixtures/xray/mocks/fs/index.ts diff --git a/src/README.md b/src/README.md index 03dbbc6be..c2f9c116d 100644 --- a/src/README.md +++ b/src/README.md @@ -4,16 +4,16 @@ This directory contains the source code for the XRF engine modification template ## engine -Contains engine source code. +Script engine source code. ## fixtures -Contains code related to test mocks and utilities. +Test mocks and utilities. ## resources -Contains all assets that should be copied to the target root folder when building the engine modification. +Assets that should be copied to the target root folder when building the engine modification. ## typedefs -Contains all types definitions used in project. +Types definitions used in project. diff --git a/src/engine/core/database/story_objects.test.ts b/src/engine/core/database/story_objects.test.ts index cb19cf74d..2835c85bb 100644 --- a/src/engine/core/database/story_objects.test.ts +++ b/src/engine/core/database/story_objects.test.ts @@ -7,6 +7,8 @@ import { getObjectIdByStoryId, getServerObjectByStoryId, getStoryIdByObjectId, + isStoryObject, + isStoryObjectExisting, registerObjectStoryLinks, registerStoryLink, unregisterStoryLinkByObjectId, @@ -68,4 +70,27 @@ describe("'story_objects' module of the database", () => { unregisterObject(clientObject); unregisterStoryLinkByStoryId("test-sid"); }); + + it("'isStoryObjectExisting' should correctly check if object is existing", () => { + expect(isStoryObjectExisting("test-sid")).toBe(false); + + const serverObject: ServerObject = mockServerAlifeObject(); + + registerStoryLink(serverObject.id, "test-sid"); + + expect(isStoryObjectExisting("test-sid")).toBe(true); + }); + + it("'isStoryObject' should correctly check if object is existing", () => { + const serverObject: ServerObject = mockServerAlifeObject(); + const clientObject: ClientObject = mockClientGameObject({ id: () => serverObject.id }); + + expect(isStoryObject(serverObject)).toBe(false); + expect(isStoryObject(clientObject)).toBe(false); + + registerStoryLink(serverObject.id, "is-story-object-example"); + + expect(isStoryObject(serverObject)).toBe(true); + expect(isStoryObject(clientObject)).toBe(true); + }); }); diff --git a/src/engine/core/database/story_objects.ts b/src/engine/core/database/story_objects.ts index c088ffa26..ba9205944 100644 --- a/src/engine/core/database/story_objects.ts +++ b/src/engine/core/database/story_objects.ts @@ -4,7 +4,16 @@ import { SYSTEM_INI } from "@/engine/core/database/ini_registry"; import { registry } from "@/engine/core/database/registry"; import { abort, assert } from "@/engine/core/utils/assertion"; import { readIniString } from "@/engine/core/utils/ini/ini_read"; -import { ClientObject, IniFile, Optional, ServerObject, TName, TNumberId, TStringId } from "@/engine/lib/types"; +import { + AnyGameObject, + ClientObject, + IniFile, + Optional, + ServerObject, + TName, + TNumberId, + TStringId, +} from "@/engine/lib/types"; /** * Register story object link based on ini configuration. @@ -86,6 +95,32 @@ export function unregisterStoryLinkByStoryId(storyId: TStringId): void { } } +/** + * Check whether object has story link. + * + * @param object - object to check + * @returns whether provided object has linked story id. + */ +export function isStoryObject(object: AnyGameObject): boolean { + if (type(object.id) === "function") { + return registry.storyLink.sidById.has((object as ClientObject).id()); + } else { + return registry.storyLink.sidById.has((object as ServerObject).id); + } +} + +/** + * Check whether story object exists. + * + * @param storyId - story ID to check existing + * @returns whether story object exists + */ +export function isStoryObjectExisting(storyId: TStringId): boolean { + const objectId: Optional = registry.storyLink.idBySid.get(storyId); + + return objectId === null ? false : alife().object(objectId) !== null; +} + /** * Get object story ID by provided game object ID. * diff --git a/src/engine/core/managers/interaction/SimulationBoardManager.ts b/src/engine/core/managers/interaction/SimulationBoardManager.ts index 3eba141ae..6a0ace681 100644 --- a/src/engine/core/managers/interaction/SimulationBoardManager.ts +++ b/src/engine/core/managers/interaction/SimulationBoardManager.ts @@ -12,7 +12,7 @@ import { abort } from "@/engine/core/utils/assertion"; import { parseStringsList } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; import { evaluateSimulationPriority } from "@/engine/core/utils/object/object_alife"; -import { changeTeamSquadGroup } from "@/engine/core/utils/object/object_set"; +import { setObjectTeamSquadGroup } from "@/engine/core/utils/object/object_set"; import { setSquadRelationToActor } from "@/engine/core/utils/relation"; import { TCommunity } from "@/engine/lib/constants/communities"; import { levels, TLevel } from "@/engine/lib/constants/levels"; @@ -360,7 +360,7 @@ export class SimulationBoardManager extends AbstractCoreManager { const smartTerrainDescriptor: ISmartTerrainDescriptor = this.smartTerrains.get(smartTerrainId); - if (squad.enteredSmartTerrainId !== null) { + if (squad.enteredSmartTerrainId) { abort("Couldn't enter smart, still in old one. Squad: [%s]", squad.name()); } @@ -382,12 +382,12 @@ export class SimulationBoardManager extends AbstractCoreManager { object = alife().object(object.id)!; // todo: Check, probably magic or unused code with duplicated changeTeam calls. - changeTeamSquadGroup(object, object.team, object.squad, groupId); + setObjectTeamSquadGroup(object, object.team, object.squad, groupId); const squad: Optional = alife().object(object.group_id); if (squad === null) { - return changeTeamSquadGroup(object, object.team, 0, object.group); + return setObjectTeamSquadGroup(object, object.team, 0, object.group); } let smartTerrain: Optional = null; @@ -399,7 +399,7 @@ export class SimulationBoardManager extends AbstractCoreManager { } if (smartTerrain === null) { - return changeTeamSquadGroup(object, object.team, 0, object.group); + return setObjectTeamSquadGroup(object, object.team, 0, object.group); } let objectSquadId: TNumberId = 0; @@ -408,7 +408,7 @@ export class SimulationBoardManager extends AbstractCoreManager { objectSquadId = smartTerrain.squadId; } - changeTeamSquadGroup(object, object.team, objectSquadId, object.group); + setObjectTeamSquadGroup(object, object.team, objectSquadId, object.group); } /** diff --git a/src/engine/core/managers/interaction/TravelManager.ts b/src/engine/core/managers/interaction/TravelManager.ts index 7b39429bc..3b06149d5 100644 --- a/src/engine/core/managers/interaction/TravelManager.ts +++ b/src/engine/core/managers/interaction/TravelManager.ts @@ -16,7 +16,7 @@ import { createAutoSave } from "@/engine/core/utils/game/game_save"; import { parseConditionsList, pickSectionFromCondList, TConditionList } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; import { - getAlifeCharacterCommunity, + getObjectCommunity, getObjectSmartTerrain, getObjectSquad, getServerDistanceBetween, @@ -240,7 +240,7 @@ export class TravelManager extends AbstractCoreManager { if (targetClsId === clsid.script_actor) { abort("Actor talking with squad, which chasing actor."); } else if (targetClsId === clsid.online_offline_group_s) { - return "dm_" + communities.stalker + "_chasing_squad_" + getAlifeCharacterCommunity(targetSquadObject as Squad); + return "dm_" + communities.stalker + "_chasing_squad_" + getObjectCommunity(targetSquadObject as Squad); } else if (targetClsId === clsid.smart_terrain) { const smartName: TName = targetSquadObject.name(); const smartDescription: TLabel = this.smartDescriptionsByName.get(smartName); diff --git a/src/engine/core/managers/interaction/dialog/DialogManager.ts b/src/engine/core/managers/interaction/dialog/DialogManager.ts index a4b3015f3..7a8937174 100644 --- a/src/engine/core/managers/interaction/dialog/DialogManager.ts +++ b/src/engine/core/managers/interaction/dialog/DialogManager.ts @@ -19,7 +19,7 @@ import { import { assert } from "@/engine/core/utils/assertion"; import { parseInfoPortions, parseStringsList } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { getCharacterCommunity, isObjectWounded } from "@/engine/core/utils/object"; +import { getObjectCommunity, isObjectWounded } from "@/engine/core/utils/object"; import { hasAlifeInfo } from "@/engine/core/utils/object/object_info_portion"; import { FALSE, TRUE } from "@/engine/lib/constants/words"; import { @@ -308,7 +308,7 @@ export class DialogManager extends AbstractCoreManager { fComm = true; } else { for (const i of $range(1, PTIDSubtable.npc_community.length())) { - if (PTIDSubtable.npc_community.get(i) === getCharacterCommunity(object)) { + if (PTIDSubtable.npc_community.get(i) === getObjectCommunity(object)) { priority = priority + 2; fComm = true; break; @@ -339,7 +339,7 @@ export class DialogManager extends AbstractCoreManager { priority = priority + 1; } else { for (const i of $range(1, PTIDSubtable.actor_community.length())) { - if (PTIDSubtable.actor_community.get(i) === getCharacterCommunity(registry.actor)) { + if (PTIDSubtable.actor_community.get(i) === getObjectCommunity(registry.actor)) { priority = priority + 2; break; } diff --git a/src/engine/core/managers/interface/notifications/NotificationManager.ts b/src/engine/core/managers/interface/notifications/NotificationManager.ts index 08a60fca1..dd2f08a32 100644 --- a/src/engine/core/managers/interface/notifications/NotificationManager.ts +++ b/src/engine/core/managers/interface/notifications/NotificationManager.ts @@ -1,4 +1,4 @@ -import { alife, game } from "xray16"; +import { alife, clsid, game } from "xray16"; import { getObjectIdByStoryId, registry } from "@/engine/core/database"; import { AbstractCoreManager } from "@/engine/core/managers/base/AbstractCoreManager"; @@ -29,7 +29,7 @@ import { GlobalSoundManager } from "@/engine/core/managers/sounds/GlobalSoundMan import { Stalker } from "@/engine/core/objects"; import { abort, assert } from "@/engine/core/utils/assertion"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isObjectWounded, isStalkerClassId } from "@/engine/core/utils/object"; +import { isObjectWounded } from "@/engine/core/utils/object"; import { getInventoryNameForItemSection } from "@/engine/core/utils/object/object_spawn"; import { captions, TCaption } from "@/engine/lib/constants/captions/captions"; import { scriptSounds } from "@/engine/lib/constants/sound/script_sounds"; @@ -338,7 +338,7 @@ export class NotificationManager extends AbstractCoreManager { let textureName: TTexture = textures.ui_iconsTotal_grouping; - if (object !== null && isStalkerClassId(object.clsid())) { + if (object !== null && (object.clsid() === clsid.script_stalker || object.clsid() === clsid.stalker)) { textureName = object.character_icon(); } else if (notificationManagerIcons[faction as TNotificationIconKey]) { textureName = notificationManagerIcons[faction as TNotificationIconKey]; diff --git a/src/engine/core/managers/sounds/GlobalSoundManager.ts b/src/engine/core/managers/sounds/GlobalSoundManager.ts index 87c83ab1d..521d8f71e 100644 --- a/src/engine/core/managers/sounds/GlobalSoundManager.ts +++ b/src/engine/core/managers/sounds/GlobalSoundManager.ts @@ -17,7 +17,7 @@ import { EPlayableSound } from "@/engine/core/objects/sounds/types"; import { abort, assert, assertDefined } from "@/engine/core/utils/assertion"; import { readIniString } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { getCharacterCommunity } from "@/engine/core/utils/object/object_get"; +import { getObjectCommunity } from "@/engine/core/utils/object/object_get"; import { getTableSize, resetTable } from "@/engine/core/utils/table"; import { ClientObject, @@ -44,7 +44,7 @@ export class GlobalSoundManager extends AbstractCoreManager { public static initializeObjectSounds(object: ClientObject): void { for (const [key, sound] of registry.sounds.themes) { if (sound.type === NpcSound.type) { - if ((sound as NpcSound).availableCommunities.has(getCharacterCommunity(object))) { + if ((sound as NpcSound).availableCommunities.has(getObjectCommunity(object))) { (sound as NpcSound).initializeObject(object); } } diff --git a/src/engine/core/managers/world/DropManager.ts b/src/engine/core/managers/world/DropManager.ts index 92a75cc0c..476dea526 100644 --- a/src/engine/core/managers/world/DropManager.ts +++ b/src/engine/core/managers/world/DropManager.ts @@ -7,12 +7,12 @@ import { abort } from "@/engine/core/utils/assertion"; import { parseNumbersList, parseStringsList } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; import { - getCharacterCommunity, - isAmmoItem, + getObjectCommunity, + isAmmoSection, isArtefact, - isExcludedFromLootDropItem, + isExcludedFromLootDropItemSection, isGrenade, - isLootableItem, + isLootableItemSection, isWeapon, setItemCondition, spawnItemsForObject, @@ -196,7 +196,7 @@ export class DropManager extends AbstractCoreManager { } const spawnItems: Optional> = this.itemsByCommunity.get( - getCharacterCommunity(object) + getObjectCommunity(object) ); if (spawnItems === null) { @@ -233,7 +233,7 @@ export class DropManager extends AbstractCoreManager { return; } - if (isExcludedFromLootDropItem(item)) { + if (isExcludedFromLootDropItemSection(section)) { alife().release(alife().object(item.id()), true); return; @@ -269,7 +269,7 @@ export class DropManager extends AbstractCoreManager { return; } - if (isLootableItem(item) && !isAmmoItem(item)) { + if (isLootableItemSection(item.section()) && !isAmmoSection(item.section())) { logger.info("Keep item, misc lootable:", object.name(), item.name(), section); return; diff --git a/src/engine/core/managers/world/SurgeManager.ts b/src/engine/core/managers/world/SurgeManager.ts index 094ee0cfa..993d06014 100644 --- a/src/engine/core/managers/world/SurgeManager.ts +++ b/src/engine/core/managers/world/SurgeManager.ts @@ -3,6 +3,7 @@ import { alife, game, hit, level } from "xray16"; import { closeLoadMarker, closeSaveMarker, + isStoryObject, openLoadMarker, openSaveMarker, registry, @@ -25,13 +26,7 @@ import { createAutoSave } from "@/engine/core/utils/game/game_save"; import { readTimeFromPacket, writeTimeToPacket } from "@/engine/core/utils/game/game_time"; import { parseConditionsList, pickSectionFromCondList, TConditionList } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { - isArtefact, - isImmuneToSurge, - isObjectOnLevel, - isStoryObject, - isSurgeEnabledOnLevel, -} from "@/engine/core/utils/object"; +import { isArtefact, isImmuneToSurgeObject, isObjectOnLevel, isSurgeEnabledOnLevel } from "@/engine/core/utils/object"; import { disableInfo, giveInfo, hasAlifeInfo } from "@/engine/core/utils/object/object_info_portion"; import { createVector } from "@/engine/core/utils/vector"; import { surgeConfig } from "@/engine/lib/configs/SurgeConfig"; @@ -490,7 +485,7 @@ export class SurgeManager extends AbstractCoreManager { logger.info("Releasing squads:", simulationBoardManager.getSquads().length()); for (const [squadId, squad] of simulationBoardManager.getSquads()) { - if (isObjectOnLevel(squad, levelName) && !isImmuneToSurge(squad) && !isStoryObject(squad)) { + if (isObjectOnLevel(squad, levelName) && !isImmuneToSurgeObject(squad) && !isStoryObject(squad)) { for (const member of squad.squad_members()) { if (!isStoryObject(member.object)) { if (this.canReleaseSquad(squad)) { @@ -571,7 +566,7 @@ export class SurgeManager extends AbstractCoreManager { const activeCovers: LuaArray = this.getCoverObjects(); for (const [squadId, squad] of simulationBoardManager.getSquads()) { - if (isObjectOnLevel(squad, levelName) && !isImmuneToSurge(squad)) { + if (isObjectOnLevel(squad, levelName) && !isImmuneToSurgeObject(squad)) { for (const member of squad.squad_members()) { let isInCover: boolean = false; diff --git a/src/engine/core/objects/binders/creature/MonsterBinder.ts b/src/engine/core/objects/binders/creature/MonsterBinder.ts index 19b30da71..fdf10993a 100644 --- a/src/engine/core/objects/binders/creature/MonsterBinder.ts +++ b/src/engine/core/objects/binders/creature/MonsterBinder.ts @@ -25,11 +25,12 @@ import { ESchemeEvent, IBaseSchemeState } from "@/engine/core/schemes"; import { SchemeHear } from "@/engine/core/schemes/hear/SchemeHear"; import { pickSectionFromCondList, TConditionList } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { action, getObjectSquad } from "@/engine/core/utils/object"; +import { getObjectSquad } from "@/engine/core/utils/object"; import { emitSchemeEvent, isMonsterScriptCaptured, scriptCaptureMonster, + scriptCommandMonster, scriptReleaseMonster, trySwitchToAnotherSection, } from "@/engine/core/utils/scheme"; @@ -124,14 +125,14 @@ export class MonsterBinder extends object_binder { scriptCaptureMonster(this.object, true); if (squad.commander_id() === this.object.id()) { - action(this.object, new move(move.walk_with_leader, targetPosition), new cond(cond.move_end)); + scriptCommandMonster(this.object, new move(move.walk_with_leader, targetPosition), new cond(cond.move_end)); } else { const commanderPosition: Vector = alife().object(squad.commander_id())!.position; if (commanderPosition.distance_to_sqr(this.object.position()) > 100) { - action(this.object, new move(move.run_with_leader, targetPosition), new cond(cond.move_end)); + scriptCommandMonster(this.object, new move(move.run_with_leader, targetPosition), new cond(cond.move_end)); } else { - action(this.object, new move(move.walk_with_leader, targetPosition), new cond(cond.move_end)); + scriptCommandMonster(this.object, new move(move.walk_with_leader, targetPosition), new cond(cond.move_end)); } } diff --git a/src/engine/core/objects/binders/creature/StalkerBinder.ts b/src/engine/core/objects/binders/creature/StalkerBinder.ts index 164edf797..193d56512 100644 --- a/src/engine/core/objects/binders/creature/StalkerBinder.ts +++ b/src/engine/core/objects/binders/creature/StalkerBinder.ts @@ -20,15 +20,17 @@ import { DUMMY_LTX, getStoryIdByObjectId, IRegistryObjectState, + loadObjectLogic, openLoadMarker, openSaveMarker, registerHelicopterEnemy, + registerStalker, registry, resetObject, + saveObjectLogic, unregisterHelicopterEnemy, + unregisterStalker, } from "@/engine/core/database"; -import { loadObjectLogic, saveObjectLogic } from "@/engine/core/database/logic"; -import { registerStalker, unregisterStalker } from "@/engine/core/database/stalker"; import { EGameEvent, EventsManager } from "@/engine/core/managers/events"; import { DialogManager } from "@/engine/core/managers/interaction/dialog/DialogManager"; import { SimulationBoardManager } from "@/engine/core/managers/interaction/SimulationBoardManager"; @@ -42,7 +44,7 @@ import { setupSmartJobsAndLogicOnSpawn } from "@/engine/core/objects/server/smar import { SmartTerrain } from "@/engine/core/objects/server/smart_terrain/SmartTerrain"; import { addStateManager } from "@/engine/core/objects/state/add_state_manager"; import { StalkerMoveManager } from "@/engine/core/objects/state/StalkerMoveManager"; -import { ESchemeEvent, IBaseSchemeState } from "@/engine/core/schemes"; +import { ESchemeEvent, IBaseSchemeState } from "@/engine/core/schemes/base"; import { SchemeCombat } from "@/engine/core/schemes/combat/SchemeCombat"; import { PostCombatIdle } from "@/engine/core/schemes/combat_idle/PostCombatIdle"; import { SchemeHear } from "@/engine/core/schemes/hear/SchemeHear"; @@ -52,10 +54,13 @@ import { SchemeLight } from "@/engine/core/schemes/sr_light/SchemeLight"; import { SchemeWounded } from "@/engine/core/schemes/wounded/SchemeWounded"; import { pickSectionFromCondList, readIniString, TConditionList } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { getCharacterCommunity, getObjectSquad } from "@/engine/core/utils/object/object_get"; -import { updateObjectInvulnerability } from "@/engine/core/utils/object/object_set"; +import { getObjectCommunity, getObjectSquad } from "@/engine/core/utils/object"; import { ERelation, setClientObjectRelation, setObjectSympathy } from "@/engine/core/utils/relation"; -import { emitSchemeEvent, trySwitchToAnotherSection } from "@/engine/core/utils/scheme"; +import { + emitSchemeEvent, + initializeObjectInvulnerability, + trySwitchToAnotherSection, +} from "@/engine/core/utils/scheme"; import { createEmptyVector } from "@/engine/core/utils/vector"; import { communities, TCommunity } from "@/engine/lib/constants/communities"; import { MAX_U16 } from "@/engine/lib/constants/memory"; @@ -207,7 +212,7 @@ export class StalkerBinder extends object_binder { setupSmartJobsAndLogicOnSpawn(this.object, this.state, object, ESchemeType.STALKER, this.isLoaded); - if (getCharacterCommunity(this.object) !== communities.zombied) { + if (getObjectCommunity(this.object) !== communities.zombied) { PostCombatIdle.addPostCombatIdleWait(this.object); } @@ -298,7 +303,7 @@ export class StalkerBinder extends object_binder { if (isObjectAlive) { GlobalSoundManager.getInstance().update(object.id()); SchemeMeet.updateObjectInteractionAvailability(object); - updateObjectInvulnerability(this.object); + initializeObjectInvulnerability(this.object); } const squad = getObjectSquad(this.object); @@ -449,7 +454,7 @@ export class StalkerBinder extends object_binder { this.resetCallbacks(); if (actor_stats.remove_from_ranking !== null) { - const community: TCommunity = getCharacterCommunity(this.object); + const community: TCommunity = getObjectCommunity(this.object); if (community === communities.zombied || community === communities.monolith) { // placeholder diff --git a/src/engine/core/objects/sounds/playable_sounds/NpcSound.ts b/src/engine/core/objects/sounds/playable_sounds/NpcSound.ts index 5a500c559..7bbb045f1 100644 --- a/src/engine/core/objects/sounds/playable_sounds/NpcSound.ts +++ b/src/engine/core/objects/sounds/playable_sounds/NpcSound.ts @@ -8,7 +8,7 @@ import { EPlayableSound, ESoundPlaylistType } from "@/engine/core/objects/sounds import { abort } from "@/engine/core/utils/assertion"; import { parseStringsList, readIniBoolean, readIniNumber, readIniString } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { getCharacterCommunity } from "@/engine/core/utils/object/object_get"; +import { getObjectCommunity } from "@/engine/core/utils/object/object_get"; import { createEmptyVector } from "@/engine/core/utils/vector"; import { communities, TCommunity } from "@/engine/lib/constants/communities"; import { roots } from "@/engine/lib/constants/roots"; @@ -301,7 +301,7 @@ export class NpcSound extends AbstractPlayableSound { if (game.translate_string(soundCaption) !== soundCaption) { if (!faction) { - faction = getCharacterCommunity(object); + faction = getObjectCommunity(object); } // Attempt to auto-translate npc goal. diff --git a/src/engine/core/objects/state/StalkerMoveManager.ts b/src/engine/core/objects/state/StalkerMoveManager.ts index a5eb0ee96..2c7da27f4 100644 --- a/src/engine/core/objects/state/StalkerMoveManager.ts +++ b/src/engine/core/objects/state/StalkerMoveManager.ts @@ -7,7 +7,7 @@ import { pickSectionFromCondList } from "@/engine/core/utils/ini/ini_config"; import { parseConditionsList } from "@/engine/core/utils/ini/ini_parse"; import { IWaypointData, TConditionList } from "@/engine/core/utils/ini/ini_types"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isStalkerAtWaypoint } from "@/engine/core/utils/object"; +import { isObjectAtWaypoint } from "@/engine/core/utils/object"; import { TRUE } from "@/engine/lib/constants/words"; import { AnyCallable, @@ -385,7 +385,7 @@ export class StalkerMoveManager { */ public isStandingOnTerminalWaypoint(): LuaMultiReturn<[boolean, Optional]> { for (const idx of $range(0, this.patrolWalk!.count() - 1)) { - if (isStalkerAtWaypoint(this.object, this.patrolWalk!, idx) && this.patrolWalk!.terminal(idx)) { + if (isObjectAtWaypoint(this.object, this.patrolWalk!, idx) && this.patrolWalk!.terminal(idx)) { return $multi(true, idx); } } @@ -446,7 +446,7 @@ export class StalkerMoveManager { } if (this.lastIndex && this.patrolWalk!.terminal(this.lastIndex)) { - if (isStalkerAtWaypoint(this.object, this.patrolWalk!, this.lastIndex)) { + if (isObjectAtWaypoint(this.object, this.patrolWalk!, this.lastIndex)) { this.onWaypoint(this.object, null, this.lastIndex); return; diff --git a/src/engine/core/schemes/animpoint/evaluators/EvaluatorNeedAnimpoint.ts b/src/engine/core/schemes/animpoint/evaluators/EvaluatorNeedAnimpoint.ts index 6452f200f..661bd308d 100644 --- a/src/engine/core/schemes/animpoint/evaluators/EvaluatorNeedAnimpoint.ts +++ b/src/engine/core/schemes/animpoint/evaluators/EvaluatorNeedAnimpoint.ts @@ -2,7 +2,7 @@ import { LuabindClass, property_evaluator } from "xray16"; import { ISchemeAnimpointState } from "@/engine/core/schemes/animpoint/ISchemeAnimpointState"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isActiveSection } from "@/engine/core/utils/object"; +import { isActiveSection } from "@/engine/core/utils/scheme"; const logger: LuaLogger = new LuaLogger($filename); diff --git a/src/engine/core/schemes/base/evaluators/EvaluatorEnd.ts b/src/engine/core/schemes/base/evaluators/EvaluatorEnd.ts index ded061504..b5e1aefc7 100644 --- a/src/engine/core/schemes/base/evaluators/EvaluatorEnd.ts +++ b/src/engine/core/schemes/base/evaluators/EvaluatorEnd.ts @@ -2,7 +2,7 @@ import { LuabindClass, property_evaluator } from "xray16"; import { IBaseSchemeState } from "@/engine/core/schemes/base"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isActiveSection } from "@/engine/core/utils/object"; +import { isActiveSection } from "@/engine/core/utils/scheme"; const logger: LuaLogger = new LuaLogger($filename); @@ -19,7 +19,7 @@ export class EvaluatorEnd extends property_evaluator { } /** - * Check whether evaluator scheme state is still active or ended. + * Check whether scheme is still active or ended. */ public override evaluate(): boolean { return !isActiveSection(this.object, this.state.section); diff --git a/src/engine/core/schemes/camper/actions/ActionCamperPatrol.ts b/src/engine/core/schemes/camper/actions/ActionCamperPatrol.ts index 89845c268..2d6d03d43 100644 --- a/src/engine/core/schemes/camper/actions/ActionCamperPatrol.ts +++ b/src/engine/core/schemes/camper/actions/ActionCamperPatrol.ts @@ -7,7 +7,7 @@ import { StalkerMoveManager } from "@/engine/core/objects/state/StalkerMoveManag import { ICampPoint, ISchemeCamperState } from "@/engine/core/schemes/camper/ISchemeCamperState"; import { abort } from "@/engine/core/utils/assertion"; import { parseWaypointsData } from "@/engine/core/utils/ini"; -import { isObjectFacingDanger, isStalkerAtWaypoint } from "@/engine/core/utils/object"; +import { isObjectAtWaypoint, isObjectFacingDanger } from "@/engine/core/utils/object"; import { createVector } from "@/engine/core/utils/vector"; import { ClientObject, DangerObject, Optional, Vector } from "@/engine/lib/types"; @@ -523,7 +523,7 @@ export class ActionCamperPatrol extends action_base { if (path !== null) { for (const k of $range(0, path.count() - 1)) { - if (isStalkerAtWaypoint(this.object, new patrol(this.state.path_walk), k)) { + if (isObjectAtWaypoint(this.object, new patrol(this.state.path_walk), k)) { for (const i of $range(0, 31)) { if (path.flag(k, i)) { this.state.wp_flag = i; diff --git a/src/engine/core/schemes/camper/evaluators/EvaluatorCloseCombat.ts b/src/engine/core/schemes/camper/evaluators/EvaluatorCloseCombat.ts index 51ce0ad09..d9ef9edd5 100644 --- a/src/engine/core/schemes/camper/evaluators/EvaluatorCloseCombat.ts +++ b/src/engine/core/schemes/camper/evaluators/EvaluatorCloseCombat.ts @@ -3,7 +3,7 @@ import { LuabindClass, property_evaluator, stalker_ids, time_global } from "xray import { EEvaluatorId } from "@/engine/core/schemes/base"; import { ISchemeCamperState } from "@/engine/core/schemes/camper/ISchemeCamperState"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isActiveSection } from "@/engine/core/utils/object"; +import { isActiveSection } from "@/engine/core/utils/scheme"; import { ActionPlanner, Optional } from "@/engine/lib/types"; const logger: LuaLogger = new LuaLogger($filename); diff --git a/src/engine/core/schemes/combat/SchemeCombat.ts b/src/engine/core/schemes/combat/SchemeCombat.ts index 56325342f..a934fc4cd 100644 --- a/src/engine/core/schemes/combat/SchemeCombat.ts +++ b/src/engine/core/schemes/combat/SchemeCombat.ts @@ -10,7 +10,7 @@ import { getConfigSwitchConditions, pickSectionFromCondList } from "@/engine/cor import { parseConditionsList } from "@/engine/core/utils/ini/ini_parse"; import { readIniConditionList } from "@/engine/core/utils/ini/ini_read"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { getCharacterCommunity } from "@/engine/core/utils/object"; +import { getObjectCommunity } from "@/engine/core/utils/object"; import { communities } from "@/engine/lib/constants/communities"; import { NIL } from "@/engine/lib/constants/words"; import { ActionBase, ActionPlanner, AnyObject, ClientObject, IniFile, Optional, TName } from "@/engine/lib/types"; @@ -34,7 +34,7 @@ export class SchemeCombat extends AbstractScheme { } public static override activate(object: ClientObject, ini: IniFile, scheme: EScheme, section: TSection): void { - const isZombied: boolean = getCharacterCommunity(object) === communities.zombied; + const isZombied: boolean = getObjectCommunity(object) === communities.zombied; if (section || isZombied) { logger.info("Activate scheme:", object.name()); diff --git a/src/engine/core/schemes/combat_zombied/evaluators/EvaluatorCombatZombied.ts b/src/engine/core/schemes/combat_zombied/evaluators/EvaluatorCombatZombied.ts index 41af7056e..1c4830a59 100644 --- a/src/engine/core/schemes/combat_zombied/evaluators/EvaluatorCombatZombied.ts +++ b/src/engine/core/schemes/combat_zombied/evaluators/EvaluatorCombatZombied.ts @@ -2,7 +2,7 @@ import { LuabindClass, property_evaluator } from "xray16"; import { ISchemeCombatState } from "@/engine/core/schemes/combat"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { getCharacterCommunity } from "@/engine/core/utils/object"; +import { getObjectCommunity } from "@/engine/core/utils/object"; import { communities } from "@/engine/lib/constants/communities"; const logger: LuaLogger = new LuaLogger($filename); @@ -23,6 +23,6 @@ export class EvaluatorCombatZombied extends property_evaluator { * Check whether zombied combat should be applied. */ public override evaluate(): boolean { - return getCharacterCommunity(this.object) === communities.zombied; + return getObjectCommunity(this.object) === communities.zombied; } } diff --git a/src/engine/core/schemes/companion/evaluators/EvaluatorNeedCompanion.ts b/src/engine/core/schemes/companion/evaluators/EvaluatorNeedCompanion.ts index a82ed7a8e..aff68835e 100644 --- a/src/engine/core/schemes/companion/evaluators/EvaluatorNeedCompanion.ts +++ b/src/engine/core/schemes/companion/evaluators/EvaluatorNeedCompanion.ts @@ -2,7 +2,7 @@ import { LuabindClass, property_evaluator } from "xray16"; import { ISchemeCompanionState } from "@/engine/core/schemes/companion"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isActiveSection } from "@/engine/core/utils/object"; +import { isActiveSection } from "@/engine/core/utils/scheme"; const logger: LuaLogger = new LuaLogger($filename); diff --git a/src/engine/core/schemes/corpse_detection/SchemeCorpseDetection.ts b/src/engine/core/schemes/corpse_detection/SchemeCorpseDetection.ts index d2bb6b9fa..b417a4971 100644 --- a/src/engine/core/schemes/corpse_detection/SchemeCorpseDetection.ts +++ b/src/engine/core/schemes/corpse_detection/SchemeCorpseDetection.ts @@ -8,7 +8,7 @@ import { EvaluatorCorpseDetect } from "@/engine/core/schemes/corpse_detection/ev import { ISchemeCorpseDetectionState } from "@/engine/core/schemes/corpse_detection/ISchemeCorpseDetectionState"; import { readIniBoolean } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isLootableItem } from "@/engine/core/utils/object"; +import { isLootableItemSection } from "@/engine/core/utils/object"; import { ActionPlanner, ClientObject, IniFile, Optional, TNumberId } from "@/engine/lib/types"; import { EScheme, ESchemeType, TSection } from "@/engine/lib/types/scheme"; @@ -116,7 +116,7 @@ export class SchemeCorpseDetection extends AbstractScheme { } corpseObject.iterate_inventory((object, item) => { - if (isLootableItem(item)) { + if (isLootableItemSection(item.section())) { object.transfer_item(item, object); } }, corpseObject); diff --git a/src/engine/core/schemes/corpse_detection/evaluators/EvaluatorCorpseDetect.ts b/src/engine/core/schemes/corpse_detection/evaluators/EvaluatorCorpseDetect.ts index b73c46485..1317327bf 100644 --- a/src/engine/core/schemes/corpse_detection/evaluators/EvaluatorCorpseDetect.ts +++ b/src/engine/core/schemes/corpse_detection/evaluators/EvaluatorCorpseDetect.ts @@ -3,7 +3,7 @@ import { level, LuabindClass, property_evaluator } from "xray16"; import { IRegistryObjectState, registry } from "@/engine/core/database"; import { IReleaseDescriptor, ReleaseBodyManager } from "@/engine/core/managers/world/ReleaseBodyManager"; import { ISchemeCorpseDetectionState } from "@/engine/core/schemes/corpse_detection"; -import { isLootableItem, isObjectWounded } from "@/engine/core/utils/object"; +import { isLootableItemSection, isObjectWounded } from "@/engine/core/utils/object"; import { communities } from "@/engine/lib/constants/communities"; import { ClientObject, Optional, TDistance, TNumberId, Vector } from "@/engine/lib/types"; @@ -46,7 +46,7 @@ export class EvaluatorCorpseDetect extends property_evaluator { let hasValuableLoot: boolean = false; const checkLoot = (npc: ClientObject, item: ClientObject) => { - if (isLootableItem(item)) { + if (isLootableItemSection(item.section())) { hasValuableLoot = true; } }; diff --git a/src/engine/core/schemes/cover/evaluators/EvaluatorNeedCover.ts b/src/engine/core/schemes/cover/evaluators/EvaluatorNeedCover.ts index 512cfdbb1..5c96a2520 100644 --- a/src/engine/core/schemes/cover/evaluators/EvaluatorNeedCover.ts +++ b/src/engine/core/schemes/cover/evaluators/EvaluatorNeedCover.ts @@ -2,7 +2,7 @@ import { LuabindClass, property_evaluator } from "xray16"; import { ISchemeCoverState } from "@/engine/core/schemes/cover"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isSectionActive } from "@/engine/core/utils/scheme/scheme_logic"; +import { isActiveSectionState } from "@/engine/core/utils/scheme/scheme_logic"; const logger: LuaLogger = new LuaLogger($filename); @@ -22,6 +22,6 @@ export class EvaluatorNeedCover extends property_evaluator { * Check whether cover scheme is active. */ public override evaluate(): boolean { - return isSectionActive(this.object, this.state); + return isActiveSectionState(this.object, this.state); } } diff --git a/src/engine/core/schemes/mob_jump/MobJumpManager.ts b/src/engine/core/schemes/mob_jump/MobJumpManager.ts index 6936a81c9..fe7418418 100644 --- a/src/engine/core/schemes/mob_jump/MobJumpManager.ts +++ b/src/engine/core/schemes/mob_jump/MobJumpManager.ts @@ -3,8 +3,7 @@ import { cond, look, patrol } from "xray16"; import { AbstractSchemeManager } from "@/engine/core/schemes"; import { ISchemeMobJumpState } from "@/engine/core/schemes/mob_jump/ISchemeMobJumpState"; import { abort } from "@/engine/core/utils/assertion"; -import { action } from "@/engine/core/utils/object/object_action"; -import { scriptCaptureMonster, scriptReleaseMonster } from "@/engine/core/utils/scheme"; +import { scriptCaptureMonster, scriptCommandMonster, scriptReleaseMonster } from "@/engine/core/utils/scheme"; import { addVectors } from "@/engine/core/utils/vector"; import { Optional, Patrol, Vector } from "@/engine/lib/types"; @@ -52,7 +51,7 @@ export class MobJumpManager extends AbstractSchemeManager { public update(delta: number): void { if (this.stateCurrent === STATE_START_LOOK) { if (!this.object.action()) { - action(this.object, new look(look.point, this.point!), new cond(cond.look_end)); + scriptCommandMonster(this.object, new look(look.point, this.point!), new cond(cond.look_end)); this.stateCurrent = STATE_WAIT_LOOK_END; } diff --git a/src/engine/core/schemes/mob_remark/MobRemarkManager.ts b/src/engine/core/schemes/mob_remark/MobRemarkManager.ts index c9f2ca6e8..4e7db6354 100644 --- a/src/engine/core/schemes/mob_remark/MobRemarkManager.ts +++ b/src/engine/core/schemes/mob_remark/MobRemarkManager.ts @@ -2,14 +2,12 @@ import { anim, cond, MonsterSpace, sound } from "xray16"; import { registry, setMonsterState } from "@/engine/core/database"; import { NotificationManager } from "@/engine/core/managers/interface/notifications"; -import { AbstractSchemeManager } from "@/engine/core/schemes"; +import { AbstractSchemeManager } from "@/engine/core/schemes/base"; import { ISchemeMobRemarkState } from "@/engine/core/schemes/mob_remark/ISchemeMobRemarkState"; import { abort } from "@/engine/core/utils/assertion"; import { getExtern } from "@/engine/core/utils/binding"; -import { pickSectionFromCondList } from "@/engine/core/utils/ini/ini_config"; -import { parseStringsList } from "@/engine/core/utils/ini/ini_parse"; -import { action } from "@/engine/core/utils/object/object_action"; -import { scriptCaptureMonster } from "@/engine/core/utils/scheme/scheme_monster"; +import { parseStringsList, pickSectionFromCondList } from "@/engine/core/utils/ini"; +import { scriptCaptureMonster, scriptCommandMonster } from "@/engine/core/utils/scheme"; import { AnyCallablesModule, LuaArray, MonsterBodyStateKey, Optional, TName } from "@/engine/lib/types"; /** @@ -79,7 +77,7 @@ export class MobRemarkManager extends AbstractSchemeManager { scriptCaptureMonster(this.monsterObject, true); - action( + scriptCommandMonster( this.monsterObject, new move(move.run_fwd, this.state.path.point(this.state.path.count() - 1)), new cond(cond.move_end) diff --git a/src/engine/core/schemes/walker/evaluators/EvaluatorNeedWalker.ts b/src/engine/core/schemes/walker/evaluators/EvaluatorNeedWalker.ts index 985f86e93..752bfed83 100644 --- a/src/engine/core/schemes/walker/evaluators/EvaluatorNeedWalker.ts +++ b/src/engine/core/schemes/walker/evaluators/EvaluatorNeedWalker.ts @@ -2,7 +2,7 @@ import { LuabindClass, property_evaluator } from "xray16"; import { ISchemeWalkerState } from "@/engine/core/schemes/walker"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { isSectionActive } from "@/engine/core/utils/scheme/scheme_logic"; +import { isActiveSectionState } from "@/engine/core/utils/scheme/scheme_logic"; const logger: LuaLogger = new LuaLogger($filename); @@ -22,6 +22,6 @@ export class EvaluatorNeedWalker extends property_evaluator { * Check whether walker scheme is active and should still continue processing. */ public override evaluate(): boolean { - return isSectionActive(this.object, this.state); + return isActiveSectionState(this.object, this.state); } } diff --git a/src/engine/core/schemes/wounded/SchemeWounded.ts b/src/engine/core/schemes/wounded/SchemeWounded.ts index 10c3d6ce7..13a51b644 100644 --- a/src/engine/core/schemes/wounded/SchemeWounded.ts +++ b/src/engine/core/schemes/wounded/SchemeWounded.ts @@ -8,7 +8,7 @@ import { ISchemeWoundedState } from "@/engine/core/schemes/wounded/ISchemeWounde import { WoundManager } from "@/engine/core/schemes/wounded/WoundManager"; import { IConfigSwitchCondition, parseConditionsList, readIniBoolean, readIniString } from "@/engine/core/utils/ini"; import { LuaLogger } from "@/engine/core/utils/logging"; -import { getCharacterCommunity } from "@/engine/core/utils/object/object_get"; +import { getObjectCommunity } from "@/engine/core/utils/object/object_get"; import { communities, TCommunity } from "@/engine/lib/constants/communities"; import { NIL } from "@/engine/lib/constants/words"; import { @@ -116,7 +116,7 @@ export class SchemeWounded extends AbstractScheme { state.wounded_section = tostring(section); - const objectCommunity: TCommunity = getCharacterCommunity(object); + const objectCommunity: TCommunity = getObjectCommunity(object); const defaults: AnyObject = {}; // Initialize defaults: diff --git a/src/engine/core/ui/menu/load/LoadDialog.ts b/src/engine/core/ui/menu/load/LoadDialog.ts index 6fded7371..1c1a42ea5 100644 --- a/src/engine/core/ui/menu/load/LoadDialog.ts +++ b/src/engine/core/ui/menu/load/LoadDialog.ts @@ -24,7 +24,7 @@ import { import { registry } from "@/engine/core/database"; import { LoadItem } from "@/engine/core/ui/menu/load/LoadItem"; -import { deleteGameSave, gatFileDataForGameSave, isGameSaveFileExist } from "@/engine/core/utils/game/game_save"; +import { deleteGameSave, getFileDataForGameSave, isGameSaveFileExist } from "@/engine/core/utils/game/game_save"; import { LuaLogger } from "@/engine/core/utils/logging"; import { resolveXmlFormPath } from "@/engine/core/utils/ui"; import { gameConfig } from "@/engine/lib/configs/GameConfig"; @@ -179,7 +179,7 @@ export class LoadDialog extends CUIScriptWnd { this.uiFileCaption.SetText(itemText); this.uiFileCaption.SetEllipsis(true); - this.uiFileData.SetText(gatFileDataForGameSave(itemText)); + this.uiFileData.SetText(getFileDataForGameSave(itemText)); if (!isGameSaveFileExist(itemText + gameConfig.GAME_SAVE_EXTENSION)) { this.uiListBox.RemoveItem(item); diff --git a/src/engine/core/utils/game/game_check.test.ts b/src/engine/core/utils/game/game_check.test.ts index 882a6b998..3e0f9d78b 100644 --- a/src/engine/core/utils/game/game_check.test.ts +++ b/src/engine/core/utils/game/game_check.test.ts @@ -1,16 +1,55 @@ import { describe, expect, it } from "@jest/globals"; import { alife } from "xray16"; -import { isGameStarted } from "@/engine/core/utils/game/game_check"; +import { isBlackScreen, isGameLevelChanging, isGameStarted } from "@/engine/core/utils/game/game_check"; import { replaceFunctionMock } from "@/fixtures/utils"; -import { mockAlifeSimulator } from "@/fixtures/xray"; +import { MockAlifeSimulator, MockDevice, mockServerAlifeCreatureActor } from "@/fixtures/xray"; +import { MockCGameGraph } from "@/fixtures/xray/mocks/CGameGraph.mock"; describe("game_check utils", () => { it("'isGameStarted' should check alife", () => { replaceFunctionMock(alife, () => null); expect(isGameStarted()).toBe(false); - replaceFunctionMock(alife, mockAlifeSimulator); + replaceFunctionMock(alife, MockAlifeSimulator.mock); expect(isGameStarted()).toBe(true); }); + + it("'isBlackScreen' should check whether black screen is visible now", () => { + expect(isBlackScreen()).toBe(false); + + const device: MockDevice = MockDevice.getInstance(); + + device.precache_frame = 10; + expect(isBlackScreen()).toBe(true); + device.precache_frame = 1; + expect(isBlackScreen()).toBe(false); + device.precache_frame = 1000; + expect(isBlackScreen()).toBe(true); + device.precache_frame = 0; + expect(isBlackScreen()).toBe(false); + }); + + it("'isGameLevelChanging' should check whether level is changing now", () => { + const gameGraph: MockCGameGraph = MockCGameGraph.getInstance(); + + replaceFunctionMock(alife, () => null); + + mockServerAlifeCreatureActor({ m_game_vertex_id: 3 }); + expect(isGameLevelChanging()).toBe(false); + + replaceFunctionMock(alife, MockAlifeSimulator.mock); + + gameGraph.vertex(10).level_id.mockImplementation(() => 5); + mockServerAlifeCreatureActor({ m_game_vertex_id: 10 }); + expect(isGameLevelChanging()).toBe(true); + + gameGraph.vertex(15).level_id.mockImplementation(() => 3); + mockServerAlifeCreatureActor({ m_game_vertex_id: 15 }); + expect(isGameLevelChanging()).toBe(false); + + gameGraph.vertex(20).level_id.mockImplementation(() => 10); + mockServerAlifeCreatureActor({ m_game_vertex_id: 20 }); + expect(isGameLevelChanging()).toBe(true); + }); }); diff --git a/src/engine/core/utils/game/game_check.ts b/src/engine/core/utils/game/game_check.ts index 2ebe510b4..62925d9a3 100644 --- a/src/engine/core/utils/game/game_check.ts +++ b/src/engine/core/utils/game/game_check.ts @@ -1,4 +1,4 @@ -import { alife, device, game_graph, IsGameTypeSingle } from "xray16"; +import { alife, device, game_graph } from "xray16"; import { AlifeSimulator, Optional } from "@/engine/lib/types"; @@ -11,19 +11,6 @@ export function isGameStarted(): boolean { return alife() !== null; } -/** - * todo - */ -export function isSinglePlayerGame(): boolean { - if (alife === null || alife() !== null) { - return true; - } else if (IsGameTypeSingle === null || IsGameTypeSingle()) { - return true; - } - - return false; -} - /** * @returns whether currently black screen is visible and rendering is paused. */ diff --git a/src/engine/core/utils/game/game_console.ts b/src/engine/core/utils/game/game_console.ts index a67b3f5b6..65449c825 100644 --- a/src/engine/core/utils/game/game_console.ts +++ b/src/engine/core/utils/game/game_console.ts @@ -5,6 +5,9 @@ import { AnyArgs } from "@/engine/lib/types"; /** * Execute console command and concatenate provided parameters for propagation. + * + * @param command - console command + * @param args - list of arguments to provide for command */ export function executeConsoleCommand(command: TConsoleCommand, ...args: AnyArgs): void { if (args.length > 0) { @@ -16,6 +19,10 @@ export function executeConsoleCommand(command: TConsoleCommand, ...args: AnyArgs /** * Execute command to get floating point number value. + * + * @param command - console command + * @param args - list of arguments to provide for command + * @returns float value from console */ export function getConsoleFloatCommand(command: TConsoleCommand, ...args: AnyArgs): T { if (args.length > 0) { diff --git a/src/engine/core/utils/game/game_save.test.ts b/src/engine/core/utils/game/game_save.test.ts index 69cee5465..ff54325ed 100644 --- a/src/engine/core/utils/game/game_save.test.ts +++ b/src/engine/core/utils/game/game_save.test.ts @@ -1,9 +1,14 @@ import { beforeEach, describe, expect, it } from "@jest/globals"; -import { createAutoSave, createSave } from "@/engine/core/utils/game/game_save"; -import { resetFunctionMock } from "@/fixtures/utils/function_mock"; -import { gameConsole } from "@/fixtures/xray/mocks/console.mock"; -import { mocksConfig } from "@/fixtures/xray/mocks/MocksConfig"; +import { + createAutoSave, + createSave, + deleteGameSave, + getFileDataForGameSave, + isGameSaveFileExist, +} from "@/engine/core/utils/game/game_save"; +import { resetFunctionMock } from "@/fixtures/utils"; +import { gameConsole, MockFileSystem, MockFileSystemList, mocksConfig } from "@/fixtures/xray"; describe("'game_save' utils", () => { beforeEach(() => { @@ -11,6 +16,41 @@ describe("'game_save' utils", () => { resetFunctionMock(gameConsole.get_float); }); + it("'getFileDataForGameSave' should correctly get save data", () => { + expect(getFileDataForGameSave("test")).toBe("no file data"); + + MockFileSystem.getInstance().file_list_open_ex.mockImplementation(() => new MockFileSystemList(["a"])); + + expect(getFileDataForGameSave("test")).toBe( + "translated_st_level: translated_pripyat\\ntranslated_ui_inv_time: 09:30 06/12/2012\\n" + + "translated_st_ui_health_sensor %d100" + ); + }); + + it("'isGameSaveFileExist' should correctly check if save file exists", () => { + const fileSystem: MockFileSystem = MockFileSystem.getInstance(); + + fileSystem.file_list_open_ex.mockImplementation(() => new MockFileSystemList()); + expect(isGameSaveFileExist("test")).toBe(false); + + fileSystem.file_list_open_ex.mockImplementation(() => new MockFileSystemList(["a"])); + expect(isGameSaveFileExist("test")).toBe(true); + }); + + it("'deleteGameSave' should correctly delete file exists", () => { + const fileSystem: MockFileSystem = MockFileSystem.getInstance(); + + fileSystem.file_list_open_ex.mockImplementation(() => new MockFileSystemList()); + deleteGameSave("todelete"); + expect(fileSystem.file_delete).toHaveBeenCalledWith("$game_saves$", "todelete.scop"); + + fileSystem.file_delete.mockReset(); + MockFileSystem.getInstance().file_list_open_ex.mockImplementation(() => new MockFileSystemList(["a"])); + deleteGameSave("another"); + expect(fileSystem.file_delete).toHaveBeenNthCalledWith(1, "$game_saves$", "another.scop"); + expect(fileSystem.file_delete).toHaveBeenNthCalledWith(2, "$game_saves$", "another.dds"); + }); + it("'createSave' should correctly generate commands", () => { createSave("test"); expect(gameConsole.execute).toHaveBeenCalledWith("save os_user_name - translated_test"); diff --git a/src/engine/core/utils/game/game_save.ts b/src/engine/core/utils/game/game_save.ts index 90303593a..5c0a2642c 100644 --- a/src/engine/core/utils/game/game_save.ts +++ b/src/engine/core/utils/game/game_save.ts @@ -13,7 +13,10 @@ import { FSFileListEX, Optional, SavedGameWrapper, TCount, TLabel, TName } from const logger: LuaLogger = new LuaLogger($filename); /** - * todo + * Check whether save file exists with provided name. + * + * @param filename - target name to search in saves folder + * @returns whether file exists */ export function isGameSaveFileExist(filename: TName): boolean { const filesList: FSFileListEX = getFS().file_list_open_ex( @@ -26,7 +29,9 @@ export function isGameSaveFileExist(filename: TName): boolean { } /** - * todo + * Delete game save file and dds file. + * + * @param filename - target name to delete from saves folder */ export function deleteGameSave(filename: TName): void { const saveFileName: TName = filename + gameConfig.GAME_SAVE_EXTENSION; @@ -42,9 +47,12 @@ export function deleteGameSave(filename: TName): void { } /** - * todo + * Get label with description for file name. + * + * @param filename - name of save file to check + * @returns label with save file description */ -export function gatFileDataForGameSave(filename: TName): TLabel { +export function getFileDataForGameSave(filename: TName): TLabel { const fileList: FSFileListEX = getFS().file_list_open_ex( roots.gameSaves, bit_or(FS.FS_ListFiles, FS.FS_RootOnly), diff --git a/src/engine/core/utils/game/game_time.test.ts b/src/engine/core/utils/game/game_time.test.ts index eae572e41..a57d28698 100644 --- a/src/engine/core/utils/game/game_time.test.ts +++ b/src/engine/core/utils/game/game_time.test.ts @@ -1,10 +1,18 @@ import { describe, expect, it } from "@jest/globals"; import { game, level } from "xray16"; -import { isInTimeInterval, readTimeFromPacket, writeTimeToPacket } from "@/engine/core/utils/game/game_time"; +import { + gameTimeToString, + globalTimeToString, + isInTimeInterval, + readTimeFromPacket, + toTimeDigit, + writeTimeToPacket, +} from "@/engine/core/utils/game/game_time"; import { MAX_I32, MAX_U8, MIN_I32 } from "@/engine/lib/constants/memory"; import { Optional, Time } from "@/engine/lib/types"; import { replaceFunctionMock } from "@/fixtures/utils/function_mock"; +import { MockCTime } from "@/fixtures/xray/mocks/CTime.mock"; import { EPacketDataType, mockNetPacket, mockNetProcessor, MockNetProcessor } from "@/fixtures/xray/mocks/save"; describe("'time' utils", () => { @@ -52,6 +60,25 @@ describe("'time' utils", () => { expect(netProcessor.readDataOrder).toEqual(netProcessor.writeDataOrder); }); + it("'toTimeDigit' should correctly convert time digits", () => { + expect(toTimeDigit(0)).toBe("00"); + expect(toTimeDigit(1)).toBe("01"); + expect(toTimeDigit(9)).toBe("09"); + expect(toTimeDigit(10)).toBe("10"); + expect(toTimeDigit(16)).toBe("16"); + expect(toTimeDigit(20)).toBe("20"); + }); + + it("'gameTimeToString' should correctly stringify game time", () => { + expect(gameTimeToString(MockCTime.mock(2015, 2, 15, 12, 45, 30, 250))).toBe("12:45 02/15/2015"); + expect(gameTimeToString(MockCTime.mock(2004, 11, 1, 4, 5, 2, 20))).toBe("04:05 11/01/2004"); + }); + + it("'globalTimeToString' should correctly stringify global time", () => { + expect(globalTimeToString(3_600_000 * 3 + 4 * 60_000 + 5 * 1000)).toBe("3:04:05"); + expect(globalTimeToString(3_600_000 * 11 + 25 * 60_000 + 16 * 1000)).toBe("11:25:16"); + }); + it("'isInTimeInterval' should correctly check time intervals", () => { replaceFunctionMock(level.get_time_hours, () => 12); diff --git a/src/engine/core/utils/game/game_time.ts b/src/engine/core/utils/game/game_time.ts index d0e40c04e..4e54e6855 100644 --- a/src/engine/core/utils/game/game_time.ts +++ b/src/engine/core/utils/game/game_time.ts @@ -1,37 +1,37 @@ -import { game, level, time_global, verify_if_thread_is_running } from "xray16"; +import { game, level } from "xray16"; +import { wait } from "@/engine/core/utils/game/game_wait"; import { LuaLogger } from "@/engine/core/utils/logging"; import { MAX_U8 } from "@/engine/lib/constants/memory"; -import { NetPacket, NetProcessor, Optional, TDuration, Time, TLabel, TRate, TTimestamp } from "@/engine/lib/types"; +import { NetPacket, NetProcessor, Optional, Time, TRate, TTimestamp } from "@/engine/lib/types"; const logger: LuaLogger = new LuaLogger($filename); /** - * todo + * Add part of time digit to a data string. + * + * @param digit - number convert + * @returns concatenated string */ -function addTimeDigit(data: string, digit: number): string { - return digit > 9 ? data + digit : data + "0" + digit; +export function toTimeDigit(digit: number): string { + return digit > 9 ? tostring(digit) : "0" + digit; } /** - * todo; + * @param time - time to stringify + * @returns stringified time string */ export function gameTimeToString(time: Time): string { const [y, m, d, h, min] = time.get(0, 0, 0, 0, 0, 0, 0); - let dateTime: TLabel = ""; - - dateTime = addTimeDigit(dateTime, h); - dateTime = dateTime + ":"; - dateTime = addTimeDigit(dateTime, min); - dateTime = dateTime + " "; - dateTime = addTimeDigit(dateTime, m); - dateTime = dateTime + "/"; - dateTime = addTimeDigit(dateTime, d); - dateTime = dateTime + "/"; - dateTime = dateTime + y; - - return dateTime; + return string.format( + "%s:%s %s/%s/%s", + toTimeDigit(h), + toTimeDigit(min), + toTimeDigit(m), + toTimeDigit(d), + toTimeDigit(y) + ); } /** @@ -39,67 +39,67 @@ export function gameTimeToString(time: Time): string { * @returns hh:mm:ss formatted time */ export function globalTimeToString(time: number): string { - const hours: number = math.floor(time / 3600000); - const minutes: number = math.floor(time / 60000 - hours * 60); - const seconds: number = math.floor(time / 1000 - hours * 3600 - minutes * 60); + const hours: number = math.floor(time / 3_600_000); + const minutes: number = math.floor(time / 60_000 - hours * 60); + const seconds: number = math.floor(time / 1_000 - hours * 3_600 - minutes * 60); - return string.format( - "%s:%s:%s", - tostring(hours), - (minutes >= 10 ? "" : "0") + tostring(minutes), - (seconds >= 10 ? "" : "0") + tostring(seconds) - ); + return string.format("%s:%s:%s", tostring(hours), toTimeDigit(minutes), toTimeDigit(seconds)); } /** * Check whether current time interval is between desired values. + * + * @param fromHours - lower time bound + * @param toHours - upper time bound + * @returns whether current game time is in provided time bounds */ -export function isInTimeInterval(fromHours: TTimestamp, toHouds: TTimestamp): boolean { +export function isInTimeInterval(fromHours: TTimestamp, toHours: TTimestamp): boolean { const gameHours: TTimestamp = level.get_time_hours(); - if (fromHours >= toHouds) { - return gameHours < toHouds || gameHours >= fromHours; + if (fromHours >= toHours) { + return gameHours < toHours || gameHours >= fromHours; } else { - return gameHours < toHouds && gameHours >= fromHours; + return gameHours < toHours && gameHours >= fromHours; } } /** - * Lock scripts execution based on game time. + * Set current time in level. + * Creates idle state with multiplied time factor. + * + * @param hour - desired day hour + * @param min - desired day min + * @param sec - desired day sec */ -export function waitGame(timeToWait: Optional = null): void { - verify_if_thread_is_running(); +export function setCurrentTime(hour: number, min: number, sec: number): void { + const currentTimeFactor: TRate = level.get_time_factor(); + const currentGameTime: TTimestamp = game.time(); - if (timeToWait === null) { - coroutine.yield(); - } else { - const timeToStop: TTimestamp = game.time() + timeToWait; + let currentDay: number = math.floor(currentGameTime / 86_400_000); + const currentTime: number = currentGameTime - currentDay * 86_400_000; + let newTime: number = (sec + min * 60 + hour * 3_600) * 1000; - while (game.time() <= timeToStop) { - coroutine.yield(); - } + // Wait for next day to match expected time. + if (currentTime > newTime) { + currentDay += 1; } -} -/** - * Lock scripts execution based on real time. - */ -export function wait(timeToWait: Optional = null): void { - verify_if_thread_is_running(); + newTime = newTime + currentDay * 86_400_000; - if (timeToWait === null) { - coroutine.yield(); - } else { - const timeToStop: TTimestamp = time_global() + timeToWait; + level.set_time_factor(10_000); - while (time_global() <= timeToStop) { - coroutine.yield(); - } + while (game.time() < newTime) { + wait(); } + + level.set_time_factor(currentTimeFactor); } /** - * todo; + * Save time object into net packet. + * + * @param packet - target packet to write data + * @param time - time object to write */ export function writeTimeToPacket(packet: NetPacket, time: Optional