diff --git a/base/Hero.ts b/base/Hero.ts index 7ef51ec75d..ac5a274be4 100644 --- a/base/Hero.ts +++ b/base/Hero.ts @@ -264,6 +264,19 @@ export class Hero extends ControllableChar { } } + /** + * Sets the hero visibility. + * @param visible whether to be visible or not. + */ + set_visible(visible: boolean) { + if (this.sprite) { + this.sprite.visible = visible; + if (this.shadow) { + this.shadow.visible = this.sprite.visible; + } + } + } + /** * Gets the hero battle encounter factor that depends on the type of the map (if * it's world map or not) and whether it's dashing or not. diff --git a/base/NPC.ts b/base/NPC.ts index 44788dee15..359b4d8758 100644 --- a/base/NPC.ts +++ b/base/NPC.ts @@ -341,10 +341,7 @@ export class NPC extends ControllableChar { if (this.storage_keys.visible !== undefined) { const storage_value = this.data.storage.get(this.storage_keys.visible); if (this.sprite?.visible !== storage_value) { - this.sprite.visible = storage_value as boolean; - if (this.shadow) { - this.shadow.visible = storage_value as boolean; - } + this.set_visible(storage_value as boolean); } } if (this.storage_keys.movement_type !== undefined) { @@ -654,9 +651,9 @@ export class NPC extends ControllableChar { } else { this.sprite.visible = true; } - } - if (this.shadow) { - this.shadow.visible = this.sprite.visible; + if (this.shadow) { + this.shadow.visible = this.sprite.visible; + } } this._active = true; } else { @@ -669,6 +666,9 @@ export class NPC extends ControllableChar { } this._active = false; } + if (this.storage_keys.active !== undefined) { + this.data.storage.set(this.storage_keys.active, this._active); + } } /** @@ -678,9 +678,12 @@ export class NPC extends ControllableChar { set_visible(visible: boolean) { if (this.sprite) { this.sprite.visible = visible; + if (this.shadow) { + this.shadow.visible = this.sprite.visible; + } } - if (this.shadow) { - this.shadow.visible = visible; + if (this.storage_keys.visible !== undefined) { + this.data.storage.set(this.storage_keys.visible, visible); } } diff --git a/base/Storage.ts b/base/Storage.ts index c89d2b4f46..b68dee1db7 100644 --- a/base/Storage.ts +++ b/base/Storage.ts @@ -168,7 +168,18 @@ export class Storage { this.data.logger.log_message(`There's no storage value with key '${key_name}'.`); return; } + const prev_value = storage[key_name].value; storage[key_name].value = value; + if (storage[key_name].type === storage_types.POSITION) { + if ( + (prev_value as StoragePosition).x === (value as StoragePosition).x && + (prev_value as StoragePosition).y === (value as StoragePosition).y + ) { + return; + } + } else if (prev_value === value) { + return; + } for (let id in storage[key_name].callbacks) { const callback_obj = storage[key_name].callbacks[id]; callback_obj.callback(); diff --git a/base/game_events/GameEventManager.ts b/base/game_events/GameEventManager.ts index 6cf79ef20a..345de40f1d 100644 --- a/base/game_events/GameEventManager.ts +++ b/base/game_events/GameEventManager.ts @@ -376,8 +376,10 @@ export class GameEventManager { info.keep_reveal, info.event_value, info.check_npc_storage_values, + info.check_io_storage_values, info.check_collision_structures, info.check_layers_visibility, + info.io_label, info.npc_label, info.npc_index, info.increment, diff --git a/base/game_events/SetCharActivationEvent.ts b/base/game_events/SetCharActivationEvent.ts index eb5bbfc771..b90b7e690e 100644 --- a/base/game_events/SetCharActivationEvent.ts +++ b/base/game_events/SetCharActivationEvent.ts @@ -19,7 +19,7 @@ export class SetCharActivationEvent extends GameEvent { npc_label: this.npc_label, }) ?? this.origin_npc; - target_char.toggle_active(this.activate); + target_char?.toggle_active(this.activate); } _destroy() {} diff --git a/base/game_events/SetCharVisibilityEvent.ts b/base/game_events/SetCharVisibilityEvent.ts index e7c616f8b9..c933461a99 100644 --- a/base/game_events/SetCharVisibilityEvent.ts +++ b/base/game_events/SetCharVisibilityEvent.ts @@ -18,11 +18,7 @@ export class SetCharVisibilityEvent extends GameEvent { is_npc: this.is_npc, npc_label: this.npc_label, }) ?? this.origin_npc; - - target_char.sprite.visible = this.visible; - if (target_char.shadow) { - target_char.shadow.visible = this.visible; - } + target_char.set_visible(this.visible); } _destroy() {} diff --git a/base/game_events/SetIoVisibilityEvent.ts b/base/game_events/SetIoVisibilityEvent.ts index e193b931dc..d0c0741ce2 100644 --- a/base/game_events/SetIoVisibilityEvent.ts +++ b/base/game_events/SetIoVisibilityEvent.ts @@ -16,9 +16,7 @@ export class SetIoVisibilityEvent extends GameEvent { return; } const interactable_object = this.data.map.interactable_objects_label_map[this.io_label]; - if (interactable_object.sprite) { - interactable_object.sprite.visible = this.visible; - } + interactable_object.set_visible(this.visible); } _destroy() {} diff --git a/base/game_events/SetValueEvent.ts b/base/game_events/SetValueEvent.ts index e57307ba64..aa39cd8215 100644 --- a/base/game_events/SetValueEvent.ts +++ b/base/game_events/SetValueEvent.ts @@ -7,8 +7,10 @@ import {storage_types} from "../Storage"; export class SetValueEvent extends GameEvent { private event_value: EventValue; private check_npc_storage_values: boolean; + private check_io_storage_values: boolean; private check_collision_structures: boolean; private check_layers_visibility: boolean; + private io_label: string; private npc_label: string; private npc_index: number; private increment: boolean; @@ -22,8 +24,10 @@ export class SetValueEvent extends GameEvent { keep_reveal, event_value, check_npc_storage_values, + check_io_storage_values, check_collision_structures, check_layers_visibility, + io_label, npc_label, npc_index, increment, @@ -32,8 +36,10 @@ export class SetValueEvent extends GameEvent { super(game, data, event_types.SET_VALUE, active, key_name, keep_reveal); this.event_value = event_value; this.check_npc_storage_values = check_npc_storage_values ?? false; + this.check_io_storage_values = check_io_storage_values ?? false; this.check_collision_structures = check_collision_structures ?? false; this.check_layers_visibility = check_layers_visibility ?? false; + this.io_label = io_label; this.npc_label = npc_label; this.npc_index = npc_index; this.increment = increment ?? false; @@ -167,6 +173,17 @@ export class SetValueEvent extends GameEvent { ); } } + if (this.check_io_storage_values) { + if (!(this.io_label in this.data.map.interactable_objects_label_map)) { + this.data.logger.log_message( + `IO label not passed to "set_value" event when "check_io_storage_values" was set true.` + ); + return; + } else { + const interactable_object = this.data.map.interactable_objects_label_map[this.io_label]; + interactable_object.check_storage_keys(); + } + } if (this.check_collision_structures) { this.data.map.collision_sprite.body.data.shapes.forEach(shape => { if (shape.properties?.controller_variable) { diff --git a/base/interactable_objects/InteractableObjects.ts b/base/interactable_objects/InteractableObjects.ts index 2fd47e1cfe..53137ce5b2 100644 --- a/base/interactable_objects/InteractableObjects.ts +++ b/base/interactable_objects/InteractableObjects.ts @@ -66,7 +66,10 @@ export class InteractableObjects { scale?: string; base_collision_layer?: string; enable?: string; + active?: string; + visible?: string; entangled_by_bush?: string; + action?: string; animation?: string; affected_by_reveal?: string; }; @@ -238,6 +241,9 @@ export class InteractableObjects { this.scale_y = scale_y; this._psynergy_casted = {}; this.block_climb_collision_layer_shift = block_climb_collision_layer_shift; + if (this.storage_keys.active !== undefined) { + active = this.data.storage.get(this.storage_keys.active); + } this._active = active ?? true; if (this.snapshot_info) { this._active = this.snapshot_info.active; @@ -279,6 +285,9 @@ export class InteractableObjects { animation = this.data.storage.get(this.storage_keys.animation); } this._current_animation = animation; + if (this.storage_keys.action !== undefined) { + action = this.data.storage.get(this.storage_keys.action); + } this._current_action = action; this._shapes_collision_active = false; this._active_filters = { @@ -292,6 +301,9 @@ export class InteractableObjects { [engine_filters.FLAME]: false, }; this._map_index = map_index; + if (this.storage_keys.visible !== undefined) { + initially_visible = this.data.storage.get(this.storage_keys.visible); + } this._initially_visible = initially_visible ?? true; } @@ -568,6 +580,22 @@ export class InteractableObjects { }); } + /** + * Sets this IO visibility. + * @param visible whether to be visible or not. + */ + set_visible(visible: boolean) { + if (this.sprite) { + this.sprite.visible = visible; + if (this.shadow) { + this.shadow.visible = this.sprite.visible; + } + } + if (this.storage_keys.visible !== undefined) { + this.data.storage.set(this.storage_keys.visible, visible); + } + } + set_entangled_by_bush(entangled_by_bush: boolean) { this._entangled_by_bush = entangled_by_bush; if (this.storage_keys.entangled_by_bush !== undefined) { @@ -1003,8 +1031,8 @@ export class InteractableObjects { } } - play(animation: string, action?: string, start: boolean = true, frame_rate?: number, loop?: boolean) { - this._current_animation = animation; + play(animation?: string, action?: string, start: boolean = true, frame_rate?: number, loop?: boolean) { + this._current_animation = animation ?? this._current_animation; this._current_action = action ?? this._current_action; if (SpriteBase.getSpriteAction(this.sprite) !== this.current_action) { @@ -1300,6 +1328,64 @@ export class InteractableObjects { }); } + /** + * Updates this IO properties according to current storage values. + */ + check_storage_keys() { + if (this.storage_keys.active !== undefined) { + const storage_value = this.data.storage.get(this.storage_keys.active); + if (this.active !== storage_value) { + this.toggle_active(storage_value as boolean); + } + } + if (this.storage_keys.base_collision_layer !== undefined) { + const storage_value = this.data.storage.get(this.storage_keys.base_collision_layer); + if (this.base_collision_layer !== storage_value) { + this._base_collision_layer = storage_value as number; + } + } + if (this.storage_keys.affected_by_reveal !== undefined) { + const storage_value = this.data.storage.get(this.storage_keys.affected_by_reveal); + if (this.affected_by_reveal !== storage_value) { + this._affected_by_reveal = storage_value as boolean; + } + } + if (this.storage_keys.visible !== undefined) { + const storage_value = this.data.storage.get(this.storage_keys.visible); + if (this.sprite?.visible !== storage_value) { + this.set_visible(storage_value as boolean); + } + } + if (this.storage_keys.enable !== undefined) { + const storage_value = this.data.storage.get(this.storage_keys.enable); + if (this.enable !== storage_value) { + this.set_enable(storage_value as boolean); + } + } + let action_or_animation_changed = false; + if (this.storage_keys.action !== undefined) { + const storage_value = this.data.storage.get(this.storage_keys.action); + if (this.current_action !== storage_value) { + this._current_action = storage_value as string; + action_or_animation_changed = true; + } + } + if (this.storage_keys.animation !== undefined) { + const storage_value = this.data.storage.get(this.storage_keys.animation); + if (this.current_animation !== storage_value) { + this._current_animation = storage_value as string; + action_or_animation_changed = true; + } + } + if (action_or_animation_changed) { + this.play(); + } + } + + /** + * Activates or deactivates this IO. + * @param active true, if you want to activate it. + */ toggle_active(active: boolean) { if (active) { this.sprite?.body?.collides(this.data.collision.hero_collision_group); @@ -1310,10 +1396,14 @@ export class InteractableObjects { this._blocking_stair_block.collides(this.data.collision.hero_collision_group); } if (this.sprite) { - this.sprite.visible = true; - } - if (this.shadow) { - this.shadow.visible = true; + if (this.storage_keys.visible !== undefined) { + this.sprite.visible = this.data.storage.get(this.storage_keys.visible) as boolean; + } else { + this.sprite.visible = true; + } + if (this.shadow) { + this.shadow.visible = this.sprite.visible; + } } this._active = true; } else { @@ -1328,10 +1418,13 @@ export class InteractableObjects { this.sprite.visible = false; } if (this.shadow) { - this.shadow.visible = false; + this.shadow.visible = this.sprite.visible; } this._active = false; } + if (this.storage_keys.active !== undefined) { + this.data.storage.set(this.storage_keys.active, this._active); + } } config_body() {