From bb3513956c010803bbbd46c7d354f4b85c957e34 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 21:34:41 +0300 Subject: [PATCH] Add op_force_encounter --- src/sfall_opcodes.cc | 17 +++++++++++++++ src/worldmap.cc | 52 ++++++++++++++++++++++++++++++++++++++++++++ src/worldmap.h | 7 ++++++ 3 files changed, 76 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index ea45d4e..8d8cd57 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -138,6 +138,13 @@ static void op_in_world_map(Program* program) programStackPushInteger(program, GameMode::isInGameMode(GameMode::kWorldmap) ? 1 : 0); } +// force_encounter +static void op_force_encounter(Program* program) +{ + int map = programStackPopInteger(program); + wmForceEncounter(map, 0); +} + // set_world_map_pos static void op_set_world_map_pos(Program* program) { @@ -541,6 +548,14 @@ static void op_get_attack_type(Program* program) } } +// force_encounter_with_flags +static void op_force_encounter_with_flags(Program* program) +{ + unsigned int flags = programStackPopInteger(program); + int map = programStackPopInteger(program); + wmForceEncounter(map, flags); +} + // atoi static void opParseInt(Program* program) { @@ -894,6 +909,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x816A, op_set_global_script_repeat); interpreterRegisterOpcode(0x816C, op_key_pressed); interpreterRegisterOpcode(0x8170, op_in_world_map); + interpreterRegisterOpcode(0x8171, op_force_encounter); interpreterRegisterOpcode(0x8172, op_set_world_map_pos); interpreterRegisterOpcode(0x8193, opGetCurrentHand); interpreterRegisterOpcode(0x819B, op_set_global_script_type); @@ -927,6 +943,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8224, op_create_message_window); interpreterRegisterOpcode(0x8228, op_get_attack_type); + interpreterRegisterOpcode(0x8229, op_force_encounter_with_flags); interpreterRegisterOpcode(0x822D, opCreateArray); interpreterRegisterOpcode(0x822E, opSetArray); interpreterRegisterOpcode(0x822F, opGetArray); diff --git a/src/worldmap.cc b/src/worldmap.cc index 3243d8d..8b30255 100644 --- a/src/worldmap.cc +++ b/src/worldmap.cc @@ -817,6 +817,8 @@ static double gGameTimeIncRemainder = 0.0; static FrmImage _backgroundFrmImage; static FrmImage _townFrmImage; static bool wmFaded = false; +static int wmForceEncounterMapId = -1; +static unsigned int wmForceEncounterFlags = 0; static inline bool cityIsValid(int city) { @@ -929,6 +931,9 @@ static int wmGenDataInit() wmGenData.tabsScrollingDelta = 0; wmGenData.viewportMaxX = 0; + wmForceEncounterMapId = -1; + wmForceEncounterFlags = 0; + return 0; } @@ -979,6 +984,9 @@ static int wmGenDataReset() wmMarkSubTileRadiusVisited(wmGenData.worldPosX, wmGenData.worldPosY); + wmForceEncounterMapId = -1; + wmForceEncounterFlags = 0; + return 0; } @@ -3347,6 +3355,33 @@ static int wmRndEncounterOccurred() } } + // SFALL: Handle forced encounter. + // CE: In Sfall a check for forced encounter is inserted instead of check + // for Horrigan encounter (above). This implemenation gives Horrigan + // encounter a priority. + if (wmForceEncounterMapId != -1) { + if ((wmForceEncounterFlags & ENCOUNTER_FLAG_NO_CAR) != 0) { + if (wmGenData.isInCar) { + wmMatchAreaContainingMapIdx(wmForceEncounterMapId, &(wmGenData.currentCarAreaId)); + } + } + + // For unknown reason fadeout and blinking icon are mutually exclusive. + if ((wmForceEncounterFlags & ENCOUNTER_FLAG_FADEOUT) != 0) { + wmFadeOut(); + } else if ((wmForceEncounterFlags & ENCOUNTER_FLAG_NO_ICON) == 0) { + bool special = (wmForceEncounterFlags & ENCOUNTER_FLAG_ICON_SP) != 0; + wmBlinkRndEncounterIcon(special); + } + + mapLoadById(wmForceEncounterMapId); + + wmForceEncounterMapId = -1; + wmForceEncounterFlags = 0; + + return 1; + } + // NOTE: Uninline. wmPartyFindCurSubTile(); @@ -6608,4 +6643,21 @@ void wmCarSetCurrentArea(int area) wmGenData.currentCarAreaId = area; } +void wmForceEncounter(int map, unsigned int flags) +{ + if ((wmForceEncounterFlags & (1 << 31)) != 0) { + return; + } + + wmForceEncounterMapId = map; + wmForceEncounterFlags = flags; + + // I don't quite understand the reason why locking needs one more flag. + if ((wmForceEncounterFlags & ENCOUNTER_FLAG_LOCK) != 0) { + wmForceEncounterFlags |= (1 << 31); + } else { + wmForceEncounterFlags &= ~(1 << 31); + } +} + } // namespace fallout diff --git a/src/worldmap.h b/src/worldmap.h index 901b0a6..20ea1c2 100644 --- a/src/worldmap.h +++ b/src/worldmap.h @@ -229,6 +229,12 @@ typedef enum Map { MAP_IN_GAME_MOVIE1 = 149, } Map; +#define ENCOUNTER_FLAG_NO_CAR 0x1 +#define ENCOUNTER_FLAG_LOCK 0x2 +#define ENCOUNTER_FLAG_NO_ICON 0x4 +#define ENCOUNTER_FLAG_ICON_SP 0x8 +#define ENCOUNTER_FLAG_FADEOUT 0x10 + extern unsigned char* circleBlendTable; int wmWorldMap_init(); @@ -279,6 +285,7 @@ int wmTeleportToArea(int areaIdx); void wmSetPartyWorldPos(int x, int y); void wmCarSetCurrentArea(int area); +void wmForceEncounter(int map, unsigned int flags); } // namespace fallout