diff --git a/base/ControllableChar.ts b/base/ControllableChar.ts index 3a1b5ef67c..24625ab45e 100644 --- a/base/ControllableChar.ts +++ b/base/ControllableChar.ts @@ -87,6 +87,8 @@ export abstract class ControllableChar { public casting_psynergy: boolean; /** Whether this char is under Reveal psynergy effect. */ public on_reveal: boolean; + /** Whether this char is under custom psynergy effect. */ + public on_custom_psynergy_effect: boolean; /** Whether this char is teleporting by the teleport TileEvent. */ public teleporting: boolean; /** Whether this char is idle during climbing state. */ @@ -227,6 +229,7 @@ export abstract class ControllableChar { this.misc_busy = false; this.casting_psynergy = false; this.on_reveal = false; + this.on_custom_psynergy_effect = false; this.teleporting = false; this.idle_climbing = false; this.ice_sliding_active = false; diff --git a/base/Map.ts b/base/Map.ts index b3aae569b7..382c82b900 100644 --- a/base/Map.ts +++ b/base/Map.ts @@ -114,7 +114,7 @@ export class Map { id: number; rect: Phaser.Rectangle; }[]; - private current_bounding_box_id: number; + private _current_bounding_box_id: number; private game_events: GameEvent[]; private before_config_game_events: GameEvent[]; private before_camera_fade_game_events: GameEvent[]; @@ -213,7 +213,7 @@ export class Map { this._background_key = background_key; this.polygons_processed = new Set(); this.bounding_boxes = []; - this.current_bounding_box_id = null; + this._current_bounding_box_id = null; this.game_events = []; this.before_config_game_events = []; this.before_camera_fade_game_events = []; @@ -345,7 +345,6 @@ export class Map { get active_filters() { return this._active_filters; } - /** Gets the window object that shows this map's name. */ get map_name_window() { return this._map_name_window; @@ -354,21 +353,22 @@ export class Map { get show_map_name() { return this._show_map_name; } - /** The internal storage keys created for objects of this map. */ get internal_map_objs_storage_keys() { return this._internal_map_objs_storage_keys; } - /** Gets Retreat psynergy info. Returns the x and y tile posoition to retreat and the destination collision index and direction. */ get retreat_data() { return this._retreat_data; } - /** Gets the collision layer index that sand psynergy will use. */ get sand_collision_layer() { return this._sand_collision_layer; } + /** Gets the current bounding box object ID. */ + get current_bounding_box_id() { + return this._current_bounding_box_id; + } /** * Sorts the sprites in the GoldenSun.middlelayer_group by y position and base_collision_layer @@ -1440,7 +1440,7 @@ export class Map { for (let i = 0; i < this.bounding_boxes.length; ++i) { const bounding_box = this.bounding_boxes[i].rect; if (bounding_box.contains(x, y)) { - this.current_bounding_box_id = this.bounding_boxes[i].id; + this._current_bounding_box_id = this.bounding_boxes[i].id; break; } } @@ -2397,7 +2397,7 @@ export class Map { this.data.middlelayer_group.removeAll(); this.encounter_zones = []; this.bounding_boxes = []; - this.current_bounding_box_id = null; + this._current_bounding_box_id = null; this.game_events = []; this.before_config_game_events = []; this.before_camera_fade_game_events = []; diff --git a/base/Snapshot.ts b/base/Snapshot.ts index 144696d140..683a4ccff7 100644 --- a/base/Snapshot.ts +++ b/base/Snapshot.ts @@ -12,7 +12,6 @@ import {Button} from "./XGamepad"; import {reverse_directions, engine_filters} from "./utils"; import {Breakable} from "./interactable_objects/Breakable"; import {RollablePillar} from "./interactable_objects/RollingPillar"; -import {RevealFieldPsynergy} from "./field_abilities/RevealFieldPsynergy"; import {RopeDock} from "./interactable_objects/RopeDock"; import {Map} from "Map"; import {Pushable} from "interactable_objects/Pushable"; @@ -507,7 +506,14 @@ export class Snapshot { mute: this.game.sound.mute, }; if (this.data.hero.on_reveal) { - (this.data.info.field_abilities_list.reveal as RevealFieldPsynergy).finish(false, false); + this.data.info.field_abilities_list.reveal.finish_psynergy(false, false); + } + if (this.data.hero.on_custom_psynergy_effect) { + _.forEach(this.data.info.field_abilities_list, ability => { + if (ability.is_custom_psynergy) { + ability.finish_psynergy(false, false); + } + }); } Snapshot.download_json(snapshot, Snapshot.SNAPSHOT_FILENAME); } diff --git a/base/field_abilities/FieldAbilities.ts b/base/field_abilities/FieldAbilities.ts index 57fb32d5b6..69f211f34c 100644 --- a/base/field_abilities/FieldAbilities.ts +++ b/base/field_abilities/FieldAbilities.ts @@ -11,6 +11,7 @@ import {Button} from "../XGamepad"; import {DialogManager} from "../utils/DialogManager"; import {degree360} from "../magic_numbers"; import {Pushable} from "../interactable_objects/Pushable"; +import {MainChar} from "../MainChar"; /** * Defines and manages the usage of field psynergy. @@ -31,6 +32,8 @@ export abstract class FieldAbilities { protected data: GoldenSun; /** The controllable char that's casting this psynergy. */ protected controllable_char: ControllableChar; + /** The main char that it casting the psynergy. */ + protected caster: MainChar; /** The target object of this psynergy. Might not be necessary depending on the psynergy. */ protected target_object: InteractableObjects | NPC; /** If called, the casting aura is destroyed. */ @@ -57,6 +60,7 @@ export abstract class FieldAbilities { private extra_cast_check: () => boolean; private target_is_npc: boolean; private map_colors_sequence: boolean; + public is_custom_psynergy: boolean; private previous_collision_status: { char: boolean; target?: boolean; @@ -80,6 +84,7 @@ export abstract class FieldAbilities { * @param ask_before_cast If true, it opens an YesNo menu asking if the char really wants to cast this ability. * @param target_is_npc If true, the target is a NPC instead of an IO. * @param map_colors_sequence If true, the map will be tinted sequentially with random colors. + * @param is_custom_psynergy Set this to true, if it's a custom psynergy. */ constructor( game: Phaser.Game, @@ -95,7 +100,8 @@ export abstract class FieldAbilities { target_found_extra_check?: (target: FieldAbilities["target_object"]) => boolean, ask_before_cast?: boolean, target_is_npc?: boolean, - map_colors_sequence?: boolean + map_colors_sequence?: boolean, + is_custom_psynergy?: boolean ) { this.game = game; this.ability_key_name = ability_key_name; @@ -118,6 +124,7 @@ export abstract class FieldAbilities { this.ask_before_cast_yes_no_menu = new YesNoMenu(this.game, this.data); this.target_is_npc = target_is_npc ?? false; this.map_colors_sequence = map_colors_sequence ?? false; + this.is_custom_psynergy = is_custom_psynergy ?? false; } /** @@ -185,6 +192,14 @@ export abstract class FieldAbilities { this.cast_finisher = method; } + /** + * Generic funcion that can be overriden. When calling this, the psynergy effect should be finished. + * @param force for the psynergy to finish. + * @param stop_char stops the char current animation. + * @param finish_callback the callback method to be called on psynergy end finish. + */ + finish_psynergy(force: boolean = false, stop_char: boolean = true, finish_callback?: () => void) {} + /** * Searches for target, NPC or IO, if this psynergy requests a target. */ @@ -433,6 +448,7 @@ export abstract class FieldAbilities { cast(controllable_char: ControllableChar, caster_key_name: string) { this.controllable_char = controllable_char; const caster = this.data.info.main_char_list[caster_key_name]; + this.caster = caster; if ( this.controllable_char.casting_psynergy || this.controllable_char.sand_mode || diff --git a/base/field_abilities/RevealFieldPsynergy.ts b/base/field_abilities/RevealFieldPsynergy.ts index e341c20777..40f60ce59f 100644 --- a/base/field_abilities/RevealFieldPsynergy.ts +++ b/base/field_abilities/RevealFieldPsynergy.ts @@ -47,12 +47,12 @@ export class RevealFieldPsynergy extends FieldAbilities { update() { if (this.controllable_char?.on_reveal) { if (this.should_finish_reveal(this.controllable_char.x, this.controllable_char.y)) { - this.finish(); + this.finish_psynergy(); } } } - toggle_reveal() { + private toggle_reveal() { this.data.map.layers.forEach(layer => { if (layer.properties?.reveal_layer) { layer.sprite.visible = !layer.sprite.visible; @@ -93,7 +93,7 @@ export class RevealFieldPsynergy extends FieldAbilities { } } - finish(force: boolean = false, stop_char: boolean = true, finish_callback?: () => void) { + finish_psynergy(force: boolean = false, stop_char: boolean = true, finish_callback?: () => void) { this.controllable_char.on_reveal = false; const previous_psynergy_state = this.controllable_char.casting_psynergy; this.controllable_char.casting_psynergy = true; @@ -126,11 +126,11 @@ export class RevealFieldPsynergy extends FieldAbilities { } } - show_wave() { + private show_wave() { this.close_field_psynergy_window(); if (this.controllable_char.on_reveal) { - this.finish(true); + this.finish_psynergy(true); this.controllable_char.casting_psynergy = true; } this.casting_point = { @@ -216,7 +216,7 @@ export class RevealFieldPsynergy extends FieldAbilities { } } } - this.finish(); + this.finish_psynergy(); }); this.end_timer.start(); }; diff --git a/base/game_events/GameEvent.ts b/base/game_events/GameEvent.ts index b63aa117c9..c9a2a7daf2 100644 --- a/base/game_events/GameEvent.ts +++ b/base/game_events/GameEvent.ts @@ -1,7 +1,7 @@ import {StoragePosition} from "Storage"; -import {RevealFieldPsynergy} from "../field_abilities/RevealFieldPsynergy"; import {GoldenSun} from "../GoldenSun"; import {NPC} from "../NPC"; +import * as _ from "lodash"; type BasicValues = string | number | boolean; @@ -209,9 +209,20 @@ export abstract class GameEvent { } /** Check if "Reveal" is currently active. If yes, then cancel it. */ - check_reveal() { + private check_reveal() { if (this.data.hero?.on_reveal && !this.keep_reveal) { - (this.data.info.field_abilities_list.reveal as RevealFieldPsynergy).finish(false, false); + this.data.info.field_abilities_list.reveal.finish_psynergy(false, false); + } + } + + /** Check if a custom psynergy is currently active. If yes, then cancel it. */ + private check_custom_psynergy() { + if (this.data?.hero.on_custom_psynergy_effect) { + _.forEach(this.data.info.field_abilities_list, ability => { + if (ability.is_custom_psynergy) { + ability.finish_psynergy(false, false); + } + }); } } @@ -228,6 +239,7 @@ export abstract class GameEvent { return; } this.check_reveal(); + this.check_custom_psynergy(); this._origin_npc = origin_npc; this._fire(); } diff --git a/base/tile_events/ClimbEvent.ts b/base/tile_events/ClimbEvent.ts index 5cb52dc5b5..e87a572ee9 100644 --- a/base/tile_events/ClimbEvent.ts +++ b/base/tile_events/ClimbEvent.ts @@ -2,8 +2,8 @@ import {base_actions, directions, reverse_directions} from "../utils"; import {TileEvent, event_types} from "./TileEvent"; import * as numbers from "../magic_numbers"; import {InteractableObjects, interactable_object_event_types} from "../interactable_objects/InteractableObjects"; -import {RevealFieldPsynergy} from "../field_abilities/RevealFieldPsynergy"; import {GoldenSun} from "../GoldenSun"; +import * as _ from "lodash"; export enum climb_actions { IDLE = "idle", @@ -66,7 +66,14 @@ export class ClimbEvent extends TileEvent { return; } if (this.data.hero.on_reveal) { - (this.data.info.field_abilities_list.reveal as RevealFieldPsynergy).finish(false, false); + this.data.info.field_abilities_list.reveal.finish_psynergy(false, false); + } + if (this.data.hero.on_custom_psynergy_effect) { + _.forEach(this.data.info.field_abilities_list, ability => { + if (ability.is_custom_psynergy) { + ability.finish_psynergy(false, false); + } + }); } if ( this.current_activation_direction !== directions.up && diff --git a/base/tile_events/TeleportEvent.ts b/base/tile_events/TeleportEvent.ts index bcc135c49f..1feb15a7be 100644 --- a/base/tile_events/TeleportEvent.ts +++ b/base/tile_events/TeleportEvent.ts @@ -1,7 +1,6 @@ import {base_actions, directions, get_centered_pos_in_px, reverse_directions} from "../utils"; import {event_types, TileEvent} from "./TileEvent"; import * as _ from "lodash"; -import {RevealFieldPsynergy} from "../field_abilities/RevealFieldPsynergy"; import {climb_actions} from "./ClimbEvent"; import {ParticlesInfo, ParticlesWrapper} from "../ParticlesWrapper"; @@ -366,7 +365,14 @@ export class TeleportEvent extends TileEvent { } } if (this.data.hero.on_reveal) { - (this.data.info.field_abilities_list.reveal as RevealFieldPsynergy).finish(true); + this.data.info.field_abilities_list.reveal.finish_psynergy(true); + } + if (this.data.hero.on_custom_psynergy_effect) { + _.forEach(this.data.info.field_abilities_list, ability => { + if (ability.is_custom_psynergy) { + ability.finish_psynergy(true); + } + }); } const destination_direction = directions[this.destination_direction] !== undefined