From 3370810795994c48741473ba709de651ca5a5946 Mon Sep 17 00:00:00 2001 From: CCHyper <73803386+CCHyper@users.noreply.github.com> Date: Wed, 28 Dec 2022 13:43:19 +0000 Subject: [PATCH] Implements support for up to 701 ("ZZ") waypoints to be used in scenarios. --- src/extensions/scenario/scenarioext.cpp | 288 +++++++++++++++++- src/extensions/scenario/scenarioext.h | 29 +- src/extensions/scenario/scenarioext_hooks.cpp | 49 +++ src/vinifera/vinifera_defines.h | 6 + 4 files changed, 370 insertions(+), 2 deletions(-) diff --git a/src/extensions/scenario/scenarioext.cpp b/src/extensions/scenario/scenarioext.cpp index f9613dd55..9dfe726d7 100644 --- a/src/extensions/scenario/scenarioext.cpp +++ b/src/extensions/scenario/scenarioext.cpp @@ -28,6 +28,8 @@ #include "scenarioext.h" #include "tibsun_globals.h" #include "tibsun_defines.h" +#include "iomap.h" +#include "waypoint.h" #include "ccini.h" #include "noinit.h" #include "swizzle.h" @@ -42,7 +44,8 @@ * @author: CCHyper */ ScenarioClassExtension::ScenarioClassExtension(const ScenarioClass *this_ptr) : - GlobalExtensionClass(this_ptr) + GlobalExtensionClass(this_ptr), + Waypoint(NEW_WAYPOINT_COUNT) { //if (this_ptr) EXT_DEBUG_TRACE("ScenarioClassExtension::ScenarioClassExtension - 0x%08X\n", (uintptr_t)(ThisPtr)); @@ -73,6 +76,11 @@ ScenarioClassExtension::ScenarioClassExtension(const NoInitClass &noinit) : ScenarioClassExtension::~ScenarioClassExtension() { //EXT_DEBUG_TRACE("ScenarioClassExtension::~ScenarioClassExtension - 0x%08X\n", (uintptr_t)(ThisPtr)); + + /** + * Free up the cell array. + */ + Waypoint.Clear(); } @@ -172,6 +180,11 @@ void ScenarioClassExtension::Init_Clear() ini.Load(CCFileClass("TUTORIAL.INI"), false); Read_Tutorial_INI(ini); } + + /** + * Clear all waypoint values, preparing for scenario loading. + */ + Clear_All_Waypoints(); } @@ -255,3 +268,276 @@ bool ScenarioClassExtension::Read_Tutorial_INI(CCINIClass &ini, bool log) return true; } + + +/** + * Get the cell value of a waypoint location. + * + * @author: CCHyper + */ +Cell ScenarioClassExtension::Get_Waypoint_Cell(WaypointType wp) const +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Get_Waypoint_Cell - 0x%08X\n", (uintptr_t)(This())); + ASSERT_FATAL(wp < Waypoint.Length()); + + return Waypoint[wp]; +} + + +/** + * Get the cell pointer of a waypoint location. + * + * @author: CCHyper + */ +CellClass *ScenarioClassExtension::Get_Waypoint_CellPtr(WaypointType wp) const +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Get_Waypoint_CellPtr - 0x%08X\n", (uintptr_t)(This())); + ASSERT_FATAL(wp < Waypoint.Length()); + + return &Map[Waypoint[wp]]; +} + + +/** + * Get the coordinate of a waypoint location. + * + * @author: CCHyper + */ +Coordinate ScenarioClassExtension::Get_Waypoint_Coord(WaypointType wp) const +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Get_Waypoint_Coord - 0x%08X\n", (uintptr_t)(This())); + ASSERT_FATAL(wp < Waypoint.Length()); + + CellClass *cell = &Map[Waypoint[wp]]; + Coordinate coord = cell->Center_Coord(); + return coord; +} + + +/** + * Get the coordinate of a waypoint location. + * + * #NOTE: The coordinate is adjusted by the bridge height if the waypoint is on a bridge cell. + * + * @author: CCHyper + */ +Coordinate ScenarioClassExtension::Get_Waypoint_Coord_Height(WaypointType wp) const +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Get_Waypoint_Coord_Height - 0x%08X\n", (uintptr_t)(This())); + ASSERT_FATAL(wp < Waypoint.Length()); + + CellClass *cell = &Map[Waypoint[wp]]; + Coordinate coord = cell->Center_Coord(); + + if (cell->Bit2_16 && cell->Bit2_64) { + coord.Z += BridgeCellHeight; + } + + return coord; +} + + +/** + * Set the waypoint location from the cell value. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Set_Waypoint_Cell(WaypointType wp, Cell &cell) +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Get_Waypoint_Cell - 0x%08X\n", (uintptr_t)(This())); + ASSERT_FATAL(wp < Waypoint.Length()); + + Waypoint[wp] = cell; +} + + +/** + * Set the waypoint location from a coordinate value. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Set_Waypoint_Coord(WaypointType wp, Coordinate &coord) +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Set_Waypoint_Coord - 0x%08X\n", (uintptr_t)(This())); + + Waypoint[wp] = Coord_Cell(coord); +} + + +/** + * Is this waypoint a valid cell location? + * + * @author: CCHyper + */ +bool ScenarioClassExtension::Is_Valid_Waypoint(WaypointType wp) const +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Is_Valid_Waypoint - 0x%08X\n", (uintptr_t)(This())); + ASSERT_FATAL(wp < Waypoint.Length()); + + return (wp >= WAYPOINT_FIRST && wp < Waypoint.Length()) ? Waypoint[wp] : false; +} + + +/** + * Clear the waypoint value. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Clear_Waypoint(WaypointType wp) +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Clear_Waypoint - 0x%08X\n", (uintptr_t)(This())); + ASSERT_FATAL(wp < Waypoint.Length()); + + Waypoint[wp] = Cell(); +} + + +/** + * Clear all the waypoints, emptying the list. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Clear_All_Waypoints() +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Clear_All_Waypoints - 0x%08X\n", (uintptr_t)(This())); + + /** + * Assume that whatever the contents of the VectorClass are is garbage + * (it may have been loaded from a save-game file), so zero it out first. + */ + new (&Waypoint) VectorClass; + Waypoint.Resize(NEW_WAYPOINT_COUNT); +} + + +/** + * Read the waypoint locations from the ini database. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Read_Waypoint_INI(CCINIClass &ini) +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Read_Waypoint_INI - 0x%08X\n", (uintptr_t)(This())); + + static const char * const WAYNAME = "Waypoints"; + + char entry[32]; + int valid_count = 0; + + /** + * Read the Waypoint entries. + */ + for (WaypointType wp = WAYPOINT_FIRST; wp < Waypoint.Length(); ++wp) { + + /** + * Get a waypoint entry. + */ + std::snprintf(entry, sizeof(entry), "%d", wp); + int value = ini.Get_Int(WAYNAME, entry, 0); + + /** + * Skip invalid entries. + */ + if (!value) { + continue; + } + + ++valid_count; + + /** + * Convert this value to an actual map cell location. + */ + Cell cell; + cell.X = value % 1000; + cell.Y = value / 1000; + + int wp_num = std::strtol(entry, nullptr, 10); + + switch (wp_num) { + case WAYPOINT_HOME: + DEV_DEBUG_INFO("Scenario: Waypoint '%d' (HOME), location '%d,%d'.\n", wp_num, cell.X, cell.Y); + break; + case WAYPOINT_REINF: + DEV_DEBUG_INFO("Scenario: Waypoint '%d' (REINF), location '%d,%d'.\n", wp_num, cell.X, cell.Y); + break; + case WAYPOINT_SPECIAL: + DEV_DEBUG_INFO("Scenario: Waypoint '%d' (SPECIAL), location '%d,%d'.\n", wp_num, cell.X, cell.Y); + break; + default: + DEV_DEBUG_INFO("Scenario: Waypoint '%d', location '%d,%d'.\n", wp_num, cell.X, cell.Y); + break; + }; + + /** + * Store the waypoint value. + */ + Waypoint[wp_num] = cell; + + /** + * If the cell location is valid, flag the cell on the map as a waypoint holder. + */ + if (wp_num >= 0 && cell) { +#ifndef NDEBUG + DEV_DEBUG_INFO("Scenario: Cell %d,%d -> IsWaypoint = true.\n", cell.X, cell.Y); +#endif + Map[cell].IsWaypoint = true; + } + + } + + if (valid_count > 0) DEV_DEBUG_INFO("Scenario: Read waypoint '%d' waypoints.\n", valid_count); +} + + +/** + * Write the waypoint locations to the ini database. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Write_Waypoint_INI(CCINIClass &ini) +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Write_Waypoint_INI - 0x%08X\n", (uintptr_t)(This())); + + static char const * const WAYNAME = "Waypoints"; + + char entry[32]; + int valid_count = 0; + + /** + * Clear any existing section from the ini database. + */ + ini.Clear(WAYNAME); + + /** + * Save the Waypoint entries. + */ + for (WaypointType wp = WAYPOINT_FIRST; wp < Waypoint.Length(); ++wp) { + if (Is_Valid_Waypoint(wp)) { + std::snprintf(entry, sizeof(entry), "%d", wp); + int value = Waypoint[wp].X + 1000 * Waypoint[wp].Y; + ini.Put_Int(WAYNAME, entry, value); + ++valid_count; + } + } + + if (valid_count > 0) DEV_DEBUG_INFO("Scenario: Wrote '%d' waypoints.\n", valid_count); +} + + +/** + * Returns the waypoint number as a string. + * + * @author: CCHyper + */ +const char * ScenarioClassExtension::Waypoint_As_String(WaypointType wp) const +{ + //EXT_DEBUG_TRACE("ScenarioClassExtension::Waypoint_As_String - 0x%08X\n", (uintptr_t)(This())); + + for (WaypointType wp = WAYPOINT_FIRST; wp < Waypoint.Length(); ++wp) { + if (Is_Valid_Waypoint(wp)) { + return ::Waypoint_As_String(wp); + } + } + + return ""; +} diff --git a/src/extensions/scenario/scenarioext.h b/src/extensions/scenario/scenarioext.h index 5d3735c85..266e48989 100644 --- a/src/extensions/scenario/scenarioext.h +++ b/src/extensions/scenario/scenarioext.h @@ -30,6 +30,7 @@ #include "always.h" #include "extension.h" #include "scenario.h" +#include "vector.h" class ScenarioClassExtension final : public GlobalExtensionClass @@ -55,6 +56,32 @@ class ScenarioClassExtension final : public GlobalExtensionClass bool Read_Tutorial_INI(CCINIClass &ini, bool log = false); - public: + Cell Get_Waypoint_Cell(WaypointType wp) const; + CellClass * Get_Waypoint_CellPtr(WaypointType wp) const; + Coordinate Get_Waypoint_Coord(WaypointType wp) const; + Coordinate Get_Waypoint_Coord_Height(WaypointType wp) const; + + void Set_Waypoint_Cell(WaypointType wp, Cell &cell); + void Set_Waypoint_Coord(WaypointType wp, Coordinate &coord); + + bool Is_Valid_Waypoint(WaypointType wp) const; + void Clear_Waypoint(WaypointType wp); + + void Clear_All_Waypoints(); + void Read_Waypoint_INI(CCINIClass &ini); + void Write_Waypoint_INI(CCINIClass &ini); + + const char *Waypoint_As_String(WaypointType wp) const; + + public: + /* + ** This is an vector of waypoints; each waypoint corresponds to a letter of + ** the alphabet, and points to a cell position. + * + ** The CellClass has a bit that tells if that cell has a waypoint attached to + ** it; the only way to find which waypoint it is, is to scan this array. This + ** shouldn't be needed often; usually, you know the waypoint & you want the "Cell". + */ + VectorClass Waypoint; }; diff --git a/src/extensions/scenario/scenarioext_hooks.cpp b/src/extensions/scenario/scenarioext_hooks.cpp index de21bd149..fb5431371 100644 --- a/src/extensions/scenario/scenarioext_hooks.cpp +++ b/src/extensions/scenario/scenarioext_hooks.cpp @@ -46,6 +46,36 @@ #include "hooker_macros.h" +/** + * A fake class for implementing new member functions which allow + * access to the "this" pointer of the intended class. + * + * @note: This must not contain a constructor or destructor! + * @note: All functions must be prefixed with "_" to prevent accidental virtualization. + */ +class ScenarioClassExt final : public ScenarioClass +{ + public: + Cell _Get_Waypoint_Cell(WaypointType wp) const { return ScenExtension->Get_Waypoint_Cell(wp); } + CellClass *_Get_Waypoint_CellPtr(WaypointType wp) const { return ScenExtension->Get_Waypoint_CellPtr(wp); } + Coordinate _Get_Waypoint_Coord(WaypointType wp) const { return ScenExtension->Get_Waypoint_Coord(wp); } + Coordinate _Get_Waypoint_Coord_Height(WaypointType wp) const { return ScenExtension->Get_Waypoint_Coord_Height(wp); } + + void _Set_Waypoint_Cell(WaypointType wp, Cell &cell) { ScenExtension->Set_Waypoint_Cell(wp, cell); } + void _Set_Waypoint_Coord(WaypointType wp, Coordinate &coord) { ScenExtension->Set_Waypoint_Coord(wp, coord); } + + bool _Is_Valid_Waypoint(WaypointType wp) const { return ScenExtension->Is_Valid_Waypoint(wp); } + void _Clear_Waypoint(WaypointType wp) { ScenExtension->Clear_Waypoint(wp); } + + void _Clear_All_Waypoints() { ScenExtension->Clear_All_Waypoints(); } + + void _Read_Waypoint_INI(CCINIClass &ini) { ScenExtension->Read_Waypoint_INI(ini); } + void _Write_Waypoint_INI(CCINIClass &ini) { ScenExtension->Write_Waypoint_INI(ini); } + + const char *_Waypoint_As_String(WaypointType wp) const { return ScenExtension->Waypoint_As_String(wp); } +}; + + /** * Process additions to the Rules data from the input file. * @@ -178,4 +208,23 @@ void ScenarioClassExtension_Hooks() Patch_Jump(0x005DC9D4, &_Do_Win_Skip_MPlayer_Score_Screen_Patch); Patch_Jump(0x005DCD92, &_Do_Lose_Skip_MPlayer_Score_Screen_Patch); Patch_Jump(0x005DD8D5, &_Read_Scenario_INI_MPlayer_INI_Patch); + + /** + * #issue-71 + * + * Increases the amount of available waypoints (see ScenarioClassExtension for implementation). + * + * @author: CCHyper + */ + Patch_Jump(0x005E1460, &ScenarioClassExt::_Get_Waypoint_Cell); + Patch_Jump(0x005E1480, &ScenarioClassExt::_Get_Waypoint_CellPtr); + Patch_Jump(0x005E14A0, &ScenarioClassExt::_Get_Waypoint_Coord); + Patch_Jump(0x005E1500, &ScenarioClassExt::_Clear_All_Waypoints); + Patch_Jump(0x005E1520, &ScenarioClassExt::_Is_Valid_Waypoint); + Patch_Jump(0x005E1560, &ScenarioClassExt::_Read_Waypoint_INI); + Patch_Jump(0x005E1630, &ScenarioClassExt::_Write_Waypoint_INI); + Patch_Jump(0x005E16C0, &ScenarioClassExt::_Clear_Waypoint); + Patch_Jump(0x005E16E0, &ScenarioClassExt::_Set_Waypoint_Coord); + Patch_Jump(0x005E1700, &ScenarioClassExt::_Get_Waypoint_Coord); + Patch_Jump(0x005E1720, &ScenarioClassExt::_Waypoint_As_String); } diff --git a/src/vinifera/vinifera_defines.h b/src/vinifera/vinifera_defines.h index c3e7599af..d5860ed94 100644 --- a/src/vinifera/vinifera_defines.h +++ b/src/vinifera/vinifera_defines.h @@ -127,3 +127,9 @@ #define UUID_FOGGEDOBJECT_EXTENSION "7D9C5263-465F-42CE-AD81-5C057B52226F" #define UUID_ALPHASHAPE_EXTENSION "4C8171D5-E7A7-43D1-80F3-0C285CF6B352" #define UUID_VEINHOLEMONSTER_EXTENSION "4AD76F43-090A-44BF-BB1A-5BFDE52BC842" + + +/** + * The maximum amount of waypoints available for a scenario to use. + */ +#define NEW_WAYPOINT_COUNT 702 // "ZZ"