diff --git a/Generals/Code/GameEngine/CMakeLists.txt b/Generals/Code/GameEngine/CMakeLists.txt index a6d33955a42..316ce75f99a 100644 --- a/Generals/Code/GameEngine/CMakeLists.txt +++ b/Generals/Code/GameEngine/CMakeLists.txt @@ -132,6 +132,7 @@ set(GAMEENGINE_SRC # Include/Common/XferSave.h Include/GameClient/Anim2D.h Include/GameClient/AnimateWindowManager.h + Include/GameClient/ChallengeGenerals.h Include/GameClient/CampaignManager.h Include/GameClient/CDCheck.h Include/GameClient/ClientInstance.h @@ -657,6 +658,7 @@ set(GAMEENGINE_SRC Source/GameClient/GlobalLanguage.cpp Source/GameClient/GraphDraw.cpp Source/GameClient/GUI/AnimateWindowManager.cpp + Source/GameClient/GUI/ChallengeGenerals.cpp Source/GameClient/GUI/ControlBar/ControlBar.cpp Source/GameClient/GUI/ControlBar/ControlBarBeacon.cpp Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp diff --git a/Generals/Code/GameEngine/Include/Common/GameCommon.h b/Generals/Code/GameEngine/Include/Common/GameCommon.h index 7163c0753c5..34e26d9b065 100644 --- a/Generals/Code/GameEngine/Include/Common/GameCommon.h +++ b/Generals/Code/GameEngine/Include/Common/GameCommon.h @@ -47,6 +47,10 @@ #pragma once +#define DONT_ALLOW_DEBUG_CHEATS_IN_RELEASE ///< Take of the DONT to get cheats back in to release + +//#define _CAMPEA_DEMO + // ---------------------------------------------------------------------------------------------- #include "Lib/BaseType.h" #include "WWCommon.h" @@ -54,7 +58,7 @@ // ---------------------------------------------------------------------------------------------- #if defined(RTS_DEBUG) - #define NO_DUMP_PERF_STATS + #define DUMP_PERF_STATS #else #define NO_DUMP_PERF_STATS #endif @@ -119,6 +123,18 @@ enum #error "this is the wrong size" #endif +// ---------------------------------------------------------------------------------------------- +enum +{ + MAX_GLOBAL_GENERAL_TYPES = 9, ///< number of playable General Types, not including the boss) + + /// The start of the playable global generals playertemplates + GLOBAL_GENERAL_BEGIN = 5, + + /// The end of the playable global generals + GLOBAL_GENERAL_END = (GLOBAL_GENERAL_BEGIN + MAX_GLOBAL_GENERAL_TYPES - 1) +}; + //------------------------------------------------------------------------------------------------- enum GameDifficulty CPP_11(: Int) { @@ -207,6 +223,7 @@ enum CommandSourceType CPP_11(: Int) CMD_FROM_SCRIPT, CMD_FROM_AI, CMD_FROM_DOZER, // Special rare command when the dozer originates a command to attack a mine. Mines are not ai-attackable, and it seems deceitful for the dozer to generate a player or script command. jba. + CMD_DEFAULT_SWITCH_WEAPON, // Special case: A weapon that can be chosen -- this is the default case (machine gun vs flashbang). COMMAND_SOURCE_TYPE_COUNT }; diff --git a/Generals/Code/GameEngine/Include/Common/GlobalData.h b/Generals/Code/GameEngine/Include/Common/GlobalData.h index 83dae4e9ecf..2ee139bef3a 100644 --- a/Generals/Code/GameEngine/Include/Common/GlobalData.h +++ b/Generals/Code/GameEngine/Include/Common/GlobalData.h @@ -280,6 +280,8 @@ class GlobalData : public SubsystemInterface #ifdef DUMP_PERF_STATS Bool m_dumpPerformanceStatistics; + Bool m_dumpStatsAtInterval;///< should I automatically dum stats every in N frames + Int m_statsInterval; ///< if so, how many is N? #endif Bool m_forceBenchmark; ///. +*/ + +//////////////////////////////////////////////////////////////////////////////// +// // +// (c) 2001-2003 Electronic Arts Inc. // +// // +//////////////////////////////////////////////////////////////////////////////// + +// FILE: ChallengeGenerals.h ////////////////////////////////////////////////////////////////////// +// Author: Steve Copeland, 6/24/2003 +// Desc: This is a manager for data pertaining to the Generals' Challenge personas and related GUI. +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +// INCLUDES ////////////////////////////////////////////////////////////////////////////////// +#include "Common/GameType.h" +#include "Common/INI.h" +#include "Common/Overridable.h" + +// DEFINES //////////////////////////////////////////////////////////////////////////////////////// +//static const Int NUM_GENERALS = 12; // ChallengeMenu.wnd dependent +#define NUM_GENERALS (12) + +// FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// +class Image; + +// CLASS DEFINITIONS ////////////////////////////////////////////////////////////////////////////// +class GeneralPersona +{ + friend class ChallengeGenerals; + +private: + Bool m_bStartsEnabled; + AsciiString m_strBioName; + AsciiString m_strBioDOB; + AsciiString m_strBioBirthplace; + AsciiString m_strBioStrategy; + AsciiString m_strBioRank; + AsciiString m_strBioBranch; + AsciiString m_strBioClassNumber; + Image *m_imageBioPortraitSmall; + Image *m_imageBioPortraitLarge; + AsciiString m_strCampaign; + AsciiString m_strPlayerTemplateName; + AsciiString m_strPortraitMovieLeftName; + AsciiString m_strPortraitMovieRightName; + Image *m_imageDefeated; + Image *m_imageVictorious; + AsciiString m_strDefeated; + AsciiString m_strVictorious; + AsciiString m_strSelectionSound; + AsciiString m_strTauntSound1; + AsciiString m_strTauntSound2; + AsciiString m_strTauntSound3; + AsciiString m_strWinSound; + AsciiString m_strLossSound; + AsciiString m_strPreviewSound; + AsciiString m_strNameSound ; + + +public: + GeneralPersona( void ) : + m_imageBioPortraitSmall(nullptr), + m_imageBioPortraitLarge(nullptr) + { + } +// ~GeneralPersona( void ); + + Bool isStartingEnabled() const { return m_bStartsEnabled; } + const AsciiString& getBioName() const { return m_strBioName; } + const AsciiString& getBioDOB() const { return m_strBioDOB; } + const AsciiString& getBioBirthplace() const { return m_strBioBirthplace; } + const AsciiString& getBioStrategy() const { return m_strBioStrategy; } + const AsciiString& getBioRank() const { return m_strBioRank; } + const AsciiString& getBioClassNumber() const { return m_strBioClassNumber; } + const AsciiString& getBioBranch() const { return m_strBioBranch; } + const Image *getBioPortraitSmall() const { return m_imageBioPortraitSmall; } + const Image *getBioPortraitLarge() const { return m_imageBioPortraitLarge; } + const AsciiString& getPortraitMovieLeftName() const { return m_strPortraitMovieLeftName; } + const AsciiString& getPortraitMovieRightName() const { return m_strPortraitMovieRightName; } + const AsciiString& getCampaign() const { return m_strCampaign; } + const AsciiString& getPlayerTemplateName() const { return m_strPlayerTemplateName; } // template name, as parsed in from ini + const Image *getImageDefeated() const { return m_imageDefeated; } + const Image *getImageVictorious() const { return m_imageVictorious; } + const AsciiString& getStringDefeated() const { return m_strDefeated; } + const AsciiString& getStringVictorious() const { return m_strVictorious; } + const AsciiString& getSelectionSound() const { return m_strSelectionSound; } + const AsciiString& getRandomTauntSound() const { + switch (rand()%3) // don't care about distribution or exactly how random this is + { + case 0: return m_strTauntSound1; + case 1: return m_strTauntSound2; + } + return m_strTauntSound3; + } + const AsciiString& getWinSound() const { return m_strWinSound; } + const AsciiString& getLossSound() const { return m_strLossSound; } + const AsciiString& getPreviewSound() const { return m_strPreviewSound; } + const AsciiString& getNameSound() const { return m_strNameSound; } +}; + + +class ChallengeGenerals +{ + +private: + /*const*/ GeneralPersona m_position[ NUM_GENERALS ]; + Int m_PlayerTemplateNum; // the template number as ThePlayerTemplateStore has it + GameDifficulty m_currentDifficulty; // the last selected game difficulty for the challenge generals + + static void parseGeneralPersona( INI* ini, void *instance, void *store, const void *userData ); + +public: + ChallengeGenerals( void ); + ~ChallengeGenerals( void ); + + void init( void ); + const GeneralPersona* getChallengeGenerals() const { return m_position; } + const FieldParse* getFieldParse( void ) const { return s_fieldParseTable; } // for INI file parsing + const GeneralPersona* getPlayerGeneralByCampaignName( AsciiString name ) const; + const GeneralPersona* getGeneralByGeneralName( AsciiString name ) const; + const GeneralPersona* getGeneralByTemplateName( AsciiString name ) const; + + void setCurrentPlayerTemplateNum( Int playerTemplateNum) { m_PlayerTemplateNum = playerTemplateNum; } + Int getCurrentPlayerTemplateNum( void ) { return m_PlayerTemplateNum; } + + void setCurrentDifficulty( GameDifficulty diff ) { m_currentDifficulty = diff; } + GameDifficulty getCurrentDifficulty( void ) { return m_currentDifficulty; } +protected: + static const FieldParse s_fieldParseTable[]; + +}; + + + +// EXTERNALS ////////////////////////////////////////////////////////////////////////////////////// +extern ChallengeGenerals *TheChallengeGenerals; +extern ChallengeGenerals *createChallengeGenerals( void ); diff --git a/Generals/Code/GameEngine/Include/GameLogic/AI.h b/Generals/Code/GameEngine/Include/GameLogic/AI.h index 1ac8581523a..acb1b5da81e 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/AI.h +++ b/Generals/Code/GameEngine/Include/GameLogic/AI.h @@ -308,10 +308,11 @@ class Waypoint; class Team; class Weapon; -// TheSuperHackers @build xezon 22/03/2025 Renames AI_PASSIVE to not conflict with macro in ws2def.h +// TheSuperHackers @build xezon 17/03/2025 Renames AI_PASSIVE to not conflict with macro in ws2def.h // Note - written out in save/load xfer and .map files, don't change these numbers. -enum AttitudeType CPP_11(: Int) { +enum AttitudeType CPP_11(: Int) +{ ATTITUDE_SLEEP = -2, ATTITUDE_PASSIVE=-1, ATTITUDE_NORMAL=0, @@ -331,6 +332,7 @@ static const char *const TheCommandSourceMaskNames[] = "FROM_SCRIPT", "FROM_AI", "FROM_DOZER", //don't use this + "DEFAULT_SWITCH_WEAPON", //unit will pick this weapon when normal logic fails. nullptr }; diff --git a/Generals/Code/GameEngine/Include/GameLogic/ScriptEngine.h b/Generals/Code/GameEngine/Include/GameLogic/ScriptEngine.h index b5f4785667a..12d3fbd3f67 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/ScriptEngine.h +++ b/Generals/Code/GameEngine/Include/GameLogic/ScriptEngine.h @@ -352,6 +352,11 @@ class ScriptEngine : public SubsystemInterface, //#if defined(RTS_DEBUG) void debugVictory( void ); //#endif + + + static void parseScriptAction( INI* ini ); + static void parseScriptCondition( INI* ini ); + protected: // snapshot methods diff --git a/Generals/Code/GameEngine/Source/Common/GlobalData.cpp b/Generals/Code/GameEngine/Source/Common/GlobalData.cpp index cf0aff23aa7..a6f4cc49b04 100644 --- a/Generals/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/Generals/Code/GameEngine/Source/Common/GlobalData.cpp @@ -1019,6 +1019,8 @@ GlobalData::GlobalData() #ifdef DUMP_PERF_STATS m_dumpPerformanceStatistics = FALSE; + m_dumpStatsAtInterval = FALSE; + m_statsInterval = 30; #endif m_forceBenchmark = FALSE; /// 1) { strlcpy(buff, &token[1], ARRAY_SIZE(buff)); } - token = getNextToken(getSepsQuote()); - - if (strlen(token) > 1 && token[1] != '\t') - { - strlcat(buff, " ", ARRAY_SIZE(buff)); + token = getNextTokenOrNull(getSepsQuote()); + if (token) { + if (strlen(token) > 1 && token[1] != '\t') + { + strlcat(buff, " ", ARRAY_SIZE(buff)); + } + strlcat(buff, token, ARRAY_SIZE(buff)); + result.set(buff); + } else { + Int len = strlen(buff); + if (len && buff[len-1] == '"') { // strip off trailing quote jba. [2/12/2003] + buff[len-1] = 0; + } + result.set(buff); } - strlcat(buff, token, ARRAY_SIZE(buff)); - result.set(buff); } } return result; @@ -1395,7 +1407,10 @@ void INI::parseSpecialPowerTemplate( INI* ini, void * /*instance*/, void *store, } const SpecialPowerTemplate *sPowerT = TheSpecialPowerStore->findSpecialPowerTemplate( AsciiString( token ) ); - DEBUG_ASSERTCRASH( sPowerT || stricmp( token, "None" ) == 0, ("Specialpower %s not found!",token) ); + if( !sPowerT && stricmp( token, "None" ) != 0 ) + { + DEBUG_CRASH( ("[LINE: %d in '%s'] Specialpower %s not found!", ini->getLineNum(), ini->getFilename().str(), token) ); + } typedef const SpecialPowerTemplate* ConstSpecialPowerTemplatePtr; ConstSpecialPowerTemplatePtr* theSpecialPowerTemplate = (ConstSpecialPowerTemplatePtr *)store; diff --git a/Generals/Code/GameEngine/Source/Common/INI/INIMultiplayer.cpp b/Generals/Code/GameEngine/Source/Common/INI/INIMultiplayer.cpp index 66d50d48f06..e2145deaeec 100644 --- a/Generals/Code/GameEngine/Source/Common/INI/INIMultiplayer.cpp +++ b/Generals/Code/GameEngine/Source/Common/INI/INIMultiplayer.cpp @@ -78,3 +78,6 @@ void INI::parseMultiplayerColorDefinition( INI* ini ) multiplayerColorDefinition->setNightColor(multiplayerColorDefinition->getRGBNightValue()); } +void INI::parseMultiplayerStartingMoneyChoiceDefinition( INI* ini ) +{ +} diff --git a/Generals/Code/GameEngine/Source/Common/System/SubsystemInterface.cpp b/Generals/Code/GameEngine/Source/Common/System/SubsystemInterface.cpp index 02c6572acaf..d7aad22f1cf 100644 --- a/Generals/Code/GameEngine/Source/Common/System/SubsystemInterface.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/SubsystemInterface.cpp @@ -43,7 +43,9 @@ SubsystemInterface::SubsystemInterface() :m_curDrawTime(0), m_startDrawTimeConsumed(0), m_startTimeConsumed(0), -m_curUpdateTime(0) +m_curUpdateTime(0), +m_dumpUpdate(false), +m_dumpDraw(false) #endif { if (TheSubsystemList) { @@ -60,6 +62,7 @@ SubsystemInterface::~SubsystemInterface() } #ifdef DUMP_PERF_STATS +static const Real MIN_TIME_THRESHOLD = 0.0002f; // .2 msec. [8/13/2003] void SubsystemInterface::UPDATE(void) { __int64 startTime64; @@ -72,8 +75,11 @@ void SubsystemInterface::UPDATE(void) m_curUpdateTime = ((double)(endTime64-startTime64))/((double)(freq64)); Real subTime = s_msConsumed - m_startTimeConsumed; if (m_name.isEmpty()) return; - if (m_curUpdateTime > 0.00001) { - //DEBUG_LOG(("Subsys %s total time %.2f, subTime %.2f, net time %.2f", + if (m_curUpdateTime>MIN_TIME_THRESHOLD) { + m_dumpUpdate = true; + } + if (m_curUpdateTime > MIN_TIME_THRESHOLD/10.0f) { + //DLOG(Debug::Format("Subsys %s total time %.2f, subTime %.2f, net time %.2f\n", // m_name.str(), m_curUpdateTime*1000, subTime*1000, (m_curUpdateTime-subTime)*1000 )); m_curUpdateTime -= subTime; @@ -95,8 +101,11 @@ void SubsystemInterface::DRAW(void) m_curDrawTime = ((double)(endTime64-startTime64))/((double)(freq64)); Real subTime = s_msConsumed - m_startDrawTimeConsumed; if (m_name.isEmpty()) return; - if (m_curDrawTime > 0.00001) { - //DEBUG_LOG(("Subsys %s total time %.2f, subTime %.2f, net time %.2f", + if (m_curDrawTime>MIN_TIME_THRESHOLD) { + m_dumpDraw = true; + } + if (m_curDrawTime > MIN_TIME_THRESHOLD/10.0f) { + //DLOG(Debug::Format("Subsys %s total time %.2f, subTime %.2f, net time %.2f\n", // m_name.str(), m_curUpdateTime*1000, subTime*1000, (m_curUpdateTime-subTime)*1000 )); m_curDrawTime -= subTime; @@ -203,7 +212,7 @@ AsciiString SubsystemInterfaceList::dumpTimesForAll() { SubsystemInterface* sys = *it; total += sys->getUpdateTime(); - if (sys->getUpdateTime()>0.00001f) { + if (sys->doDumpUpdate()) { AsciiString curLine; curLine.format(" Time %02.2f MS update() %s \n", sys->getUpdateTime()*1000.0f, sys->getName().str()); buffer.concat(curLine); @@ -211,7 +220,7 @@ AsciiString SubsystemInterfaceList::dumpTimesForAll() misc += sys->getUpdateTime(); } total += sys->getDrawTime(); - if (sys->getDrawTime()>0.00001f) { + if (sys->doDumpDraw()) { AsciiString curLine; curLine.format(" Time %02.2f MS draw () %s \n", sys->getDrawTime()*1000.0f, sys->getName().str()); buffer.concat(curLine); diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/ChallengeGenerals.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/ChallengeGenerals.cpp new file mode 100644 index 00000000000..eb5adf62d1b --- /dev/null +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/ChallengeGenerals.cpp @@ -0,0 +1,160 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +//////////////////////////////////////////////////////////////////////////////// +// // +// (c) 2001-2003 Electronic Arts Inc. // +// // +//////////////////////////////////////////////////////////////////////////////// + +// FILE: ChallengeGenerals.h ////////////////////////////////////////////////////////////////////// +// Author: Steve Copeland, 6/24/2003 +// Desc: This is a manager for data pertaining to the Generals' Challenge personas and related GUI. +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine + +#include "GameClient/ChallengeGenerals.h" + + +ChallengeGenerals *TheChallengeGenerals = nullptr; + +ChallengeGenerals *createChallengeGenerals( void ) +{ + return MSGNEW("ChallengeGenerals") ChallengeGenerals; +} + + +ChallengeGenerals::ChallengeGenerals() +{ + //ctor +} + + +ChallengeGenerals::~ChallengeGenerals() +{ + //dtor +} + + +void ChallengeGenerals::init( void ) +{ + INI ini; + ini.loadFileDirectory( "Data\\INI\\ChallengeMode", INI_LOAD_OVERWRITE, nullptr ); +} + + +void ChallengeGenerals::parseGeneralPersona(INI *ini, void *instance, void *store, const void *userData) +{ + static const FieldParse dataFieldParse[] = + { + { "StartsEnabled", INI::parseBool, nullptr, offsetof( GeneralPersona, m_bStartsEnabled ) }, + { "BioNameString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strBioName ) }, + { "BioDOBString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strBioDOB ) }, + { "BioBirthplaceString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strBioBirthplace ) }, + { "BioStrategyString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strBioStrategy ) }, + { "BioRankString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strBioRank ) }, + { "BioBranchString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strBioBranch ) }, + { "BioClassNumberString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strBioClassNumber ) }, + { "BioPortraitSmall", INI::parseMappedImage, nullptr, offsetof( GeneralPersona, m_imageBioPortraitSmall ) }, + { "BioPortraitLarge", INI::parseMappedImage, nullptr, offsetof( GeneralPersona, m_imageBioPortraitLarge ) }, + { "Campaign", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strCampaign ) }, + { "PlayerTemplate", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strPlayerTemplateName ) }, + { "PortraitMovieLeftName", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strPortraitMovieLeftName ) }, + { "PortraitMovieRightName", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strPortraitMovieRightName ) }, + { "DefeatedImage", INI::parseMappedImage, nullptr, offsetof( GeneralPersona, m_imageDefeated ) }, + { "VictoriousImage", INI::parseMappedImage, nullptr, offsetof( GeneralPersona, m_imageVictorious ) }, + { "DefeatedString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strDefeated ) }, + { "VictoriousString", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strVictorious ) }, + { "SelectionSound", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strSelectionSound ) }, + { "TauntSound1", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strTauntSound1 ) }, + { "TauntSound2", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strTauntSound2 ) }, + { "TauntSound3", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strTauntSound3 ) }, + { "WinSound", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strWinSound ) }, + { "LossSound", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strLossSound ) }, + { "PreviewSound", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strPreviewSound ) }, + { "NameSound", INI::parseAsciiString, nullptr, offsetof( GeneralPersona, m_strNameSound ) }, + + { nullptr, nullptr, nullptr, 0 } + }; + ini->initFromINI(store, dataFieldParse); +} + + +const FieldParse ChallengeGenerals::s_fieldParseTable[] = +{ + { "GeneralPersona0", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[0] ) }, + { "GeneralPersona1", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[1] ) }, + { "GeneralPersona2", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[2] ) }, + { "GeneralPersona3", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[3] ) }, + { "GeneralPersona4", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[4] ) }, + { "GeneralPersona5", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[5] ) }, + { "GeneralPersona6", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[6] ) }, + { "GeneralPersona7", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[7] ) }, + { "GeneralPersona8", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[8] ) }, + { "GeneralPersona9", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[9] ) }, + { "GeneralPersona10", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[10] ) }, + { "GeneralPersona11", ChallengeGenerals::parseGeneralPersona, nullptr, offsetof( ChallengeGenerals, m_position[11] ) }, + { nullptr, nullptr, nullptr, 0 } +}; + + +//------------------------------------------------------------------------------------------------- +/** Parse Gen Challenge entries */ +//------------------------------------------------------------------------------------------------- +void INI::parseChallengeModeDefinition( INI* ini ) +{ + if( TheChallengeGenerals ) + { + ini->initFromINI( TheChallengeGenerals, TheChallengeGenerals->getFieldParse() ); + } +} + +const GeneralPersona* ChallengeGenerals::getPlayerGeneralByCampaignName( AsciiString name ) const +{ + for (Int i = 0; i < NUM_GENERALS; i++) + { + AsciiString campaignName = m_position[i].getCampaign(); + if (campaignName.compareNoCase( name.str() ) == 0) + return &m_position[i]; + } + DEBUG_CRASH(("Can't find General by Campaign Name")); + return nullptr; +} + +const GeneralPersona* ChallengeGenerals::getGeneralByGeneralName( AsciiString name ) const +{ + for (Int i = 0; i < NUM_GENERALS; i++) + { + AsciiString generalName = m_position[i].getBioName(); + if (generalName.compareNoCase( name.str() ) == 0) + return &m_position[i]; + } + return nullptr; +} + +const GeneralPersona* ChallengeGenerals::getGeneralByTemplateName( AsciiString name ) const +{ + for (Int i = 0; i < NUM_GENERALS; i++) + { + AsciiString templateName = m_position[i].getPlayerTemplateName(); + if (templateName.compareNoCase( name.str() ) == 0) + return &m_position[i]; + } + return nullptr; +} diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp index 4dd90299907..c1ec34fb967 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp @@ -348,6 +348,26 @@ void AttackPriorityInfo::loadPostProcess( void ) // ScriptEngine class +//------------------------------------------------------------------------------------------------- +/** Parse script action entry. The InternalName has to match the action's internal name, and then it +overrides the ui name and help text. If no entry is present in the ini file, the default code +initialized value in ScriptEngine::init() is used. jba*/ +//------------------------------------------------------------------------------------------------- +void ScriptEngine::parseScriptAction( INI* ini ) +{ +} + + +//------------------------------------------------------------------------------------------------- +/** Parse script condition entry. The InternalName has to match the condition's internal name, and then it +overrides the ui name and help text. If no entry is present in the ini file, the default code +initialized value in ScriptEngine::init() is used. jba*/ +//------------------------------------------------------------------------------------------------- +void ScriptEngine::parseScriptCondition( INI* ini ) +{ +} + + //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- ScriptEngine::ScriptEngine(): diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index f8bb76df4ba..12df34796f1 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -263,7 +263,7 @@ void StatDumpClass::dumpStats() fprintf( m_fp, " Video RAM: %d\n", Debug_Statistics::Get_Record_Texture_Size() - 1376256 ); // terrain stats - fprintf( m_fp, " 3-Way Blends: %d, Shoreline Blends: %d\n", TheTerrainRenderObject->getNumExtraBlendTiles(), TheTerrainRenderObject->getNumShoreLineTiles() ); + fprintf( m_fp, " 3-Way Blends: %d/%d, \n Shoreline Blends: %d/%d\n", TheTerrainRenderObject->getNumExtraBlendTiles(TRUE),TheTerrainRenderObject->getNumExtraBlendTiles(FALSE), TheTerrainRenderObject->getNumShoreLineTiles(TRUE),TheTerrainRenderObject->getNumShoreLineTiles(FALSE)); fprintf( m_fp, "\n" ); diff --git a/Generals/Code/Libraries/Include/Lib/BaseType.h b/Generals/Code/Libraries/Include/Lib/BaseType.h index 73b766ede9a..274f700f76e 100644 --- a/Generals/Code/Libraries/Include/Lib/BaseType.h +++ b/Generals/Code/Libraries/Include/Lib/BaseType.h @@ -65,7 +65,7 @@ inline Real deg2rad(Real rad) { return rad * (PI/180); } //----------------------------------------------------------------------------- // For twiddling bits //----------------------------------------------------------------------------- -// TheSuperHackers @build xezon 22/03/2025 Renames BitTest to BitIsSet to prevent conflict with BitTest macro from winnt.h +// TheSuperHackers @build xezon 17/03/2025 Renames BitTest to BitIsSet to prevent conflict with BitTest macro from winnt.h #define BitIsSet( x, i ) ( ( (x) & (i) ) != 0 ) #define BitSet( x, i ) ( (x) |= (i) ) #define BitClear( x, i ) ( (x ) &= ~(i) ) @@ -151,8 +151,13 @@ __forceinline float fast_float_ceil(float f) #define INT_TO_REAL(x) ((Real)(x)) // once we've ceiled/floored, trunc and round are identical, and currently, round is faster... (srj) +#if RTS_GENERALS /*&& RETAIL_COMPATIBLE_CRC*/ #define REAL_TO_INT_CEIL(x) (fast_float2long_round(ceilf(x))) #define REAL_TO_INT_FLOOR(x) (fast_float2long_round(floorf(x))) +#else +#define REAL_TO_INT_CEIL(x) (fast_float2long_round(fast_float_ceil(x))) +#define REAL_TO_INT_FLOOR(x) (fast_float2long_round(fast_float_floor(x))) +#endif #define FAST_REAL_TRUNC(x) fast_float_trunc(x) #define FAST_REAL_CEIL(x) fast_float_ceil(x) @@ -195,12 +200,13 @@ struct Coord2D } } - Real toAngle( void ); ///< turn 2D vector into angle (where angle 0 is down the +x axis) + Real toAngle( void ) const; ///< turn 2D vector into angle (where angle 0 is down the +x axis) }; -inline Real Coord2D::toAngle( void ) +inline Real Coord2D::toAngle( void ) const { +#if RTS_GENERALS /*&& RETAIL_COMPATIBLE_CRC*/ Coord2D vector; vector.x = x; @@ -242,7 +248,20 @@ inline Real Coord2D::toAngle( void ) // S is sign of angle - MSB return value; +#else + const Real len = length(); + if (len == 0.0f) + return 0.0f; + Real c = x/len; + // bound it in case of numerical error + if (c < -1.0f) + c = -1.0f; + else if (c > 1.0f) + c = 1.0f; + + return y < 0.0f ? -ACos(c) : ACos(c); +#endif } struct ICoord2D diff --git a/GeneralsMD/Code/GameEngine/Include/Common/SubsystemInterface.h b/GeneralsMD/Code/GameEngine/Include/Common/SubsystemInterface.h index f3cb210055d..f7a9802d67f 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/SubsystemInterface.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/SubsystemInterface.h @@ -154,7 +154,7 @@ class SubsystemInterfaceList void resetAll(); void shutdownAll(); #ifdef DUMP_PERF_STATS - AsciiString dumpTimesForAll(); + AsciiString dumpTimesForAll(); #endif private: diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/ChallengeGenerals.h b/GeneralsMD/Code/GameEngine/Include/GameClient/ChallengeGenerals.h index 73be82f6868..351d57d6ae1 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/ChallengeGenerals.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/ChallengeGenerals.h @@ -31,6 +31,7 @@ // INCLUDES ////////////////////////////////////////////////////////////////////////////////// #include "Common/GameType.h" +#include "Common/INI.h" #include "Common/Overridable.h" // DEFINES //////////////////////////////////////////////////////////////////////////////////////// @@ -38,7 +39,7 @@ #define NUM_GENERALS (12) // FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// - +class Image; // CLASS DEFINITIONS ////////////////////////////////////////////////////////////////////////////// class GeneralPersona diff --git a/GeneralsMD/Code/Libraries/Include/Lib/BaseType.h b/GeneralsMD/Code/Libraries/Include/Lib/BaseType.h index 383c2471418..7d3da6b9f2b 100644 --- a/GeneralsMD/Code/Libraries/Include/Lib/BaseType.h +++ b/GeneralsMD/Code/Libraries/Include/Lib/BaseType.h @@ -151,8 +151,13 @@ __forceinline float fast_float_ceil(float f) #define INT_TO_REAL(x) ((Real)(x)) // once we've ceiled/floored, trunc and round are identical, and currently, round is faster... (srj) +#if RTS_GENERALS /*&& RETAIL_COMPATIBLE_CRC*/ +#define REAL_TO_INT_CEIL(x) (fast_float2long_round(ceilf(x))) +#define REAL_TO_INT_FLOOR(x) (fast_float2long_round(floorf(x))) +#else #define REAL_TO_INT_CEIL(x) (fast_float2long_round(fast_float_ceil(x))) #define REAL_TO_INT_FLOOR(x) (fast_float2long_round(fast_float_floor(x))) +#endif #define FAST_REAL_TRUNC(x) fast_float_trunc(x) #define FAST_REAL_CEIL(x) fast_float_ceil(x) @@ -201,6 +206,49 @@ struct Coord2D inline Real Coord2D::toAngle( void ) const { +#if RTS_GENERALS /*&& RETAIL_COMPATIBLE_CRC*/ + Coord2D vector; + + vector.x = x; + vector.y = y; + + Real dist = (Real)sqrt(vector.x * vector.x + vector.y * vector.y); + + // normalize + if (dist == 0.0f) + return 0.0f; + + Coord2D dir; + dir.x = 1.0f; + dir.y = 0.0f; + + Real distInv = 1.0f / dist; + vector.x *= distInv; + vector.y *= distInv; + + // dot of two unit vectors is cos of angle + Real c = dir.x*vector.x + dir.y*vector.y; + + // bound it in case of numerical error + if (c < -1.0) + c = -1.0; + else if (c > 1.0) + c = 1.0; + + Real value = (Real)ACos( (Real)c ); + + // Determine sign by checking Z component of dir cross vector + // Note this is assumes 2D, and is identical to dotting the perpendicular of v with dir + Real perpZ = dir.x * vector.y - dir.y * vector.x; + if (perpZ < 0.0f) + value = -value; + + // note: to make this 3D, 'dir' and 'vector' can be normalized and dotted just as they are + // to test sign, compute N = dir X vector, then P = N x dir, then S = P . vector, where sign of + // S is sign of angle - MSB + + return value; +#else const Real len = length(); if (len == 0.0f) return 0.0f; @@ -213,6 +261,7 @@ inline Real Coord2D::toAngle( void ) const c = 1.0f; return y < 0.0f ? -ACos(c) : ACos(c); +#endif } struct ICoord2D