Skip to content

Commit

Permalink
Separate file for scheme logics init. Tests for `initializeObjectSche…
Browse files Browse the repository at this point in the history
…meLogic`, `initializeObjectSectionItems`. Rename state logics fields to camelcase. Use separate state interface to describe logics. More todos/comments.
  • Loading branch information
Neloreck committed Jun 19, 2023
1 parent 9d6ca46 commit e550e03
Show file tree
Hide file tree
Showing 33 changed files with 532 additions and 334 deletions.
5 changes: 1 addition & 4 deletions doc/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
## 🧰 Main todos

- Create xrf.ltx config and place extended game configs in it
- Scripts to unpack raw_gamedata for observation / usage
- Rework acdc perl script and add all.spawn editing utils
- Interop with level editor tools etc
- Add linux/windows custom CLI script
- Update statistics measurements to be more generic and less hardcoded

## 🧰 Tech

Expand Down
5 changes: 5 additions & 0 deletions src/engine/configs/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## todo: scripts logic ini fields descriptions

## Logic fields

`spawn` - name of section describing items to spawn on section activation
8 changes: 7 additions & 1 deletion src/engine/core/database/ini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import { ClientObject, IniFile, Optional, TName } from "@/engine/lib/types";

/**
* Create dynamic ini file representation or get existing one from cache.
* Used to create in-memory ini files based on string content.
* Usually describes smart terrain job logic for an object based on assigned smart terrain.
*
* @param name - dynamic ini file name
* @param content - dynamic ini file content to initialize, if it does not exist
* @returns multi return of file and filename
*/
export function loadDynamicIni(name: TName, content: Optional<string> = null): LuaMultiReturn<[IniFile, TName]> {
const nameKey: TName = DYNAMIC_LTX_PREFIX + name;
Expand All @@ -30,7 +33,10 @@ export function loadDynamicIni(name: TName, content: Optional<string> = null): L
}

/**
* Get ini file based on active object logic.
* Get ini file describing object script logic.
* In case of `customdata` get spawn ini.
* In case of dynamic LTX load it according to object job descriptor or from database.
* As fallback just load LTX file from configs by name.
*
* @param object - game object to get matching ini config
* @param filename - ini file name
Expand Down
14 changes: 7 additions & 7 deletions src/engine/core/database/logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ describe("'logic' database module", () => {
} as IBaseSchemeState;

state.job_ini = "test.ltx";
state.ini_filename = "test2.ltx";
state.section_logic = "section_ex";
state.iniFilename = "test2.ltx";
state.sectionLogic = "section_ex";
state.activeSection = "active_sect_ex";
state.gulag_name = "gulag_name_ex";
state.smartTerrainName = "gulag_name_ex";
state.activeScheme = EScheme.COMBAT;
state.activationTime = 15_000;
state.activationGameTime = time;
Expand Down Expand Up @@ -124,10 +124,10 @@ describe("'logic' database module", () => {
]);

expect(nextState.job_ini).toBe("test.ltx");
expect(nextState.loaded_ini_filename).toBe("test2.ltx");
expect(nextState.loaded_section_logic).toBe("section_ex");
expect(nextState.loaded_active_section).toBe("active_sect_ex");
expect(nextState.loaded_gulag_name).toBe("gulag_name_ex");
expect(nextState.loadedIniFilename).toBe("test2.ltx");
expect(nextState.loadedSectionLogic).toBe("section_ex");
expect(nextState.loadedActiveSection).toBe("active_sect_ex");
expect(nextState.loadedSmartTerrainName).toBe("gulag_name_ex");
expect(nextState.activationTime).toBe(15_000);
expect(nextState.activationGameTime.toString()).toBe(time.toString());

Expand Down
14 changes: 7 additions & 7 deletions src/engine/core/database/logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ export function saveObjectLogic(object: ClientObject, packet: NetPacket): void {
openSaveMarker(packet, "object" + object.name());

packet.w_stringZ(state.job_ini ? state.job_ini : "");
packet.w_stringZ(state.ini_filename ? state.ini_filename : "");
packet.w_stringZ(state.section_logic ? state.section_logic : "");
packet.w_stringZ(state.iniFilename ? state.iniFilename : "");
packet.w_stringZ(state.sectionLogic ? state.sectionLogic : "");
packet.w_stringZ(state.activeSection ? state.activeSection : "");
packet.w_stringZ(state.gulag_name ? state.gulag_name : "");
packet.w_stringZ(state.smartTerrainName ? state.smartTerrainName : "");

packet.w_s32((state.activationTime || 0) - time_global());
writeTimeToPacket(packet, state.activationGameTime);
Expand Down Expand Up @@ -73,10 +73,10 @@ export function loadObjectLogic(object: ClientObject, reader: NetProcessor): voi
const gulagName: TName = reader.r_stringZ();

state.job_ini = jobIni === "" ? null : jobIni;
state.loaded_ini_filename = iniFilename === "" ? null : iniFilename;
state.loaded_section_logic = sectionLogic === "" ? null : sectionLogic;
state.loaded_active_section = activeSection === "" ? NIL : activeSection;
state.loaded_gulag_name = gulagName;
state.loadedIniFilename = iniFilename === "" ? null : iniFilename;
state.loadedSectionLogic = sectionLogic === "" ? null : sectionLogic;
state.loadedActiveSection = activeSection === "" ? NIL : activeSection;
state.loadedSmartTerrainName = gulagName;

state.activationTime = reader.r_s32() + time_global();
state.activationGameTime = readTimeFromPacket(reader) as Time;
Expand Down
78 changes: 43 additions & 35 deletions src/engine/core/database/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,17 @@ import {
} from "@/engine/lib/types";

/**
* Client-side registry of game objects logics and states.
* Client objects registry state logics descriptor.
*/
export interface IRegistryObjectState extends Record<EScheme, Optional<IBaseSchemeState>> {
/**
* Client object reference to owner of the registry state.
*/
object: ClientObject;
export interface IRegistryObjectStateLogic {
/**
* todo;
*/
ini: IniFile;
/**
* todo;
*/
ini_filename: Optional<TName>;
/**
* Dynamically stored flags / variables.
*/
portableStore: Optional<LuaTable<TName>>;
iniFilename: Optional<TName>;
/**
* Based on object type, marks compatible scheme types.
*/
Expand All @@ -52,11 +44,13 @@ export interface IRegistryObjectState extends Record<EScheme, Optional<IBaseSche
/**
* todo;
*/
section_logic: Optional<TName>;
sectionLogic: Optional<TName>;
/**
* todo;
* Object smart terrain name.
* Used as base for schemes to pick up logic when smart terrains are capturing objects/squads.
* Having smart terrain allows selecting generic schemes defined for smart terrain.
*/
gulag_name: Optional<TName>;
smartTerrainName: Optional<TName>;
/**
* todo;
*/
Expand All @@ -69,7 +63,40 @@ export interface IRegistryObjectState extends Record<EScheme, Optional<IBaseSche
* Time of logics section activation - in-game time.
*/
activationGameTime: Time;
/**
* todo;
*/
job_ini: Optional<TName>;
/**
* Describes last active logic file name when game was saved.
*/
loadedIniFilename: Optional<TName>;
/**
* Describes last active section logic section when game was saved.
*/
loadedSectionLogic: Optional<TSection>;
/**
* Describes last active logic section when game was saved.
*/
loadedActiveSection: Optional<TSection>;
/**
* Describes last active smart terrain name when game was saved.
*/
loadedSmartTerrainName: Optional<TName>;
}

/**
* Client objects registry state describing logics and state.
*/
export interface IRegistryObjectState extends Record<EScheme, Optional<IBaseSchemeState>>, IRegistryObjectStateLogic {
/**
* Client object reference to owner of the registry state.
*/
object: ClientObject;
/**
* Dynamically stored flags / variables.
*/
portableStore: Optional<LuaTable<TName>>;
/**
* todo;
*/
Expand Down Expand Up @@ -130,30 +157,11 @@ export interface IRegistryObjectState extends Record<EScheme, Optional<IBaseSche
* todo;
*/
registred_camp: Optional<TNumberId>;
/**
* todo;
*/
job_ini: Optional<TName>;
/**
* todo;
*/
loaded_ini_filename: Optional<TName>;
/**
* todo;
*/
loaded_section_logic: Optional<TSection>;
/**
* todo;
*/
loaded_active_section: Optional<TSection>;
/**
* todo;
*/
loaded_gulag_name: Optional<TName>;
}

/**
* todo;
* Offline object state descriptor.
* Remember object active section when object switched offline.
*/
export interface IStoredOfflineObject {
levelVertexId: Optional<TNumberId>;
Expand Down
6 changes: 3 additions & 3 deletions src/engine/core/managers/debug/DebugManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,12 @@ export class DebugManager extends AbstractCoreManager {
const state: IRegistryObjectState = registry.objects.get(object.id());

logger.info("Object section:", object.section());
logger.info("Ini file name:", state.ini_filename);
logger.info("Section logic:", state.section_logic);
logger.info("Ini file name:", state.iniFilename);
logger.info("Section logic:", state.sectionLogic);
logger.info("Scheme type:", ESchemeType[state.schemeType]);
logger.info("Active scheme:", state.activeScheme);
logger.info("Active section:", state.activeSection);
logger.info("Active gulag name:", state.gulag_name);
logger.info("Smart terrain name:", state.smartTerrainName);
logger.info("Activation time:", state.activationTime);
logger.info("Activation game time:", gameTimeToString(state.activationGameTime));
logger.info("Portable store:", toJSON(state.portableStore));
Expand Down
22 changes: 16 additions & 6 deletions src/engine/core/managers/interaction/TradeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ini_file, time_global } from "xray16";
import { closeLoadMarker, closeSaveMarker, openSaveMarker, registry } from "@/engine/core/database";
import { openLoadMarker } from "@/engine/core/database/save_markers";
import { AbstractCoreManager } from "@/engine/core/managers/base/AbstractCoreManager";
import { abort } from "@/engine/core/utils/assertion";
import { abort, assert } from "@/engine/core/utils/assertion";
import { pickSectionFromCondList } from "@/engine/core/utils/ini/config";
import { parseConditionsList } from "@/engine/core/utils/ini/parse";
import { readIniNumber, readIniString } from "@/engine/core/utils/ini/read";
Expand Down Expand Up @@ -41,16 +41,25 @@ export interface ITradeManagerDescriptor {

/**
* todo;
* todo: Move periods to logicsConfig.TRADE
*/
export class TradeManager extends AbstractCoreManager {
public static readonly TRADE_UPDATE_PERIOD: TDuration = 3_600_000;
public static readonly TRADE_RESUPPLY_PERIOD: TDuration = 24 * 3_600_000;

/**
* todo;
*/
public static initializeForObject(object: ClientObject, iniFilePath: TPath): void {
TradeManager.getInstance().initializeForObject(object, iniFilePath);
}

/**
* todo
* todo: Do not reuse data variable.
*/
public initForObject(object: ClientObject, configFilePath: TPath): void {
logger.info("Init trade for:", object.name(), configFilePath);
public initializeForObject(object: ClientObject, configFilePath: TPath): void {
logger.info("Initialize trade for:", object.name(), configFilePath);

const objectId: TNumberId = object.id();

Expand All @@ -60,9 +69,7 @@ export class TradeManager extends AbstractCoreManager {

let data = readIniString(registry.trade.get(objectId).config, "trader", "buy_condition", true, "");

if (data === null) {
abort("Incorrect trader settings. Cannot find buy_condition. [%s]->[%s]", object.name(), configFilePath);
}
assert(data, "Incorrect trader settings. Cannot find buy_condition. [%s]->[%s].", object.name(), configFilePath);

registry.trade.get(objectId).buy_condition = parseConditionsList(data);

Expand Down Expand Up @@ -155,6 +162,9 @@ export class TradeManager extends AbstractCoreManager {
}
}

/**
* todo: Description.
*/
public getBuyDiscountForObject(objectId: TNumberId): number {
const tradeDescriptor: ITradeManagerDescriptor = registry.trade.get(objectId);
const data: string = readIniString(tradeDescriptor.config, "trader", "discounts", false, "", "");
Expand Down
6 changes: 3 additions & 3 deletions src/engine/core/managers/interface/MapDisplayManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class MapDisplayManager extends AbstractCoreManager {
let spotSection;

if (scheme === null || scheme === NIL) {
spotSection = readIniString(state.ini, state.section_logic, "show_spot", false, "");
spotSection = readIniString(state.ini, state.sectionLogic, "show_spot", false, "");
} else {
spotSection = readIniString(state.ini, section, "show_spot", false, "");
}
Expand All @@ -94,7 +94,7 @@ export class MapDisplayManager extends AbstractCoreManager {
const actor: ClientObject = registry.actor;
let mapSpot: Optional<EMapMarkType> = readIniString(
state.ini,
state.section_logic,
state.sectionLogic,
"level_spot",
false,
""
Expand Down Expand Up @@ -152,7 +152,7 @@ export class MapDisplayManager extends AbstractCoreManager {
const objectId: Maybe<TNumberId> = simulator.object(object.id())?.id;
let mapSpot: Optional<EMapMarkType> = readIniString<EMapMarkType>(
state.ini,
state.section_logic,
state.sectionLogic,
"level_spot",
false,
""
Expand Down
2 changes: 1 addition & 1 deletion src/engine/core/managers/world/DropManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export class DropManager extends AbstractCoreManager {

const state: Optional<IRegistryObjectState> = registry.objects.get(object.id());

if (state.ini?.line_exist(state.section_logic, DropManager.DONT_SPAWN_LOOT_LTX_SECTION)) {
if (state.ini?.line_exist(state.sectionLogic, DropManager.DONT_SPAWN_LOOT_LTX_SECTION)) {
return;
}

Expand Down
2 changes: 1 addition & 1 deletion src/engine/core/managers/world/ReleaseBodyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export class ReleaseBodyManager extends AbstractCoreManager {

const state: IRegistryObjectState = registry.objects.get(object.id());
const knownInfo: TSection =
readIniString(characterIni, state.section_logic, "known_info", false, "", null) || "known_info";
readIniString(characterIni, state.sectionLogic, "known_info", false, "", null) || "known_info";

return characterIni.section_exist(knownInfo);
}
Expand Down
4 changes: 2 additions & 2 deletions src/engine/core/objects/binders/HelicopterBinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { HeliCombat } from "@/engine/core/schemes/heli_move/HeliCombat";
import { getHeliFirer, HeliFire } from "@/engine/core/schemes/heli_move/HeliFire";
import { readIniNumber, readIniString } from "@/engine/core/utils/ini/read";
import { LuaLogger } from "@/engine/core/utils/logging";
import { emitSchemeEvent, initializeObjectSchemeLogic } from "@/engine/core/utils/scheme/logic";
import { emitSchemeEvent, initializeObjectSchemeLogic } from "@/engine/core/utils/scheme";
import {
ClientObject,
ESchemeType,
Expand Down Expand Up @@ -97,7 +97,7 @@ export class HelicopterBinder extends object_binder {

if (!this.initialized && actor) {
this.initialized = true;
initializeObjectSchemeLogic(this.object, this.state, this.loaded, actor, ESchemeType.HELI);
initializeObjectSchemeLogic(this.object, this.state, this.loaded, ESchemeType.HELI);
}

if (this.state.activeSection !== null) {
Expand Down
2 changes: 1 addition & 1 deletion src/engine/core/objects/binders/creature/StalkerBinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ export class StalkerBinder extends object_binder {
statisticsManager.updateBestMonsterKilled(npc);
}

const knownInfo: Optional<TName> = readIniString(state.ini!, state.section_logic, "known_info", false, "", null);
const knownInfo: Optional<TName> = readIniString(state.ini!, state.sectionLogic, "known_info", false, "", null);

this.initializeInfoPortions(state.ini!, knownInfo);

Expand Down
4 changes: 2 additions & 2 deletions src/engine/core/objects/binders/physic/PhysicObjectBinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { ESchemeEvent } from "@/engine/core/schemes";
import { pickSectionFromCondList } from "@/engine/core/utils/ini/config";
import { TConditionList } from "@/engine/core/utils/ini/types";
import { LuaLogger } from "@/engine/core/utils/logging";
import { emitSchemeEvent, initializeObjectSchemeLogic } from "@/engine/core/utils/scheme/logic";
import { emitSchemeEvent, initializeObjectSchemeLogic } from "@/engine/core/utils/scheme";
import {
ClientObject,
EScheme,
Expand Down Expand Up @@ -142,7 +142,7 @@ export class PhysicObjectBinder extends object_binder {

if (!this.initialized) {
this.initialized = true;
initializeObjectSchemeLogic(this.object, this.state, this.loaded, registry.actor, ESchemeType.ITEM);
initializeObjectSchemeLogic(this.object, this.state, this.loaded, ESchemeType.ITEM);
}

const spawnIni: Optional<IniFile> = this.object.spawn_ini();
Expand Down
Loading

0 comments on commit e550e03

Please sign in to comment.