Skip to content

Commit

Permalink
Updated wounded schema logics / docs. Separation of wounded utils.
Browse files Browse the repository at this point in the history
  • Loading branch information
Neloreck committed Aug 19, 2023
1 parent 73d4d50 commit c9dd44f
Show file tree
Hide file tree
Showing 24 changed files with 217 additions and 201 deletions.
4 changes: 2 additions & 2 deletions src/engine/configs/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
- `combat_ignore_keep_when_attacked` - ???
- `meet` - ???
- `invulnerable` - ???
- `gather_items_enabled` - ???
- `help_wounded_enabled` - ???
- `gather_items_enabled` - whether stalker can loot items from corpses nearby if any detected, true by default
- `help_wounded_enabled` - whether stalker can help wounded if injured nearby are detected, true by default
- `corpse_detection_enabled` - ???
- `use_camp` - [boolean] whether object can use camp logic (stories, guitar, harmonica), true by default
2 changes: 1 addition & 1 deletion src/engine/core/database/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export interface IRegistryObjectState extends Record<EScheme, Optional<IBaseSche
* ID of object currently looting object.
* Used to prevent looting of same object by multiple objects at once.
*
* todo: Move to loot scheme state, not store it in global.
* todo: Move to loot scheme state, not store it in global. Probaly pstore is correct place.
*/
lootedByObject: Optional<TNumberId>;
/**
Expand Down
5 changes: 3 additions & 2 deletions src/engine/core/objects/animation/animations/base.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IAnimationDescriptor } from "@/engine/core/objects/animation/animation_types";
import { EStalkerState } from "@/engine/core/objects/animation/state_types";
import { finishCorpseLooting } from "@/engine/core/schemes/corpse_detection/utils";
import { SchemeHelpWounded } from "@/engine/core/schemes/help_wounded";
import { finishHelpWounded } from "@/engine/core/schemes/help_wounded/utils";
import { createSequence } from "@/engine/core/utils/animation";
import { getExtern } from "@/engine/core/utils/binding";
import { startPlayingGuitar, startPlayingHarmonica } from "@/engine/core/utils/camp";
Expand Down Expand Up @@ -1015,8 +1015,9 @@ export const baseAnimations: LuaTable<TName, IAnimationDescriptor> = $fromObject
into: createSequence([
"dinamit_1",
{
// When animation ends, finish help wounded and heal up.
f: (object: ClientObject) => {
SchemeHelpWounded.helpWounded(object);
finishHelpWounded(object);
},
},
]),
Expand Down
4 changes: 2 additions & 2 deletions src/engine/core/objects/binders/creature/StalkerBinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import { SchemeHear } from "@/engine/core/schemes/hear/SchemeHear";
import { SchemeMeet } from "@/engine/core/schemes/meet/SchemeMeet";
import { SchemeReachTask } from "@/engine/core/schemes/reach_task/SchemeReachTask";
import { SchemeLight } from "@/engine/core/schemes/sr_light/SchemeLight";
import { SchemeWounded } from "@/engine/core/schemes/wounded/SchemeWounded";
import { ISchemeWoundedState } from "@/engine/core/schemes/wounded";
import { pickSectionFromCondList, readIniString, TConditionList } from "@/engine/core/utils/ini";
import { IObjectJobDescriptor } from "@/engine/core/utils/job";
import { LuaLogger } from "@/engine/core/utils/logging";
Expand Down Expand Up @@ -555,7 +555,7 @@ export class StalkerBinder extends object_binder {
}

if (amount > 0) {
SchemeWounded.onHit(object.id());
(this.state[EScheme.WOUNDED] as ISchemeWoundedState)?.woundManager.onHit();
}

EventsManager.emitEvent(EGameEvent.STALKER_HIT, this.object, amount, direction, who, boneIndex);
Expand Down
2 changes: 2 additions & 0 deletions src/engine/core/schemes/base/id/evaluator_ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ const BASE: TNumberId = stalker_ids.property_script || 74;
* todo;
*/
export enum EEvaluatorId {
// Whether any corpse to loot exists nearby.
IS_CORPSE_EXISTING = BASE + 50, // 124
// Whether any wounded stalker to help exists nearby.
IS_WOUNDED_EXISTING = BASE + 55, // 129
IS_STATE_IDLE_COMBAT = BASE + 101, // 175
IS_STATE_IDLE_ALIFE = BASE + 102, // 176
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { Optional, TNumberId, Vector } from "@/engine/lib/types";
* State of corpse looting scheme.
*/
export interface ISchemeCorpseDetectionState extends IBaseSchemeState {
// Whether object can detect and search nearby corpses for loot.
isCorpseDetectionEnabled: Optional<boolean>;
// Selected corpse vertex id to loot.
selectedCorpseVertexId: TNumberId;
// Selected corpse vertex position to loot.
selectedCorpseVertexPosition: Optional<Vector>;
// Selected corpse ID to loot.
selectedCorpseId: Optional<TNumberId>;
// Whether object can detect and search nearby corpses for loot.
isCorpseDetectionEnabled: Optional<boolean>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class ActionSearchCorpse extends action_base {
super.execute();

// Start playing looting animation when actually reach destination point.
if (this.object.position().distance_to_sqr(this.state.selectedCorpseVertexPosition as Vector) < 2) {
if (this.object.position().distance_to_sqr(this.state.selectedCorpseVertexPosition as Vector) <= 2) {
setStalkerState(this.object, EStalkerState.SEARCH_CORPSE, null, null, {
lookPosition: this.state.selectedCorpseVertexPosition,
lookObject: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ export class EvaluatorCorpseDetect extends property_evaluator {
if (
// Is registered in client side.
corpseObject &&
// Is visible so can be looted.
this.object.see(corpseObject) &&
// Is not looted by anyone or looted by current object.
(registryState.lootedByObject === null || registryState.lootedByObject === this.object.id())
(registryState.lootedByObject === null || registryState.lootedByObject === this.object.id()) &&
// Is visible so can be looted.
this.object.see(corpseObject)
) {
if (
// Is near enough.
Expand Down
14 changes: 9 additions & 5 deletions src/engine/core/schemes/help_wounded/ISchemeHelpWoundedState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ import { IBaseSchemeState } from "@/engine/core/schemes/base";
import { TNumberId, Vector } from "@/engine/lib/types";

/**
* todo;
* State of helping wounded scheme.
*/
export interface ISchemeHelpWoundedState extends IBaseSchemeState {
help_wounded_enabled: boolean;
vertex_id: TNumberId;
vertex_position: Vector;
selected_id: TNumberId;
// Whether object can detect and help nearby wounded stalkers.
isHelpingWoundedEnabled: boolean;
// Selected wounded stalker vertex id to help.
selectedWoundedVertexId: TNumberId;
// Selected wounded stalker position to help.
selectedWoundedVertexPosition: Vector;
// Selected wounded stalker ID to help.
selectedWoundedId: TNumberId;
}
64 changes: 23 additions & 41 deletions src/engine/core/schemes/help_wounded/SchemeHelpWounded.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
import { alife, stalker_ids, world_property } from "xray16";
import { stalker_ids, world_property } from "xray16";

import { IRegistryObjectState, registry } from "@/engine/core/database";
import { GlobalSoundManager } from "@/engine/core/managers/sounds/GlobalSoundManager";
import { IRegistryObjectState } from "@/engine/core/database";
import { AbstractScheme, EActionId, EEvaluatorId } from "@/engine/core/schemes";
import { ActionHelpWounded } from "@/engine/core/schemes/help_wounded/actions";
import { EvaluatorWoundedExist } from "@/engine/core/schemes/help_wounded/evaluators";
import { ISchemeHelpWoundedState } from "@/engine/core/schemes/help_wounded/ISchemeHelpWoundedState";
import { SchemeWounded } from "@/engine/core/schemes/wounded/SchemeWounded";
import { readIniBoolean } from "@/engine/core/utils/ini/ini_read";
import { LuaLogger } from "@/engine/core/utils/logging";
import { scriptSounds } from "@/engine/lib/constants/sound/script_sounds";
import { ActionPlanner, ClientObject, IniFile, Optional, TNumberId } from "@/engine/lib/types";
import { ActionPlanner, ClientObject, IniFile, Optional } from "@/engine/lib/types";
import { EScheme, ESchemeType, TSection } from "@/engine/lib/types/scheme";

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

/**
* todo;
* Scheme describing object logics for helping friendly injured stalkers.
* Part of shared generics logics available for all stalker objects.
*/
export class SchemeHelpWounded extends AbstractScheme {
public static override readonly SCHEME_SECTION: EScheme = EScheme.HELP_WOUNDED;
public static override readonly SCHEME_TYPE: ESchemeType = ESchemeType.STALKER;

/**
* todo: Description.
* Activate section with help wounded for the object.
*/
public static override activate(object: ClientObject, ini: IniFile, scheme: EScheme, section: Optional<TSection>) {
AbstractScheme.assign(object, ini, scheme, section);
}

/**
* todo: Description.
* Add scheme generic states / evaluators / actions for the object.
*/
public static override add(
object: ClientObject,
Expand All @@ -41,6 +39,7 @@ export class SchemeHelpWounded extends AbstractScheme {
): void {
const actionPlanner: ActionPlanner = object.motivation_action_manager();

// Add custom evaluator to check if wounded stalkers exist nearby.
actionPlanner.add_evaluator(EEvaluatorId.IS_WOUNDED_EXISTING, new EvaluatorWoundedExist(state));

const action: ActionHelpWounded = new ActionHelpWounded(state);
Expand All @@ -51,53 +50,36 @@ export class SchemeHelpWounded extends AbstractScheme {
action.add_precondition(new world_property(stalker_ids.property_anomaly, false));
action.add_precondition(new world_property(EEvaluatorId.IS_WOUNDED_EXISTING, true));
action.add_precondition(new world_property(EEvaluatorId.IS_WOUNDED, false));
// Clean up wounded stalkers search once action is finished.
action.add_effect(new world_property(EEvaluatorId.IS_WOUNDED_EXISTING, false));

// Help stalkers nearby if conditions are met.
actionPlanner.add_action(EActionId.HELP_WOUNDED, action);

// Do not allow alife activity before finish helping all stalkers nearby.
actionPlanner.action(EActionId.ALIFE).add_precondition(new world_property(EEvaluatorId.IS_WOUNDED_EXISTING, false));

// Do not allow alife idle activity before finish helping all stalkers nearby.
actionPlanner
.action(EActionId.STATE_TO_IDLE_ALIFE)
.add_precondition(new world_property(EEvaluatorId.IS_WOUNDED_EXISTING, false));
}

/**
* todo: Description.
* Reset scheme state and read configuration from current logics section for the object.
*/
public static override reset(object: ClientObject, scheme: EScheme, state: IRegistryObjectState, section: TSection) {
(state[SchemeHelpWounded.SCHEME_SECTION] as ISchemeHelpWoundedState).help_wounded_enabled = readIniBoolean(
state.ini!,
public static override reset(
object: ClientObject,
scheme: EScheme,
state: IRegistryObjectState,
section: TSection
): void {
(state[SchemeHelpWounded.SCHEME_SECTION] as ISchemeHelpWoundedState).isHelpingWoundedEnabled = readIniBoolean(
state.ini,
section,
"help_wounded_enabled",
false,
true
);
}

/**
* todo: Description.
*/
public static helpWounded(object: ClientObject): void {
const state: IRegistryObjectState = registry.objects.get(object.id());
const selectedId: TNumberId = (state[EScheme.HELP_WOUNDED] as ISchemeHelpWoundedState).selected_id;
const selectedObject: Optional<ClientObject> =
registry.objects.get(selectedId) && registry.objects.get(selectedId).object!;

if (selectedObject === null) {
return;
}

alife().create(
"medkit_script",
selectedObject.position(),
selectedObject.level_vertex_id(),
selectedObject.game_vertex_id(),
selectedId
);

SchemeWounded.unlockMedkit(selectedObject);

registry.objects.get(selectedId).wounded_already_selected = -1;

GlobalSoundManager.getInstance().playSound(object.id(), scriptSounds.wounded_medkit, null, null);
}
}
27 changes: 12 additions & 15 deletions src/engine/core/schemes/help_wounded/actions/ActionHelpWounded.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { LuaLogger } from "@/engine/core/utils/logging";
const logger: LuaLogger = new LuaLogger($filename);

/**
* todo;
* Action class describing how stalkers help each other when one of them is wounded.
*/
@LuabindClass()
export class ActionHelpWounded extends action_base {
Expand All @@ -20,35 +20,32 @@ export class ActionHelpWounded extends action_base {
}

/**
* todo: Description.
* On init set destination vertex of wounded object and try to reach it.
*/
public override initialize(): void {
super.initialize();

this.object.set_desired_position();
this.object.set_desired_direction();
this.object.set_dest_level_vertex_id(this.state.vertex_id);

this.object.set_dest_level_vertex_id(this.state.selectedWoundedVertexId);

setStalkerState(this.object, EStalkerState.PATROL);
}

/**
* todo: Description.
* Wait for object to reach target location.
* Then run heal up animation.
* On animation end separate callback to heal target will be called.
*/
public override execute(): void {
super.execute();

if (this.object.position().distance_to_sqr(this.state.vertex_position) > 2) {
return;
if (this.object.position().distance_to_sqr(this.state.selectedWoundedVertexPosition) <= 2) {
setStalkerState(this.object, EStalkerState.HELP_WOUNDED, null, null, {
lookPosition: this.state.selectedWoundedVertexPosition,
lookObject: null,
});
}

setStalkerState(
this.object,
EStalkerState.HELP_WOUNDED,
null,
null,
{ lookPosition: this.state.vertex_position, lookObject: null },
null
);
}
}
Loading

0 comments on commit c9dd44f

Please sign in to comment.