diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt index 69d218f593b..bf41a6c8708 100644 --- a/Core/GameEngine/CMakeLists.txt +++ b/Core/GameEngine/CMakeLists.txt @@ -1,5 +1,5 @@ set(GAMEENGINE_SRC -# Include/Common/AcademyStats.h + Include/Common/AcademyStats.h # Include/Common/ActionManager.h Include/Common/AddonCompat.h Include/Common/ArchiveFile.h @@ -621,7 +621,7 @@ set(GAMEENGINE_SRC Source/Common/RandomValue.cpp # Source/Common/Recorder.cpp Source/Common/ReplaySimulation.cpp -# Source/Common/RTS/AcademyStats.cpp + Source/Common/RTS/AcademyStats.cpp # Source/Common/RTS/ActionManager.cpp # Source/Common/RTS/Energy.cpp # Source/Common/RTS/Handicap.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/Common/AcademyStats.h b/Core/GameEngine/Include/Common/AcademyStats.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/Common/AcademyStats.h rename to Core/GameEngine/Include/Common/AcademyStats.h diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/AcademyStats.cpp b/Core/GameEngine/Source/Common/RTS/AcademyStats.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/Common/RTS/AcademyStats.cpp rename to Core/GameEngine/Source/Common/RTS/AcademyStats.cpp diff --git a/Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index 0d8003df21a..264ba6b9411 100644 --- a/Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -8789,11 +8789,7 @@ Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet PathfindCell *ignoreCell = getClippedCell(goalObj->getLayer(), goalObj->getPosition()); if ( (goalCell->getObstacleID()==ignoreCell->getObstacleID()) && (goalCell->getObstacleID() != INVALID_ID) ) { Object* newObstacle = TheGameLogic->findObjectByID(goalCell->getObstacleID()); -#if RTS_GENERALS - if (newObstacle != nullptr && newObstacle->isKindOf(KINDOF_AIRFIELD)) -#else if (newObstacle != nullptr && newObstacle->isKindOf(KINDOF_FS_AIRFIELD)) -#endif { m_ignoreObstacleID = goalCell->getObstacleID(); goalOnObstacle = true; diff --git a/Generals/Code/GameEngine/Include/Common/KindOf.h b/Generals/Code/GameEngine/Include/Common/KindOf.h index caa345ee57d..a7563273efa 100644 --- a/Generals/Code/GameEngine/Include/Common/KindOf.h +++ b/Generals/Code/GameEngine/Include/Common/KindOf.h @@ -32,6 +32,7 @@ // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "Lib/BaseType.h" #include "Common/BitFlags.h" +#include "Common/BitFlagsIO.h" //------------------------------------------------------------------------------------------------- /** Kind of flags for determining groups of things that belong together @@ -42,7 +43,7 @@ enum KindOfType CPP_11(: Int) KINDOF_INVALID = -1, KINDOF_OBSTACLE, ///< an obstacle to land-based pathfinders - KINDOF_SELECTABLE, ///< Selectable + KINDOF_SELECTABLE, ///< Actually means MOUSE-INTERACTABLE (doesn't mean you can select it!) KINDOF_IMMOBILE, ///< fixed in location KINDOF_CAN_ATTACK, ///< can attack KINDOF_STICK_TO_TERRAIN_SLOPE, ///< should be stuck at ground level, aligned to terrain slope. requires that IMMOBILE bit is also set. @@ -79,7 +80,9 @@ enum KindOfType CPP_11(: Int) KINDOF_STEALTH_GARRISON, /** enemy teams can't tell that unit is in building.. and if they garrison that building, they stealth unit will eject. */ KINDOF_CASH_GENERATOR, ///< used to check if the unit generates cash... checked by cash hackers and whatever else comes up +#if RTS_GENERALS KINDOF_AIRFIELD, ///< unit has a runway that planes can takeoff/land on +#endif KINDOF_DRAWABLE_ONLY, ///< template is used only to create drawables (not Objects) KINDOF_MP_COUNT_FOR_VICTORY, ///< If a player loses all his buildings that have this kindof in a multiplayer game, he loses. KINDOF_REBUILD_HOLE, ///< a GLA rebuild hole @@ -119,7 +122,7 @@ enum KindOfType CPP_11(: Int) KINDOF_TECH_BUILDING, ///< Neutral tech building - Oil derrick, Hospital, Radio Station, Refinery. KINDOF_POWERED, ///< This object gets the Underpowered disabled condition when its owning player has power consumption exceed supply KINDOF_PRODUCED_AT_HELIPAD, ///< ugh... hacky fix for comanche. (srj) - KINDOF_DRONE, ///< Object drone type -- used for filtering them out of battle plan bonuses and whatever else may come up. + KINDOF_DRONE, ///< Object drone type -- used for filtering them out of battle plan bonuses, making un-snipable, and whatever else may come up. KINDOF_CAN_SEE_THROUGH_STRUCTURE,///< Structure does not block line of sight. KINDOF_BALLISTIC_MISSILE, ///< Large ballistic missiles that are specifically large enough to be targetted by base defenses. KINDOF_CLICK_THROUGH, ///< Objects with this will never be picked by mouse interactions! @@ -139,6 +142,35 @@ enum KindOfType CPP_11(: Int) KINDOF_HERO, ///< Any of the single-instance infantry, JarmenKell, BlackLotus, ColonelBurton KINDOF_IGNORES_SELECT_ALL, ///< Too late to figure out intelligently if something should respond to a Select All command KINDOF_DONT_AUTO_CRUSH_INFANTRY, ///< These units don't try to crush the infantry if ai. + // TheSuperHackers @info Added in Zero Hour: + KINDOF_CLIFF_JUMPER, ///< Can't climb cliffs, but can jump off of them. + KINDOF_FS_SUPPLY_DROPZONE, ///< A supply dropzone. + KINDOF_FS_SUPERWEAPON, ///< A superweapon structure like a nuke silo, particle uplink cannon, scudstorm. + KINDOF_FS_BLACK_MARKET, ///< Is this object a black market? + KINDOF_FS_SUPPLY_CENTER, ///< Is this object a supply center? + KINDOF_FS_STRATEGY_CENTER, ///< Is this object a strategy center? + KINDOF_MONEY_HACKER, ///< Unit that generates money from air. Needed for things that directly power them up. + KINDOF_ARMOR_SALVAGER, ///< subset of salvager that can get armor upgrades from salvage + KINDOF_REVEALS_ENEMY_PATHS, ///< like the listening outpost... when selected, any enemy drawable will draw show paths when moused over + KINDOF_BOOBY_TRAP, ///< A sticky bomb that gets set off by 5 random and unrelated events. + KINDOF_FS_FAKE, ///< Fake structure! + KINDOF_FS_INTERNET_CENTER, ///< Internet Center. + KINDOF_BLAST_CRATER, ///< deeply gouges out the terrain under object footprint + KINDOF_PROP, ///< A prop, visual only, doesn't interact with other objects (rock, street sign, inert fire hydrant) + KINDOF_OPTIMIZED_TREE, ///< An optimized, client side only tree. (The only good kind of tree. jba) + KINDOF_FS_ADVANCED_TECH, ///< Represents each faction's advanced techtree building -- strategy center, propaganda center, and palace. + KINDOF_FS_BARRACKS, ///< A barracks + KINDOF_FS_WARFACTORY, ///< A war factory or arms dealer. + KINDOF_FS_AIRFIELD, ///< An airfield. + KINDOF_AIRCRAFT_CARRIER, ///< An aircraft carrier. + KINDOF_NO_SELECT, ///< Can't select it but you can mouse over it to see it's health (drones!) + KINDOF_REJECT_UNMANNED, ///< Unit cannot enter an unmanned vehicle. + KINDOF_CANNOT_RETALIATE, ///< Unit will not retaliate if asked. + KINDOF_TECH_BASE_DEFENSE, ///< Tech Building that acts as base defence when captured + KINDOF_EMP_HARDENED, ///< Like a delivery plane (B52, B3, CargoPlane,etc.) or a SpectreGunship, which sort-of IS the weapon... + KINDOF_DEMOTRAP, ///< Added strictly only for disarming purposes. They don't act like mines which have rendering and selection implications! + KINDOF_CONSERVATIVE_BUILDING, ///< Conservative structures aren't considered part of your base for sneak attack boundary calculations... + KINDOF_IGNORE_DOCKING_BONES, ///< Structure will not look up docking bones. Patch 1.03 hack. KINDOF_COUNT, // total number of kindofs KINDOF_FIRST = 0, @@ -186,3 +218,4 @@ inline void FLIP_KINDOFMASK(KindOfMaskType& m) // defined in Common/System/Kindof.cpp extern KindOfMaskType KINDOFMASK_NONE; // inits to all zeroes +extern KindOfMaskType KINDOFMASK_FS; // Initializes all FS types for faction structures. diff --git a/Generals/Code/GameEngine/Include/Common/Player.h b/Generals/Code/GameEngine/Include/Common/Player.h index 43c00857936..9ad4bd379c4 100644 --- a/Generals/Code/GameEngine/Include/Common/Player.h +++ b/Generals/Code/GameEngine/Include/Common/Player.h @@ -44,6 +44,7 @@ #pragma once +#include "Common/AcademyStats.h" #include "Common/Debug.h" #include "Common/Energy.h" #include "Common/GameType.h" @@ -519,7 +520,7 @@ class Player : public Snapshot /** * Iterate all objects that this player has */ - void iterateObjects( ObjectIterateFunc func, void *userData ); + void iterateObjects( ObjectIterateFunc func, void *userData ) const; /** return this player's "default" team. @@ -651,6 +652,9 @@ class Player : public Snapshot void setCashBounty(Real percentage) { m_cashBountyPercent = percentage; } void doBountyForKill(const Object* killer, const Object* victim); + AcademyStats* getAcademyStats() { return &m_academyStats; } + const AcademyStats* getAcademyStats() const { return &m_academyStats; } + //Set via logical message. Options menu sets the client value in global data. Player::update() //detects a change, and posts a message. When the message gets processed, this value gets set. Bool isLogicalRetaliationModeEnabled() const { return m_logicalRetaliationModeEnabled; } @@ -772,6 +776,8 @@ class Player : public Snapshot PlayerRelationMap *m_playerRelations; ///< allies & enemies TeamRelationMap *m_teamRelations; ///< allies & enemies + AcademyStats m_academyStats; ///< Keeps track of various statistics in order to provide advice to the player about how to improve playing. + Bool m_canBuildUnits; ///< whether the current player is allowed to build units Bool m_canBuildBase; ///< whether the current player is allowed to build Base buildings Bool m_observer; diff --git a/Generals/Code/GameEngine/Include/Common/PlayerTemplate.h b/Generals/Code/GameEngine/Include/Common/PlayerTemplate.h index 2f69328a42e..527d22f4b73 100644 --- a/Generals/Code/GameEngine/Include/Common/PlayerTemplate.h +++ b/Generals/Code/GameEngine/Include/Common/PlayerTemplate.h @@ -82,6 +82,7 @@ class PlayerTemplate UnicodeString getDisplayName() const { return m_displayName; } AsciiString getSide() const { return m_side; } + AsciiString getBaseSide() const { return m_baseSide; } /// return the tech tree for the player. const Handicap *getHandicap() const { return &m_handicap; } @@ -142,7 +143,7 @@ class PlayerTemplate NameKeyType m_nameKey; UnicodeString m_displayName; - AsciiString m_side; + AsciiString m_side, m_baseSide; Handicap m_handicap; ///< initial baseline for Player capabilities Money m_money; ///< starting credits, if any RGBColor m_preferredColor; ///< our preferred starting color diff --git a/Generals/Code/GameEngine/Include/Common/SpecialPower.h b/Generals/Code/GameEngine/Include/Common/SpecialPower.h index 4666522eb87..ad36025c101 100644 --- a/Generals/Code/GameEngine/Include/Common/SpecialPower.h +++ b/Generals/Code/GameEngine/Include/Common/SpecialPower.h @@ -44,6 +44,7 @@ class ObjectCreationList; class Object; enum ScienceType CPP_11(: Int); struct FieldParse; +enum AcademyClassificationType CPP_11(: Int); // For SpecialPowerType and SpecialPowerMaskType::s_bitNameList. Part of detangling. #include "Common/SpecialPowerType.h" @@ -121,6 +122,7 @@ class SpecialPowerTemplate : public Overridable Real getViewObjectRange() const { return getFO()->m_viewObjectRange; } Real getRadiusCursorRadius() const { return getFO()->m_radiusCursorRadius; } Bool isShortcutPower() const { return getFO()->m_shortcutPower; } + AcademyClassificationType getAcademyClassificationType() const { return m_academyClassificationType; } private: @@ -133,6 +135,7 @@ class SpecialPowerTemplate : public Overridable ScienceType m_requiredScience; ///< science required (if any) to actually execute this power AudioEventRTS m_initiateSound; ///< sound to play when initiated AudioEventRTS m_initiateAtLocationSound; ///< sound to play at target location (if any) + AcademyClassificationType m_academyClassificationType; ///< A value used by the academy to evaluate advice based on what players do. UnsignedInt m_detectionTime; ///< (frames) after using infiltration power (defection, etc.), ///< how long it takes for ex comrades to realize it on their own UnsignedInt m_viewObjectDuration; ///< Lifetime of a looking object we slap down so you can watch the effect diff --git a/Generals/Code/GameEngine/Include/Common/Upgrade.h b/Generals/Code/GameEngine/Include/Common/Upgrade.h index 1a13cfc49c1..a847abed5b0 100644 --- a/Generals/Code/GameEngine/Include/Common/Upgrade.h +++ b/Generals/Code/GameEngine/Include/Common/Upgrade.h @@ -40,6 +40,7 @@ class Player; class UpgradeTemplate; enum NameKeyType CPP_11(: Int); class Image; +enum AcademyClassificationType CPP_11(: Int); //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- @@ -51,7 +52,7 @@ enum UpgradeStatusType CPP_11(: Int) }; //The maximum number of upgrades. -// TheSuperHackers @tweak Stubbjax 22/01/2026 Increases max upgrade count from 64 to allow for more upgrades. +// TheSuperHackers @tweak Stubbjax 22/01/2026 Increases max upgrade count from Generals:64, Zero Hour:128 to allow for more upgrades. // A value of 512 was chosen to allow room for plenty of upgrades while also conserving memory. #define UPGRADE_MAX_COUNT 512 @@ -178,6 +179,7 @@ class UpgradeTemplate : public MemoryPoolObject UpgradeType getUpgradeType() const { return m_type; } const AudioEventRTS* getResearchCompleteSound() const { return &m_researchSound; } const AudioEventRTS* getUnitSpecificSound() const { return &m_unitSpecificSound; } + AcademyClassificationType getAcademyClassificationType() const { return m_academyClassificationType; } /// inventory pictures void cacheButtonImage(); @@ -207,6 +209,7 @@ class UpgradeTemplate : public MemoryPoolObject UpgradeMaskType m_upgradeMask; ///< Unique bitmask for this upgrade template AudioEventRTS m_researchSound; ///< Sound played when upgrade researched. AudioEventRTS m_unitSpecificSound; ///< Secondary sound played when upgrade researched. + AcademyClassificationType m_academyClassificationType; ///< A value used by the academy to evaluate advice based on what players do. UpgradeTemplate *m_next; ///< next UpgradeTemplate *m_prev; ///< prev diff --git a/Generals/Code/GameEngine/Include/GameClient/CommandXlat.h b/Generals/Code/GameEngine/Include/GameClient/CommandXlat.h index fec363bbc59..2dcc4c8d3cd 100644 --- a/Generals/Code/GameEngine/Include/GameClient/CommandXlat.h +++ b/Generals/Code/GameEngine/Include/GameClient/CommandXlat.h @@ -50,13 +50,13 @@ class CommandTranslator : public GameMessageTranslator Int m_objective; Bool m_teamExists; ///< is there a currently selected "team"? - // these are for determining if a drag occurred or it was just a sloppy click - ICoord2D m_mouseRightDragAnchor; // the location of a possible mouse drag start - ICoord2D m_mouseRightDragLift; // the location of a possible mouse drag end - UnsignedInt m_mouseRightDown; // when the mouse down happened - UnsignedInt m_mouseRightUp; // when the mouse up happened + // these are for determining if a drag occurred or it was just a sloppy click + ICoord2D m_mouseRightDragAnchor; // the location of a possible mouse drag start + ICoord2D m_mouseRightDragLift; // the location of a possible mouse drag end + UnsignedInt m_mouseRightDown; // when the mouse down happened + UnsignedInt m_mouseRightUp; // when the mouse up happened - GameMessage::Type createMoveToLocationMessage( Drawable *draw, const Coord3D *dest, CommandEvaluateType commandType ); + GameMessage::Type createMoveToLocationMessage( Drawable *draw, const Coord3D *dest, CommandEvaluateType commandType ); GameMessage::Type createAttackMessage( Drawable *draw, Drawable *other, CommandEvaluateType commandType ); GameMessage::Type createEnterMessage( Drawable *enter, CommandEvaluateType commandType ); GameMessage::Type issueMoveToLocationCommand( const Coord3D *pos, Drawable *drawableInWay, CommandEvaluateType commandType ); diff --git a/Generals/Code/GameEngine/Include/GameClient/InGameUI.h b/Generals/Code/GameEngine/Include/GameClient/InGameUI.h index 3397af27993..e9df6827937 100644 --- a/Generals/Code/GameEngine/Include/GameClient/InGameUI.h +++ b/Generals/Code/GameEngine/Include/GameClient/InGameUI.h @@ -342,6 +342,7 @@ friend class Drawable; // for selection/deselection transactions ACTIONTYPE_MAKE_DEFECTOR, ACTIONTYPE_SET_RALLY_POINT, ACTIONTYPE_COMBATDROP_INTO, + ACTIONTYPE_SABOTAGE_BUILDING, NUM_ACTIONTYPES }; @@ -538,10 +539,12 @@ friend class Drawable; // for selection/deselection transactions void setCameraRotateRight( Bool set ) { m_cameraRotatingRight = set; } void setCameraZoomIn( Bool set ) { m_cameraZoomingIn = set; } void setCameraZoomOut( Bool set ) { m_cameraZoomingOut = set; } + void setCameraTrackingDrawable( Bool set ) { m_cameraTrackingDrawable = set; } Bool isCameraRotatingLeft() const { return m_cameraRotatingLeft; } Bool isCameraRotatingRight() const { return m_cameraRotatingRight; } Bool isCameraZoomingIn() const { return m_cameraZoomingIn; } Bool isCameraZoomingOut() const { return m_cameraZoomingOut; } + Bool isCameraTrackingDrawable() const { return m_cameraTrackingDrawable; } void resetCamera(); virtual void addIdleWorker( Object *obj ); @@ -586,6 +589,9 @@ friend class Drawable; // for selection/deselection transactions void registerWindowLayout(WindowLayout *layout); // register a layout for updates void unregisterWindowLayout(WindowLayout *layout); // stop updates for this layout + void triggerDoubleClickAttackMoveGuardHint(); + + public: // World 2D animation methods void addWorldAnimation( Anim2DTemplate *animTemplate, @@ -647,7 +653,7 @@ friend class Drawable; // for selection/deselection transactions struct MilitarySubtitleData { UnicodeString subtitle; ///< The complete subtitle to be drawn, each line is separated by L"\n" - UnsignedInt index; ///< the current index that we are at through the sibtitle + UnsignedInt index; ///< the current index that we are at through the subtitle ICoord2D position; ///< Where on the screen the subtitle should be drawn DisplayString *displayStrings[MAX_SUBTITLE_LINES]; ///< We'll only allow MAX_SUBTITLE_LINES worth of display strings UnsignedInt currentDisplayString; ///< contains the current display string we're on. (also lets us know the last display string allocated @@ -726,6 +732,8 @@ friend class Drawable; // for selection/deselection transactions Int m_maxSelectCount; ///< Max number of objects to select UnsignedInt m_frameSelectionChanged; ///< Frame when the selection last changed. + Int m_duringDoubleClickAttackMoveGuardHintTimer; ///< Frames left to draw the doubleClickFeedbackTimer + Coord3D m_duringDoubleClickAttackMoveGuardHintStashedPosition; // Video playback data VideoBuffer* m_videoBuffer; ///< video playback buffer @@ -930,6 +938,7 @@ friend class Drawable; // for selection/deselection transactions Bool m_cameraRotatingLeft; Bool m_cameraRotatingRight; Bool m_cameraZoomingIn; + Bool m_cameraTrackingDrawable; Bool m_cameraZoomingOut; Bool m_drawRMBScrollAnchor; diff --git a/Generals/Code/GameEngine/Include/GameLogic/GameLogic.h b/Generals/Code/GameEngine/Include/GameLogic/GameLogic.h index 2aac21da376..0a93e513b0a 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/GameLogic.h +++ b/Generals/Code/GameEngine/Include/GameLogic/GameLogic.h @@ -112,6 +112,9 @@ class GameLogic : public SubsystemInterface, public Snapshot void preUpdate(); +#if defined(RTS_DEBUG) + Int getNumberSleepyUpdates() const {return m_sleepyUpdates.size();} //For profiling, so not in Release. +#endif void processCommandList( CommandList *list ); ///< process the command list void prepareNewGame( GameMode gameMode, GameDifficulty diff, Int rankPoints ); ///< prepare for new game diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/ContainModule.h b/Generals/Code/GameEngine/Include/GameLogic/Module/ContainModule.h index 6ebd21ba40a..32be50719d7 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/ContainModule.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/ContainModule.h @@ -165,6 +165,7 @@ class ContainModuleInterface virtual PlayerMaskType getPlayerWhoEntered() const = 0; virtual void processDamageToContained(Real percentDamage) = 0; ///< Do our % damage to units now. + virtual Object* getClosestRider ( const Coord3D *pos ) = 0; virtual void enableLoadSounds( Bool enable ) = 0; diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h b/Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h index 850a4332e27..f4194c95816 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h @@ -211,6 +211,7 @@ class OpenContain : public UpdateModule, virtual void enableLoadSounds( Bool enable ) override { m_loadSoundsEnabled = enable; } + virtual Object* getClosestRider ( const Coord3D *pos ) override; protected: virtual void monitorConditionChanges(); ///< check to see if we need to update our occupant postions from a model change or anything else diff --git a/Generals/Code/GameEngine/Source/Common/RTS/ActionManager.cpp b/Generals/Code/GameEngine/Source/Common/RTS/ActionManager.cpp index 568fa960314..5af71d74f93 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/ActionManager.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/ActionManager.cpp @@ -173,7 +173,7 @@ Bool ActionManager::canGetRepairedAt( const Object *obj, const Object *repairDes { // aircraft require an airfield. if( !obj->isAboveTerrain() || - repairDest->isKindOf( KINDOF_AIRFIELD ) == FALSE ) + repairDest->isKindOf( KINDOF_FS_AIRFIELD ) == FALSE ) return FALSE; } else @@ -577,7 +577,7 @@ Bool ActionManager::canEnterObject( const Object *obj, const Object *objectToEnt } // Special case for aircraft. - if( obj->isKindOf( KINDOF_AIRCRAFT ) && objectToEnter->isKindOf( KINDOF_AIRFIELD ) ) + if( obj->isKindOf( KINDOF_AIRCRAFT ) && objectToEnter->isKindOf( KINDOF_FS_AIRFIELD ) ) { if (!obj->isAboveTerrain()) return FALSE; diff --git a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp index 94cd2da6583..360741f91d2 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -1671,7 +1671,7 @@ void Player::healAllObjects() } //============================================================================= -void Player::iterateObjects( ObjectIterateFunc func, void *userData ) +void Player::iterateObjects( ObjectIterateFunc func, void *userData ) const { for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin(); it != m_playerTeamPrototypes.end(); ++it) diff --git a/Generals/Code/GameEngine/Source/Common/RTS/PlayerTemplate.cpp b/Generals/Code/GameEngine/Source/Common/RTS/PlayerTemplate.cpp index 458d28d4734..1bab7c1cdf6 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/PlayerTemplate.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/PlayerTemplate.cpp @@ -68,6 +68,7 @@ static const FieldParse TheFieldParseTable[] = { { "Side", INI::parseAsciiString, nullptr, offsetof( PlayerTemplate, m_side ) }, + { "BaseSide", INI::parseAsciiString, nullptr, offsetof( PlayerTemplate, m_baseSide ) }, { "PlayableSide", INI::parseBool, nullptr, offsetof( PlayerTemplate, m_playableSide ) }, { "DisplayName", INI::parseAndTranslateLabel, nullptr, offsetof( PlayerTemplate, m_displayName) }, { "StartMoney", PlayerTemplate::parseStartMoney, nullptr, offsetof( PlayerTemplate, m_money ) }, diff --git a/Generals/Code/GameEngine/Source/Common/RTS/SpecialPower.cpp b/Generals/Code/GameEngine/Source/Common/RTS/SpecialPower.cpp index 409659096f2..3b07f7af930 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/SpecialPower.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/SpecialPower.cpp @@ -174,6 +174,7 @@ void SpecialPowerStore::parseSpecialPowerDefinition( INI *ini ) { "ViewObjectRange", INI::parseReal, nullptr, offsetof( SpecialPowerTemplate, m_viewObjectRange ) }, { "RadiusCursorRadius", INI::parseReal, nullptr, offsetof( SpecialPowerTemplate, m_radiusCursorRadius ) }, { "ShortcutPower", INI::parseBool, nullptr, offsetof( SpecialPowerTemplate, m_shortcutPower ) }, + { "AcademyClassify", INI::parseIndexList, TheAcademyClassificationTypeNames, offsetof( SpecialPowerTemplate, m_academyClassificationType ) }, { nullptr, nullptr, nullptr, 0 } }; diff --git a/Generals/Code/GameEngine/Source/Common/System/KindOf.cpp b/Generals/Code/GameEngine/Source/Common/System/KindOf.cpp index 55b79b6dacf..e5c20d2cfb2 100644 --- a/Generals/Code/GameEngine/Source/Common/System/KindOf.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/KindOf.cpp @@ -71,7 +71,9 @@ const char* const KindOfMaskType::s_bitNameList[] = "HEAL_PAD", "STEALTH_GARRISON", "CASH_GENERATOR", +#if RTS_GENERALS "AIRFIELD", +#endif "DRAWABLE_ONLY", "MP_COUNT_FOR_VICTORY", "REBUILD_HOLE", @@ -131,9 +133,55 @@ const char* const KindOfMaskType::s_bitNameList[] = "HERO", "IGNORES_SELECT_ALL", "DONT_AUTO_CRUSH_INFANTRY", + "CLIFF_JUMPER", + "FS_SUPPLY_DROPZONE", + "FS_SUPERWEAPON", + "FS_BLACK_MARKET", + "FS_SUPPLY_CENTER", + "FS_STRATEGY_CENTER", + "MONEY_HACKER", + "ARMOR_SALVAGER", + "REVEALS_ENEMY_PATHS", + "BOOBY_TRAP", + "FS_FAKE", + "FS_INTERNET_CENTER", + "BLAST_CRATER", + "PROP", + "OPTIMIZED_TREE", + "FS_ADVANCED_TECH", + "FS_BARRACKS", + "FS_WARFACTORY", + "FS_AIRFIELD", + "AIRCRAFT_CARRIER", + "NO_SELECT", + "REJECT_UNMANNED", + "CANNOT_RETALIATE", + "TECH_BASE_DEFENSE", + "EMP_HARDENED", + "DEMOTRAP", + "CONSERVATIVE_BUILDING", + "IGNORE_DOCKING_BONES", + nullptr }; static_assert(ARRAY_SIZE(KindOfMaskType::s_bitNameList) == KindOfMaskType::NumBits + 1, "Incorrect array size"); -KindOfMaskType KINDOFMASK_NONE; // inits to all zeroes +static const Int fsList[] = { + KINDOF_FS_FACTORY, + KINDOF_FS_BASE_DEFENSE, + KINDOF_FS_TECHNOLOGY, + KINDOF_FS_SUPPLY_DROPZONE, + KINDOF_FS_SUPERWEAPON, + KINDOF_FS_BLACK_MARKET, + KINDOF_FS_SUPPLY_CENTER, + KINDOF_FS_STRATEGY_CENTER, + KINDOF_FS_FAKE, + KINDOF_FS_INTERNET_CENTER, + KINDOF_FS_ADVANCED_TECH, + KINDOF_FS_BARRACKS, + KINDOF_FS_WARFACTORY, + KINDOF_FS_AIRFIELD, +}; +KindOfMaskType KINDOFMASK_NONE; // inits to all zeroes +KindOfMaskType KINDOFMASK_FS(KindOfMaskType::kInit, fsList, ARRAY_SIZE(fsList)); diff --git a/Generals/Code/GameEngine/Source/Common/System/Upgrade.cpp b/Generals/Code/GameEngine/Source/Common/System/Upgrade.cpp index 6b30a1b8061..dbf9263c34c 100644 --- a/Generals/Code/GameEngine/Source/Common/System/Upgrade.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/Upgrade.cpp @@ -30,7 +30,6 @@ // USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine -#define DEFINE_UPGRADE_TYPE_NAMES #define DEFINE_VETERANCY_NAMES #include "Common/Upgrade.h" #include "Common/Player.h" @@ -123,6 +122,7 @@ const FieldParse UpgradeTemplate::m_upgradeFieldParseTable[] = { "ButtonImage", INI::parseAsciiString, nullptr, offsetof( UpgradeTemplate, m_buttonImageName ) }, { "ResearchSound", INI::parseAudioEventRTS, nullptr, offsetof( UpgradeTemplate, m_researchSound ) }, { "UnitSpecificSound", INI::parseAudioEventRTS, nullptr, offsetof( UpgradeTemplate, m_unitSpecificSound ) }, + { "AcademyClassify", INI::parseIndexList, TheAcademyClassificationTypeNames, offsetof( UpgradeTemplate, m_academyClassificationType ) }, { nullptr, nullptr, nullptr, 0 } }; @@ -138,6 +138,7 @@ UpgradeTemplate::UpgradeTemplate() m_next = nullptr; m_prev = nullptr; m_buttonImage = nullptr; + m_academyClassificationType = ACT_NONE; } @@ -153,7 +154,7 @@ UpgradeTemplate::~UpgradeTemplate() //------------------------------------------------------------------------------------------------- Int UpgradeTemplate::calcTimeToBuild( Player *player ) const { -#if defined(RTS_DEBUG) +#if defined(RTS_DEBUG) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) if( player->buildsInstantly() ) { return 1; diff --git a/Generals/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp b/Generals/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp index f306e3dbf54..8abd18d352d 100644 --- a/Generals/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp +++ b/Generals/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp @@ -82,6 +82,27 @@ const Int USE_EXP_VALUE_FOR_SKILL_VALUE = -999; AudioEventRTS ThingTemplate::s_audioEventNoSound; +static void parseKindOfFromINI(INI* ini, void* instance, void *store, const void* userData) +{ + KindOfMaskType::parseFromINI(ini, instance, store, userData); + +#if RTS_GENERALS + KindOfMaskType* kindOf = reinterpret_cast(store); + + if (kindOf->test(KINDOF_AIRFIELD)) + { + // KINDOF_AIRFIELD became KINDOF_FS_AIRFIELD in Zero Hour. + kindOf->set(KINDOF_FS_AIRFIELD); + } + + if (kindOf->test(KINDOF_DRONE)) + { + // KINDOF_DRONE was implicitly KINDOF_NO_SELECT in Generals. + kindOf->set(KINDOF_NO_SELECT); + } +#endif +} + /* NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem! @@ -142,7 +163,7 @@ const FieldParse ThingTemplate::s_objectFieldParseTable[] = { "IsPrerequisite", INI::parseBool, nullptr, offsetof( ThingTemplate, m_isPrerequisite ) }, { "DisplayColor", INI::parseColorInt, nullptr, offsetof( ThingTemplate, m_displayColor ) }, { "EditorSorting", INI::parseByteSizedIndexList, EditorSortingNames, offsetof( ThingTemplate, m_editorSorting ) }, - { "KindOf", KindOfMaskType::parseFromINI, nullptr, offsetof( ThingTemplate, m_kindof ) }, + { "KindOf", parseKindOfFromINI, nullptr, offsetof( ThingTemplate, m_kindof ) }, { "CommandSet", INI::parseAsciiString, nullptr, offsetof( ThingTemplate, m_commandSetString ) }, { "BuildVariations", INI::parseAsciiStringVector, nullptr, offsetof( ThingTemplate, m_buildVariations ) }, diff --git a/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp b/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp index ab1f26a767b..aa724fdf8e5 100644 --- a/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -1026,6 +1026,8 @@ InGameUI::InGameUI() m_nextMoveHint = 0; m_selectCount = 0; m_frameSelectionChanged = 0; + m_duringDoubleClickAttackMoveGuardHintTimer = 0; + m_duringDoubleClickAttackMoveGuardHintStashedPosition.zero(); m_maxSelectCount = -1; m_isScrolling = FALSE; m_isSelecting = FALSE; @@ -1489,11 +1491,31 @@ void InGameUI::handleRadiusCursor() if( !rts::localPlayerHasRadar() || (TheRadar->screenPixelToWorld( &mouseIO->pos, &pos ) == FALSE) )// if radar off, or point not on radar TheTacticalView->screenToTerrain( &mouseIO->pos, &pos ); - m_curRadiusCursor.setPosition(pos); //world space position of center of decal - m_curRadiusCursor.update(); - } + + if ( TheGlobalData->m_doubleClickAttackMove && m_duringDoubleClickAttackMoveGuardHintTimer > 0 ) + { + m_curRadiusCursor.setOpacity( m_duringDoubleClickAttackMoveGuardHintTimer * 0.1f ); + m_curRadiusCursor.setPosition( m_duringDoubleClickAttackMoveGuardHintStashedPosition ); //world space position of center of decal + + } + else + { + m_curRadiusCursor.setPosition(pos); //world space position of center of decal + m_curRadiusCursor.update(); + } + + } +} + + +void InGameUI::triggerDoubleClickAttackMoveGuardHint() +{ + m_duringDoubleClickAttackMoveGuardHintTimer = 11; + const MouseIO* mouseIO = TheMouse->getMouseStatus(); + TheTacticalView->screenToTerrain( &mouseIO->pos, &m_duringDoubleClickAttackMoveGuardHintStashedPosition ); } + //------------------------------------------------------------------------------------------------- /** Handle the placement "icons" that appear at the cursor when we're putting down a * structure to build. Note that this has additional logic to also show a line @@ -2788,6 +2810,24 @@ void InGameUI::createCommandHint( const GameMessage *msg ) } //#endif + + setRadiusCursorNone(); + if ( TheGlobalData->m_doubleClickAttackMove ) + { + if ( --m_duringDoubleClickAttackMoveGuardHintTimer > 0 ) + { + setMouseCursor(Mouse::FORCE_ATTACK_GROUND); + setRadiusCursor(RADIUSCURSOR_GUARD_AREA, + nullptr, + PRIMARY_WEAPON); + return; + } + } + + + + + // set cursor to normal if there is a window under the cursor GameWindow *window = nullptr; const MouseIO *io = TheMouse->getMouseStatus(); diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index ae4b136dbb1..9f70ad3049f 100644 --- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -91,7 +91,6 @@ #include "ww3d.h" - #if defined(RTS_DEBUG) /*non-static*/ Real TheSkateDistOverride = 0.0f; @@ -321,20 +320,40 @@ static CanAttackResult canObjectForceAttack( Object *obj, const Object *victim, //Special case -- objects with spawn weapons have to do different checks. Stinger site with stinger soldiers is //the catalyst example. - if( result != ATTACKRESULT_POSSIBLE && result != ATTACKRESULT_POSSIBLE_AFTER_MOVING && obj->isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) ) - { - SpawnBehaviorInterface *spawnInterface = obj->getSpawnBehaviorInterface(); - if( spawnInterface ) - { - //We found the spawn interface, now get the closest slave to the target. - Object *slave = spawnInterface->getClosestSlave( victim->getPosition() ); - if( slave ) - { - result = slave->getAbleToAttackSpecificObject( ATTACK_NEW_TARGET_FORCED, victim, CMD_FROM_PLAYER ); - } - } - } - return result; + if ( obj->isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) ) + { + if( result != ATTACKRESULT_POSSIBLE && result != ATTACKRESULT_POSSIBLE_AFTER_MOVING ) + { + SpawnBehaviorInterface *spawnInterface = obj->getSpawnBehaviorInterface(); + if( spawnInterface ) + { + //We found the spawn interface, now get the closest slave to the target. + Object *slave = spawnInterface->getClosestSlave( victim->getPosition() ); + if( slave ) + { + result = slave->getAbleToAttackSpecificObject( ATTACK_NEW_TARGET_FORCED, victim, CMD_FROM_PLAYER ); + } + } + } + else // oh dear me. The weird case of a garrisoncontainer being a KINDOF_SPAWNS_ARE_THE_WEAPONS... the AmericaBuildingFirebase + { + ContainModuleInterface *contain = obj->getContain(); + if ( contain ) + { + Object *rider = contain->getClosestRider( victim->getPosition() ); + if ( rider ) + { + result = rider->getAbleToAttackSpecificObject( ATTACK_NEW_TARGET_FORCED, victim, CMD_FROM_PLAYER ); + if( result != ATTACKRESULT_NOT_POSSIBLE ) + return result; + } + } + } + + } + + + return result; } else { @@ -352,13 +371,26 @@ static CanAttackResult canObjectForceAttack( Object *obj, const Object *victim, Object *slave = spawnInterface->getClosestSlave( pos ); if( slave ) { - testObj = obj; + testObj = slave; } } - - } - //Now evaluate the testObj to see if it is capable of force attacking the pos. - result = testObj->getAbleToUseWeaponAgainstTarget( ATTACK_NEW_TARGET, victim, pos, CMD_FROM_PLAYER ); + else + { + result = obj->getAbleToUseWeaponAgainstTarget( ATTACK_NEW_TARGET, nullptr, pos, CMD_FROM_PLAYER ); + if( result != ATTACKRESULT_POSSIBLE ) // oh dear me. The weird case of a garrisoncontainer being a KINDOF_SPAWNS_ARE_THE_WEAPONS... the AmericaBuildingFirebase + { + ContainModuleInterface *contain = obj->getContain(); + if ( contain ) + { + Object *rider = contain->getClosestRider( pos ); + if ( rider ) + testObj = rider; + } + } + } + } + //Now evaluate the testObj again to see if it is capable of force attacking the pos. + result = testObj->getAbleToUseWeaponAgainstTarget( ATTACK_NEW_TARGET, nullptr, pos, CMD_FROM_PLAYER ); return result; } } @@ -480,7 +512,15 @@ void pickAndPlayUnitVoiceResponse( const DrawableList *list, GameMessage::Type m } else if( target && target->isKindOf( KINDOF_STRUCTURE ) ) { - soundToPlayPtr = templ->getPerUnitSound( "VoiceGarrison" ); + if( obj->getRelationship( target ) == ENEMIES ) + { + //Saboteurs + soundToPlayPtr = templ->getPerUnitSound( "VoiceEnterHostile" ); + } + else + { + soundToPlayPtr = templ->getPerUnitSound( "VoiceGarrison" ); + } } // order matters: we want to know if I consider it to be an ally, not vice versa else if( target && obj->getRelationship(target) != ALLIES ) @@ -544,6 +584,18 @@ void pickAndPlayUnitVoiceResponse( const DrawableList *list, GameMessage::Type m skip = true; } } + // Special case for GLA worker to use a different set of move voices when he has received the worker shoes upgrade + Player *player = obj->getControllingPlayer(); + static const UpgradeTemplate *workerShoeTemplate = TheUpgradeCenter->findUpgrade( "Upgrade_GLAWorkerShoes" ); + if (player && workerShoeTemplate && player->hasUpgradeComplete(workerShoeTemplate)) + { + if (obj->isKindOf(KINDOF_INFANTRY) && obj->isKindOf(KINDOF_DOZER) && obj->isKindOf(KINDOF_HARVESTER)) // Only Workers fit all 3 + { + soundToPlayPtr = templ->getPerUnitSound( "VoiceMoveUpgraded" ); + objectWithSound = obj; + skip = true; + } + } } break; } @@ -1158,6 +1210,9 @@ GameMessage::Type CommandTranslator::issueSpecialPowerCommand( const CommandButt msg = TheMessageStream->appendMessage( msgType ); msg->appendIntegerArgument( command->getSpecialPowerTemplate()->getID() ); msg->appendLocationArgument( *pos ); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_NETWORKING) + msg->appendRealArgument( INVALID_ANGLE ); //We don't use the angle (unless we're using a construction special in PlaceEventTranslator). +#endif //Object in way.... some specials care, others don't ObjectID targetID = (target && target->getObject()) ? target->getObject()->getID() : INVALID_ID; msg->appendObjectIDArgument( targetID ); @@ -1193,6 +1248,28 @@ GameMessage::Type CommandTranslator::issueSpecialPowerCommand( const CommandButt } } + if( command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT && commandType == DO_COMMAND ) + { + Object *obj = sourceDraw->getObject(); + SpecialPowerUpdateInterface *spUpdate = obj->findSpecialPowerWithOverridableDestination(); + if( spUpdate ) + { + //Deselect the drawables before posting the selection message. + TheInGameUI->deselectAllDrawables(); + + //Because we just launched a special power via shortcut, and the special power accepts input + //from the player (particle uplink cannon, spectre gunship), simply select the object now. + //-------------------- + GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP_NO_SOUND ); + // creating a new team so pass in true + teamMsg->appendBooleanArgument( TRUE ); + teamMsg->appendObjectIDArgument( obj->getID() ); + + TheInGameUI->selectDrawable( obj->getDrawable() ); + } + } + + return msgType; } @@ -1530,7 +1607,7 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw, TheMessageStream->appendMessage(msgType); } else if( TheInGameUI->areSelectedObjectsControllable() - || (command && command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER)) + || (command && command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT)) { GameMessage *hintMessage; @@ -1558,7 +1635,7 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw, if(command && (command->isContextCommand() || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER - || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER)) + || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT)) { if( obj && obj->isKindOf( KINDOF_SHRUBBERY ) && !BitIsSet( command->getOptions(), ALLOW_SHRUBBERY_TARGET ) ) { @@ -1624,16 +1701,19 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw, case GUICOMMANDMODE_HIJACK_VEHICLE: currentlyValid = TheInGameUI->canSelectedObjectsDoAction( InGameUI::ACTIONTYPE_HIJACK_VEHICLE, obj, InGameUI::SELECTION_ANY ); break; + case GUICOMMANDMODE_SABOTAGE_BUILDING: + currentlyValid = TheInGameUI->canSelectedObjectsDoAction( InGameUI::ACTIONTYPE_SABOTAGE_BUILDING, obj, InGameUI::SELECTION_ANY ); + break; #ifdef ALLOW_SURRENDER case GUICOMMANDMODE_PICK_UP_PRISONER: currentlyValid = TheInGameUI->canSelectedObjectsDoAction( InGameUI::ACTIONTYPE_PICK_UP_PRISONER, obj, InGameUI::SELECTION_ANY ); break; #endif - case GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER: + case GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT: { - Object* cmdCenter = ThePlayerList->getLocalPlayer()->findNaturalCommandCenter(); - if (cmdCenter) - currentlyValid = TheInGameUI->canSelectedObjectsDoSpecialPower( command, obj, pos, InGameUI::SELECTION_ANY, command->getOptions(), cmdCenter ); + Object* unit = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( command->getSpecialPowerTemplate()->getSpecialPowerType() ); + if( unit ) + currentlyValid = TheInGameUI->canSelectedObjectsDoSpecialPower( command, obj, pos, InGameUI::SELECTION_ANY, command->getOptions(), unit ); else currentlyValid = false; break; @@ -1657,6 +1737,7 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw, { case GUICOMMANDMODE_CONVERT_TO_CARBOMB: case GUICOMMANDMODE_HIJACK_VEHICLE: + case GUICOMMANDMODE_SABOTAGE_BUILDING: msgType = createEnterMessage( draw, type ); break; #ifdef ALLOW_SURRENDER @@ -1664,11 +1745,11 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw, msgType = issueAttackCommand( draw, type, command->getCommandType() ); break; #endif - case GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER: + case GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT: { - Object* cmdCenter = ThePlayerList->getLocalPlayer()->findNaturalCommandCenter(); - if (cmdCenter) - msgType = issueSpecialPowerCommand( command, type, draw, pos, cmdCenter ); + Object* unit = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( command->getSpecialPowerTemplate()->getSpecialPowerType() ); + if( unit ) + msgType = issueSpecialPowerCommand( command, type, draw, pos, unit ); break; } case GUI_COMMAND_SPECIAL_POWER://lorenzen @@ -1704,6 +1785,31 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw, } } + else if( command && (command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_CONSTRUCT + || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT) ) + { + //We're using the build placement interface to determine where to build our special power item. + //Because of that, we only care about DO_COMMAND. The context evaluation and hint feedback system + //is already taken care of. But what we need to do is trigger the special power to actually build + //the object and reset the timer. + if( type == DO_COMMAND ) + { + switch( command->getCommandType() ) + { + case GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT: + { + Object* unit = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( command->getSpecialPowerTemplate()->getSpecialPowerType() ); + if( unit ) + msgType = issueSpecialPowerCommand( command, type, draw, pos, unit ); + break; + } + case GUI_COMMAND_SPECIAL_POWER://lorenzen + msgType = issueSpecialPowerCommand( command, type, draw, pos, nullptr ); + break; + } + } + } + // ******************************************************************************************** else if( TheInGameUI->canSelectedObjectsOverrideSpecialPowerDestination( pos, InGameUI::SELECTION_ANY, SPECIAL_INVALID ) ) @@ -1949,6 +2055,25 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw, } } // ******************************************************************************************** + else if( draw && draw->getObject() && !TheInGameUI->isInForceAttackMode() && + TheInGameUI->canSelectedObjectsDoAction( InGameUI::ACTIONTYPE_SABOTAGE_BUILDING, + draw->getObject(), + InGameUI::SELECTION_ANY ) ) + { + + if( type == DO_COMMAND || type == EVALUATE_ONLY ) + { + msgType = createEnterMessage( draw, type ); + } + else + { + msgType = GameMessage::MSG_SABOTAGE_HINT; + hintMessage = TheMessageStream->appendMessage( msgType ); + hintMessage->appendObjectIDArgument( draw->getObject()->getID() ); + } + + } + // ******************************************************************************************** else if( draw && !TheInGameUI->isInForceAttackMode() && canSelectionSalvage(obj) ) { GameMessage *msg; @@ -2266,11 +2391,28 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw, { Object *obj = (*it) ? (*it)->getObject() : nullptr; AIUpdateInterface *ai = obj ? obj->getAI() : nullptr; - if( ai && ai->isQuickPathAvailable( pos ) ) - { - validQuickPath = TRUE; - break; - } + if( ai ) + { + if ( ai->isQuickPathAvailable( pos ) ) + { + validQuickPath = TRUE; + break; + } + // Wait! there are some units that CAN moveTo positions that Quickpath will reject, + // namely, Colonel Burton and the CombatBike. Both have CLIFF locomotors. + // We must detect whether the position is valid for these, before just invalidating the cursor, + // out of hand. + if ( ai->hasLocomotorForSurface( LOCOMOTORSURFACE_CLIFF ) ) + { + if ( TheTerrainLogic->isCliffCell( pos->x, pos->y ) ) + { + validQuickPath = TRUE;// yeah, not really quick, but you know... + break; + } + } + } + + } } @@ -2383,7 +2525,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage { break; } - else if( object && object->isMobile() && object->isLocallyControlled() && !object->isContained() && !object->isKindOf( KINDOF_DRONE ) ) + else if( object && object->isMobile() && object->isLocallyControlled() && !object->isContained() && !object->isKindOf( KINDOF_NO_SELECT ) ) { // create a new group. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP ); @@ -2431,7 +2573,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage else { const Object *tempObject = temp->getObject(); - if( tempObject && tempObject->isMobile() && tempObject->isLocallyControlled() && !tempObject->isContained() && !tempObject->isKindOf( KINDOF_DRONE ) ) + if( tempObject && tempObject->isMobile() && tempObject->isLocallyControlled() && !tempObject->isContained() && !tempObject->isKindOf( KINDOF_NO_SELECT ) ) { newDrawable = temp; break; @@ -2489,7 +2631,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage { break; } - else if( object && object->isMobile() && object->isLocallyControlled() && !object->isContained() && !object->isKindOf( KINDOF_DRONE ) ) + else if( object && object->isMobile() && object->isLocallyControlled() && !object->isContained() && !object->isKindOf( KINDOF_NO_SELECT ) ) { // create a new group. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP ); @@ -2538,7 +2680,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage const Object *tempObject = temp->getObject(); // must take case of this case here or else the loop will break without getting newDrawable if( tempObject && temp->getNextDrawable() == selectedDrawable && !temp->isSelected() - && tempObject->isMobile() && tempObject->isLocallyControlled() && !tempObject->isContained() && !tempObject->isKindOf( KINDOF_DRONE ) ) + && tempObject->isMobile() && tempObject->isLocallyControlled() && !tempObject->isContained() && !tempObject->isKindOf( KINDOF_NO_SELECT ) ) { newDrawable = temp; break; @@ -3097,7 +3239,6 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_REMOVE_BEACON: if (TheGameLogic->isInMultiplayerGame() && !TheGameLogic->isInReplayGame()) - { TheMessageStream->appendMessage( GameMessage::MSG_REMOVE_BEACON ); } @@ -3264,7 +3405,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage break; case GameMessage::MSG_META_ALT_CAMERA_ROTATE_LEFT: if (TheTacticalView->isCameraMovementFinished()) - TheTacticalView->rotateCamera(-1.0f / 8.0f, 500); + TheTacticalView->rotateCamera(-1.0f / 8.0f, 500, 100, 400); break; case GameMessage::MSG_META_BEGIN_CAMERA_ROTATE_RIGHT: DEBUG_ASSERTCRASH(!TheInGameUI->isCameraRotatingRight(), ("Setting rotate camera right, but it's already set!")); @@ -3276,7 +3417,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage break; case GameMessage::MSG_META_ALT_CAMERA_ROTATE_RIGHT: if (TheTacticalView->isCameraMovementFinished()) - TheTacticalView->rotateCamera(1.0f / 8.0f, 500); + TheTacticalView->rotateCamera(1.0f / 8.0f, 500, 100, 400); break; case GameMessage::MSG_META_BEGIN_CAMERA_ZOOM_IN: DEBUG_ASSERTCRASH(!TheInGameUI->isCameraZoomingIn(), ("Setting zoom camera in, but it's already set!")); @@ -3297,6 +3438,10 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage case GameMessage::MSG_META_CAMERA_RESET: TheInGameUI->resetCamera(); break; + case GameMessage::MSG_META_TOGGLE_CAMERA_TRACKING_DRAWABLE: + TheInGameUI->setCameraTrackingDrawable( true ); + break; + //-------------------------------------------------------------------------------------- case GameMessage::MSG_META_TOGGLE_FAST_FORWARD_REPLAY: { if( TheGlobalData ) @@ -3348,6 +3493,190 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage } break; } + +#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)//may be defined in GameCommon.h + case GameMessage::MSG_CHEAT_RUNSCRIPT1: + case GameMessage::MSG_CHEAT_RUNSCRIPT2: + case GameMessage::MSG_CHEAT_RUNSCRIPT3: + case GameMessage::MSG_CHEAT_RUNSCRIPT4: + case GameMessage::MSG_CHEAT_RUNSCRIPT5: + case GameMessage::MSG_CHEAT_RUNSCRIPT6: + case GameMessage::MSG_CHEAT_RUNSCRIPT7: + case GameMessage::MSG_CHEAT_RUNSCRIPT8: + case GameMessage::MSG_CHEAT_RUNSCRIPT9: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + if( TheScriptEngine ) + { + Int script = t - GameMessage::MSG_CHEAT_RUNSCRIPT1 + 1; + AsciiString scriptName; + scriptName.format("KEY_F%d", script); + TheScriptEngine->runScript(scriptName); + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugRunScript", L"Run script %d", script) ); + } + disp = DESTROY_MESSAGE; + } + break; + } + //-------------------------------------------------------------------------------------- + case GameMessage::MSG_CHEAT_TOGGLE_SPECIAL_POWER_DELAYS: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + if( TheGlobalData ) + { + + TheWritableGlobalData->m_specialPowerUsesDelay = 1 - TheGlobalData->m_specialPowerUsesDelay; + + if (TheGlobalData->m_specialPowerUsesDelay) + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugSpecialPowerDelaysOn", L"Special Power (Superweapon) Delay is ON") ); + else + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugSpecialPowerDelaysOff", L"Special Power (Superweapon) Delay is OFF") ); + + } + + disp = DESTROY_MESSAGE; + } + break; + + } + //-------------------------------------------------------------------------------------- + case GameMessage::MSG_CHEAT_SWITCH_TEAMS: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + if (TheGameLogic->isInGame()) + { + Int idx; + for (Int i = 0; i < ThePlayerList->getPlayerCount(); i++) + { + if (ThePlayerList->getNthPlayer(i) == ThePlayerList->getLocalPlayer()) + { + idx = i; + break; + } + } + Int idxOrig = idx; + do + { + ++idx; + if (idx >= ThePlayerList->getPlayerCount()) + idx = 0; + + if (idx == idxOrig) + break; + + } while (ThePlayerList->getNthPlayer(idx) == ThePlayerList->getNeutralPlayer()); + + Player* player = ThePlayerList->getNthPlayer(idx); + rts::changeLocalPlayer(player); + } + disp = DESTROY_MESSAGE; + } + break; + } + //-------------------------------------------------------------------------------------- + case GameMessage::MSG_CHEAT_KILL_SELECTION: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + // THIS CALLS THE STANDARD DEBUG MESSAGE, WHICH IS CALLED: + TheMessageStream->appendMessage( GameMessage::MSG_DEBUG_KILL_SELECTION ); + disp = DESTROY_MESSAGE; + } + break; + } + case GameMessage::MSG_CHEAT_INSTANT_BUILD: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + // Doesn't make a valid network message + Player *localPlayer = ThePlayerList->getLocalPlayer(); + localPlayer->toggleInstantBuild(); + + if (localPlayer->buildsInstantly()) + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugInstantBuildOn", L"Instant Build is ON") ); + else + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugInstantBuildOff", L"Instant Build is OFF") ); + + disp = DESTROY_MESSAGE; + } + break; + } + case GameMessage::MSG_CHEAT_ADD_CASH: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + Player *localPlayer = ThePlayerList->getLocalPlayer(); + Money *money = localPlayer->getMoney(); + money->deposit( 10000 ); + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugAddCash", L"Add Cash") ); + } + break; + } + case GameMessage::MSG_CHEAT_GIVE_ALL_SCIENCES: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + Player *player = ThePlayerList->getLocalPlayer(); + if (player) + { + giveAllSciences(player); + } + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugGiveAllSciences", L"Granting all sciences!" ) ); + disp = DESTROY_MESSAGE; + } + break; + } + case GameMessage::MSG_CHEAT_GIVE_SCIENCEPURCHASEPOINTS: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + Player *player = ThePlayerList->getLocalPlayer(); + if (player) + player->addSciencePurchasePoints(1); + + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugGiveSciencePurchasePoint", L"Adding a SciencePurchasePoint" ) ); + disp = DESTROY_MESSAGE; + } + break; + } + case GameMessage::MSG_CHEAT_SHOW_HEALTH: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + TheWritableGlobalData->m_showObjectHealth = 1 - TheGlobalData->m_showObjectHealth; + + if (TheGlobalData->m_showObjectHealth) + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugObjectHealthOn", L"Object Health is ON") ); + else + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugObjectHealthOff", L"Object Health is OFF") ); + } + break; + + } + case GameMessage::MSG_CHEAT_TOGGLE_MESSAGE_TEXT: + { + if ( !TheGameLogic->isInMultiplayerGame() ) + { + + // toggle the message state + TheInGameUI->toggleMessages(); + + // when messages get turned on, display a message + if( TheInGameUI->isMessagesOn() ) + TheInGameUI->message("GUI:MessagesOn"); + + disp = DESTROY_MESSAGE; + } + break; + + } + + +#endif + //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_BEGIN_FORCEMOVE: DEBUG_ASSERTCRASH(!TheInGameUI->isInForceMoveToMode(), ("forceMoveToMode mismatch")); @@ -3362,7 +3691,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_BEGIN_WAYPOINTS: - DEBUG_ASSERTCRASH( !TheInGameUI->isInWaypointMode(), ("Setting m_waypointMode but it's already set!") ); +// DEBUG_ASSERTCRASH( !TheInGameUI->isInWaypointMode(), ("Setting m_waypointMode but it's already set!") ); TheInGameUI->setWaypointMode( true ); break; @@ -3378,7 +3707,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_END_WAYPOINTS: - DEBUG_ASSERTCRASH( TheInGameUI->isInWaypointMode(), ("Clearing m_waypointMode but it's already clear!") ); +// DEBUG_ASSERTCRASH( TheInGameUI->isInWaypointMode(), ("Clearing m_waypointMode but it's already clear!") ); TheInGameUI->setWaypointMode( false ); break; @@ -3480,7 +3809,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage { const CommandButton *command = TheInGameUI->getGUICommand(); if( TheInGameUI->getSelectCount() > 0 - || (command && command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER) ) // If something is selected + || (command && command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT) ) // If something is selected { /// @todo This as well as the one in GameMessage::MSG_DRAWABLE_PICKED below should possibly have a generalized CanAttack instead of simply checking isEnemyOf Drawable *draw = TheGameClient->findDrawableByID( msg->getArgument( 0 )->drawableID ); @@ -3536,49 +3865,76 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage case GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_DOWN: { // There are two ways in which we can ignore this as a deselect: - // 1) 2-D position on screen - // 2) Time has exceeded the time which we allow for this to be a click. - m_mouseRightDragAnchor = msg->getArgument( 0 )->pixel; - m_mouseRightDown = (UnsignedInt) msg->getArgument( 2 )->integer; - - break; - } - - //----------------------------------------------------------------------------- - case GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_UP: - { - // register this event for determining if the click was fast or short enough not to be a drag - m_mouseRightDragLift = msg->getArgument( 0 )->pixel; - m_mouseRightUp = (UnsignedInt) msg->getArgument( 2 )->integer; - - break; - } - - //----------------------------------------------------------------------------- - case GameMessage::MSG_MOUSE_RIGHT_DOUBLE_CLICK: - case GameMessage::MSG_MOUSE_RIGHT_CLICK: - { - // right click is only actioned here if we're in alternate mouse mode - if (TheGlobalData->m_useAlternateMouse - && TheMouse->isClick(&m_mouseRightDragAnchor, &m_mouseRightDragLift, m_mouseRightDown, m_mouseRightUp)) - { - Bool isPoint = (msg->getArgument(0)->pixelRegion.height() == 0 && msg->getArgument(0)->pixelRegion.width() == 0); - - // NOTE: RIGHT_CLICK is not transmitted if AREA_SELECTION or DRAWABLE_PICKED occurs. - // If we see this msg, no object was clicked on, therefore clicked on ground. - // Issue move command to all currently selected objects. - - // sanity - if( TheTacticalView == nullptr ) - break; - - // translate from screen coordinates to terrain coords + // 1) 2-D position on screen + // 2) Time has exceeded the time which we allow for this to be a click. + m_mouseRightDragAnchor = msg->getArgument( 0 )->pixel; + m_mouseRightDown = (UnsignedInt) msg->getArgument( 2 )->integer; + + break; + } + + //----------------------------------------------------------------------------- + case GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_UP: + { + // register this event for determining if the click was fast or short enough not to be a drag + m_mouseRightDragLift = msg->getArgument( 0 )->pixel; + m_mouseRightUp = (UnsignedInt) msg->getArgument( 2 )->integer; + + //Kris: July 7, 2003. Added this code to deselect build placement mode when right clicked. This fixes + //a bug where you couldn't cancel the sneak attack mode via right click. This only happened when you + //didn't have anything selected which is possible via the shortcut bar. Normally, it would get deselected + //via the deselect drawable code. + if( TheMouse->isClick(&m_mouseRightDragAnchor, &m_mouseRightDragLift, m_mouseRightDown, m_mouseRightUp) ) + { + TheInGameUI->placeBuildAvailable( nullptr, nullptr ); + } + + break; + } + + //----------------------------------------------------------------------------- + case GameMessage::MSG_MOUSE_RIGHT_DOUBLE_CLICK: + { + if( TheGlobalData->m_useAlternateMouse && TheGlobalData->m_doubleClickAttackMove ) + { + // create the message and append arguments for a guard location + GameMessage *newMsg = TheMessageStream->appendMessage( GameMessage::MSG_DO_GUARD_POSITION ); + Coord3D pos; + TheTacticalView->screenToTerrain( &msg->getArgument( 0 )->pixel, &pos ); + newMsg->appendLocationArgument(pos); + newMsg->appendIntegerArgument(GUARDMODE_NORMAL); + + ThePlayerList->getLocalPlayer()->getAcademyStats()->recordDoubleClickAttackMoveOrderGiven(); + + TheInGameUI->triggerDoubleClickAttackMoveGuardHint(); + + break; + } + FALLTHROUGH; //intentional fall through + } + case GameMessage::MSG_MOUSE_RIGHT_CLICK: + { + // right click is only actioned here if we're in alternate mouse mode + if (TheGlobalData->m_useAlternateMouse + && TheMouse->isClick(&m_mouseRightDragAnchor, &m_mouseRightDragLift, m_mouseRightDown, m_mouseRightUp)) + { + Bool isPoint = (msg->getArgument(0)->pixelRegion.height() == 0 && msg->getArgument(0)->pixelRegion.width() == 0); + + // NOTE: RIGHT_CLICK is not transmitted if AREA_SELECTION or DRAWABLE_PICKED occurs. + // If we see this msg, no object was clicked on, therefore clicked on ground. + // Issue move command to all currently selected objects. + + // sanity + if( TheTacticalView == nullptr ) + break; + + // translate from screen coordinates to terrain coords Coord3D pos; TheTacticalView->screenToTerrain( &msg->getArgument( 0 )->pixel, &pos ); const CommandButton *command = TheInGameUI->getGUICommand(); Bool controllable = TheInGameUI->areSelectedObjectsControllable() - || (command && command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER); + || (command && command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT); if (isPoint && controllable) { UnsignedInt pickType = getPickTypesForContext( TheInGameUI->isInForceAttackMode() ); @@ -3609,6 +3965,24 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------- case GameMessage::MSG_MOUSE_LEFT_DOUBLE_CLICK: + { + if( !TheGlobalData->m_useAlternateMouse && TheGlobalData->m_doubleClickAttackMove ) + { + // create the message and append arguments for a guard location + GameMessage *newMsg = TheMessageStream->appendMessage( GameMessage::MSG_DO_GUARD_POSITION ); + Coord3D pos; + TheTacticalView->screenToTerrain( &msg->getArgument( 0 )->pixel, &pos ); + newMsg->appendLocationArgument(pos); + newMsg->appendIntegerArgument(GUARDMODE_NORMAL); + + ThePlayerList->getLocalPlayer()->getAcademyStats()->recordDoubleClickAttackMoveOrderGiven(); + + TheInGameUI->triggerDoubleClickAttackMoveGuardHint(); + + break; + } + FALLTHROUGH; //intentional fall through + } case GameMessage::MSG_MOUSE_LEFT_CLICK: { Bool isPoint = (msg->getArgument(0)->pixelRegion.height() == 0 && msg->getArgument(0)->pixelRegion.width() == 0); @@ -3626,20 +4000,20 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage TheTacticalView->screenToTerrain( &msg->getArgument( 0 )->pixel, &pos ); const CommandButton *command = TheInGameUI->getGUICommand(); - // maintain this as the set of GUI button initiated commands that require a left click action in alt mouse mode - Bool isFiringGUICommand = (command && (command->getCommandType() == GUI_COMMAND_SPECIAL_POWER - || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER - || command->getCommandType() == GUI_COMMAND_FIRE_WEAPON + // maintain this as the list of GUI button initiated commands that fire with left click in alt mouse mode + Bool isFiringGUICommand = (command && (command->getCommandType() == GUI_COMMAND_SPECIAL_POWER + || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT + || command->getCommandType() == GUI_COMMAND_FIRE_WEAPON || command->getCommandType() == GUI_COMMAND_COMBATDROP || command->getCommandType() == GUICOMMANDMODE_HIJACK_VEHICLE || command->getCommandType() == GUICOMMANDMODE_CONVERT_TO_CARBOMB)); - // in alternate mouse mode, this left click is only actioned here if we're firing a gui command - if ((TheGlobalData->m_useAlternateMouse) && (! isFiringGUICommand)) - break; + // in alternate mouse mode, this left click is only actioned here if we're firing a gui command + if ((TheGlobalData->m_useAlternateMouse) && (! isFiringGUICommand)) + break; Bool controllable = TheInGameUI->areSelectedObjectsControllable() - || (command && command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER); + || (command && command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT); if (isPoint && controllable) { UnsignedInt pickType = getPickTypesForContext( TheInGameUI->isInForceAttackMode() ); @@ -3668,7 +4042,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage break; } - + case GameMessage::MSG_META_DEMO_INSTANT_QUIT: { TheGameLogic->quit(TRUE); @@ -4412,10 +4786,13 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_DEMO_ADD_CASH: { - Player *localPlayer = ThePlayerList->getLocalPlayer(); - Money *money = localPlayer->getMoney(); - money->deposit( 10000 ); - TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugAddCash", L"Add Cash") ); + if ( !TheGameLogic->isInMultiplayerGame() ) + { + Player *localPlayer = ThePlayerList->getLocalPlayer(); + Money *money = localPlayer->getMoney(); + money->deposit( 10000 ); + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugAddCash", L"Add Cash") ); + } break; } @@ -4570,6 +4947,21 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage break; } + //------------------------------------------------------------------------------- DEMO MESSAGES + //----------------------------------------------------------------------------------------- + case GameMessage::MSG_META_DEMO_TOGGLE_SUPPLY_CENTER_PLACEMENT: + { + TheWritableGlobalData->m_debugSupplyCenterPlacement = !TheWritableGlobalData->m_debugSupplyCenterPlacement; + + if (TheGlobalData->m_debugSupplyCenterPlacement) + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugSupplyCenterPlacementOn", L"Log SupplyCenter Placement is ON") ); + else + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugSupplyCenterPlacementOff", L"Log SupplyCenter Placement is OFF") ); + + disp = DESTROY_MESSAGE; + break; + } + //------------------------------------------------------------------------------- DEMO MESSAGES //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_DEMO_GIVE_SCIENCEPURCHASEPOINTS: @@ -4795,7 +5187,19 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage break; - //------------------------------------------------------------------------------- DEMO MESSAGES + //------------------------------------------------------------------------------- DEMO MESSAGES + //----------------------------------------------------------------------------------------- + case GameMessage::MSG_META_DEBUG_SHOW_AUDIO_LOCATIONS: + TheWritableGlobalData->m_showAudioLocations = 1 - TheGlobalData->m_showAudioLocations; + + if (TheGlobalData->m_showAudioLocations) + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugAudioLocationsOn", L"Show Audio Locations is ON") ); + else + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE("GUI:DebugAudioLocationsOff", L"Show Audio Locations is OFF") ); + + break; + + //------------------------------------------------------------------------------- DEMO MESSAGES //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_DEBUG_SHOW_HEALTH: { @@ -4958,6 +5362,123 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage break; } + //------------------------------------------------------------------------DEMO MESSAGES + //----------------------------------------------------------------------------------------- + case GameMessage::MSG_META_DEBUG_SLEEPY_UPDATE_PERFORMANCE: + { + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugIncreaseAnimSkateSpeed", + L"Number of Sleepy Modules: %d", TheGameLogic->getNumberSleepyUpdates() ) ); + break; + } + + //------------------------------------------------------------------------DEMO MESSAGES + //----------------------------------------------------------------------------------------- + case GameMessage::MSG_META_DEBUG_OBJECT_ID_PERFORMANCE: + { + static __int64 startTime64; + static __int64 endTime64,freq64; + QueryPerformanceCounter((LARGE_INTEGER *)&startTime64); + QueryPerformanceFrequency((LARGE_INTEGER *)&freq64); + Int numberLookups = 10000; + Int testindex = 1; + for( ; testindex < numberLookups; testindex++ ) + { + Object *objPtr = TheGameLogic->findObjectByID((ObjectID)testindex); + objPtr++; + } + QueryPerformanceCounter((LARGE_INTEGER *)&endTime64); + double timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64)); + + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugObjectIdPerformance", + L"Time to run %d ObjectID lookups is %f. Next index is %d", numberLookups, timeToUpdate, (Int)TheGameLogic->getObjectIDCounter() ) ); + + + QueryPerformanceCounter((LARGE_INTEGER *)&startTime64); + QueryPerformanceFrequency((LARGE_INTEGER *)&freq64); + numberLookups = 100000; + for( testindex = 1; testindex < numberLookups; testindex++ ) + { + Object *objPtr = TheGameLogic->findObjectByID((ObjectID)testindex); + objPtr++; + } + QueryPerformanceCounter((LARGE_INTEGER *)&endTime64); + timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64)); + + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugObjectIdPerformance", + L"Time to run %d ObjectID lookups is %f. Next index is %d", numberLookups, timeToUpdate, (Int)TheGameLogic->getObjectIDCounter() ) ); + + + QueryPerformanceCounter((LARGE_INTEGER *)&startTime64); + QueryPerformanceFrequency((LARGE_INTEGER *)&freq64); + numberLookups = 1000000; + for( testindex = 1; testindex < numberLookups; testindex++ ) + { + Object *objPtr = TheGameLogic->findObjectByID((ObjectID)testindex); + objPtr++; + } + QueryPerformanceCounter((LARGE_INTEGER *)&endTime64); + timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64)); + + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugObjectIdPerformance", + L"Time to run %d ObjectID lookups is %f. Next index is %d", numberLookups, timeToUpdate, (Int)TheGameLogic->getObjectIDCounter() ) ); + + break; + } + + //------------------------------------------------------------------------DEMO MESSAGES + //----------------------------------------------------------------------------------------- + case GameMessage::MSG_META_DEBUG_DRAWABLE_ID_PERFORMANCE: + { + static __int64 startTime64; + static __int64 endTime64,freq64; + QueryPerformanceCounter((LARGE_INTEGER *)&startTime64); + QueryPerformanceFrequency((LARGE_INTEGER *)&freq64); + Int numberLookups = 10000; + Int testindex = 1; + for( ; testindex < numberLookups; testindex++ ) + { + Drawable *drawPtr = TheGameClient->findDrawableByID((DrawableID)testindex); + drawPtr++; + } + QueryPerformanceCounter((LARGE_INTEGER *)&endTime64); + double timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64)); + + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugDrawableIdPerformance", + L"Time to run %d DrawableID lookups is %f. Next index is %d", numberLookups, timeToUpdate, (Int)TheGameClient->getDrawableIDCounter() ) ); + + + QueryPerformanceCounter((LARGE_INTEGER *)&startTime64); + QueryPerformanceFrequency((LARGE_INTEGER *)&freq64); + numberLookups = 100000; + for( testindex = 1; testindex < numberLookups; testindex++ ) + { + Drawable *drawPtr = TheGameClient->findDrawableByID((DrawableID)testindex); + drawPtr++; + } + QueryPerformanceCounter((LARGE_INTEGER *)&endTime64); + timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64)); + + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugDrawableIdPerformance", + L"Time to run %d DrawableID lookups is %f. Next index is %d", numberLookups, timeToUpdate, (Int)TheGameClient->getDrawableIDCounter() ) ); + + + QueryPerformanceCounter((LARGE_INTEGER *)&startTime64); + QueryPerformanceFrequency((LARGE_INTEGER *)&freq64); + numberLookups = 1000000; + for( testindex = 1; testindex < numberLookups; testindex++ ) + { + Drawable *drawPtr = TheGameClient->findDrawableByID((DrawableID)testindex); + drawPtr++; + } + QueryPerformanceCounter((LARGE_INTEGER *)&endTime64); + timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64)); + + TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugDrawableIdPerformance", + L"Time to run %d DrawableID lookups is %f. Next index is %d", numberLookups, timeToUpdate, (Int)TheGameClient->getDrawableIDCounter() ) ); + + break; + } + //--------------------------------------------------------------------------- END DEMO MESSAGES //--------------------------------------------------------------------------- END DEMO MESSAGES //--------------------------------------------------------------------------- END DEMO MESSAGES diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp index 92dc77b2889..5150b885c3d 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp @@ -508,6 +508,36 @@ void OpenContain::iterateContained( ContainIterateFunc func, void *userData, Boo } } +//------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------ +Object* OpenContain::getClosestRider( const Coord3D *pos ) +{ + Object *closest = nullptr; + Real closestDistance; + + for(ContainedItemsList::const_iterator it = m_containList.begin(); it != m_containList.end(); ++it) + { + Object *rider = *it; + + if (rider) + { + Real distance = ThePartitionManager->getDistanceSquared( rider, pos, FROM_CENTER_2D ); + if( !closest || closestDistance > distance ) + { + closest = rider; + closestDistance = distance; + } + } + + } + + return closest; //Could be null! +} + + + + + //------------------------------------------------------------------------------------------------- struct DropData { diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index 2c0091b0c53..01985bbbf2e 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -900,7 +900,7 @@ ChinookAIUpdate::~ChinookAIUpdate() static ParkingPlaceBehaviorInterface* getPP(ObjectID id) { Object* airfield = TheGameLogic->findObjectByID( id ); - if (airfield == nullptr || airfield->isEffectivelyDead() || !airfield->isKindOf(KINDOF_AIRFIELD)) + if (airfield == nullptr || airfield->isEffectivelyDead() || !airfield->isKindOf(KINDOF_FS_AIRFIELD)) return nullptr; ParkingPlaceBehaviorInterface* pp = nullptr; diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp index c21cdc65607..483cf6b45f7 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp @@ -107,7 +107,7 @@ static ParkingPlaceBehaviorInterface* getPP(ObjectID id, Object** airfieldPP = n *airfieldPP = nullptr; Object* airfield = TheGameLogic->findObjectByID( id ); - if (airfield == nullptr || airfield->isEffectivelyDead() || !airfield->isKindOf(KINDOF_AIRFIELD) || airfield->testStatus(OBJECT_STATUS_SOLD)) + if (airfield == nullptr || airfield->isEffectivelyDead() || !airfield->isKindOf(KINDOF_FS_AIRFIELD) || airfield->testStatus(OBJECT_STATUS_SOLD)) return nullptr; if (airfieldPP) @@ -146,7 +146,7 @@ class PartitionFilterHasParkingPlace : public PartitionFilter //------------------------------------------------------------------------------------------------- static Object* findSuitableAirfield(Object* jet) { - PartitionFilterAcceptByKindOf filterKind(MAKE_KINDOF_MASK(KINDOF_AIRFIELD), KINDOFMASK_NONE); + PartitionFilterAcceptByKindOf filterKind(MAKE_KINDOF_MASK(KINDOF_FS_AIRFIELD), KINDOFMASK_NONE); PartitionFilterRejectByObjectStatus filterStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION ), OBJECT_STATUS_MASK_NONE ); PartitionFilterRejectByObjectStatus filterStatusTwo( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_SOLD ), OBJECT_STATUS_MASK_NONE ); // Independent to make it an OR PartitionFilterRelationship filterTeam(jet, PartitionFilterRelationship::ALLOW_ALLIES); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp index b9880bcdf1a..c9651c4fd1b 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp @@ -654,7 +654,7 @@ Bool WeaponTemplate::shouldProjectileCollideWith( // horrible special case for airplanes sitting on airfields: the projectile might // "collide" with the airfield's (invisible) collision geometry when a resting plane // is targeted. we don't want this. special case it: - if (thingWeCollidedWith->isKindOf(KINDOF_AIRFIELD)) + if (thingWeCollidedWith->isKindOf(KINDOF_FS_AIRFIELD)) { // // ok, so if we are an airfield, and our intended victim has a reserved space diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index ea7ef771ff7..76928a04764 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -1,5 +1,5 @@ set(GAMEENGINE_SRC - Include/Common/AcademyStats.h +# Include/Common/AcademyStats.h Include/Common/ActionManager.h # Include/Common/ArchiveFile.h # Include/Common/ArchiveFileSystem.h @@ -607,7 +607,7 @@ set(GAMEENGINE_SRC # Source/Common/RandomValue.cpp Source/Common/Recorder.cpp # Source/Common/ReplaySimulation.cpp - Source/Common/RTS/AcademyStats.cpp +# Source/Common/RTS/AcademyStats.cpp Source/Common/RTS/ActionManager.cpp Source/Common/RTS/Energy.cpp Source/Common/RTS/Handicap.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/Common/KindOf.h b/GeneralsMD/Code/GameEngine/Include/Common/KindOf.h index ab6736ec2cf..a81d4e62870 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/KindOf.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/KindOf.h @@ -41,7 +41,7 @@ enum KindOfType CPP_11(: Int) { KINDOF_INVALID = -1, - + KINDOF_OBSTACLE, ///< an obstacle to land-based pathfinders KINDOF_SELECTABLE, ///< Actually means MOUSE-INTERACTABLE (doesn't mean you can select it!) KINDOF_IMMOBILE, ///< fixed in location @@ -80,6 +80,9 @@ enum KindOfType CPP_11(: Int) KINDOF_STEALTH_GARRISON, /** enemy teams can't tell that unit is in building.. and if they garrison that building, they stealth unit will eject. */ KINDOF_CASH_GENERATOR, ///< used to check if the unit generates cash... checked by cash hackers and whatever else comes up +#if RTS_GENERALS + KINDOF_AIRFIELD, ///< unit has a runway that planes can takeoff/land on +#endif KINDOF_DRAWABLE_ONLY, ///< template is used only to create drawables (not Objects) KINDOF_MP_COUNT_FOR_VICTORY, ///< If a player loses all his buildings that have this kindof in a multiplayer game, he loses. KINDOF_REBUILD_HOLE, ///< a GLA rebuild hole @@ -139,6 +142,7 @@ enum KindOfType CPP_11(: Int) KINDOF_HERO, ///< Any of the single-instance infantry, JarmenKell, BlackLotus, ColonelBurton KINDOF_IGNORES_SELECT_ALL, ///< Too late to figure out intelligently if something should respond to a Select All command KINDOF_DONT_AUTO_CRUSH_INFANTRY, ///< These units don't try to crush the infantry if ai. + // TheSuperHackers @info Added in Zero Hour: KINDOF_CLIFF_JUMPER, ///< Can't climb cliffs, but can jump off of them. KINDOF_FS_SUPPLY_DROPZONE, ///< A supply dropzone. KINDOF_FS_SUPERWEAPON, ///< A superweapon structure like a nuke silo, particle uplink cannon, scudstorm. diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Upgrade.h b/GeneralsMD/Code/GameEngine/Include/Common/Upgrade.h index 1f7401f646f..f6fe7759fe3 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Upgrade.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Upgrade.h @@ -52,7 +52,7 @@ enum UpgradeStatusType CPP_11(: Int) }; //The maximum number of upgrades. -// TheSuperHackers @tweak Stubbjax 22/01/2026 Increases max upgrade count from 128 to allow for more upgrades. +// TheSuperHackers @tweak Stubbjax 22/01/2026 Increases max upgrade count from Generals:64, Zero Hour:128 to allow for more upgrades. // A value of 512 was chosen to allow room for plenty of upgrades while also conserving memory. #define UPGRADE_MAX_COUNT 512 diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/KindOf.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/KindOf.cpp index cd07c9f3504..fab350e998d 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/KindOf.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/KindOf.cpp @@ -71,6 +71,9 @@ const char* const KindOfMaskType::s_bitNameList[] = "HEAL_PAD", "STEALTH_GARRISON", "CASH_GENERATOR", +#if RTS_GENERALS + "AIRFIELD", +#endif "DRAWABLE_ONLY", "MP_COUNT_FOR_VICTORY", "REBUILD_HOLE", diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp index be047412608..c113766a012 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ThingTemplate.cpp @@ -84,6 +84,27 @@ const Int USE_EXP_VALUE_FOR_SKILL_VALUE = -999; AudioEventRTS ThingTemplate::s_audioEventNoSound; +static void parseKindOfFromINI(INI* ini, void* instance, void *store, const void* userData) +{ + KindOfMaskType::parseFromINI(ini, instance, store, userData); + +#if RTS_GENERALS + KindOfMaskType* kindOf = reinterpret_cast(store); + + if (kindOf->test(KINDOF_AIRFIELD)) + { + // KINDOF_AIRFIELD became KINDOF_FS_AIRFIELD in Zero Hour. + kindOf->set(KINDOF_FS_AIRFIELD); + } + + if (kindOf->test(KINDOF_DRONE)) + { + // KINDOF_DRONE was implicitly KINDOF_NO_SELECT in Generals. + kindOf->set(KINDOF_NO_SELECT); + } +#endif +} + /* NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem! @@ -147,7 +168,7 @@ const FieldParse ThingTemplate::s_objectFieldParseTable[] = { "IsPrerequisite", INI::parseBool, nullptr, offsetof( ThingTemplate, m_isPrerequisite ) }, { "DisplayColor", INI::parseColorInt, nullptr, offsetof( ThingTemplate, m_displayColor ) }, { "EditorSorting", INI::parseByteSizedIndexList, EditorSortingNames, offsetof( ThingTemplate, m_editorSorting ) }, - { "KindOf", KindOfMaskType::parseFromINI, nullptr, offsetof( ThingTemplate, m_kindof ) }, + { "KindOf", parseKindOfFromINI, nullptr, offsetof( ThingTemplate, m_kindof ) }, { "CommandSet", INI::parseAsciiString, nullptr, offsetof( ThingTemplate, m_commandSetString ) }, { "BuildVariations", INI::parseAsciiStringVector, nullptr, offsetof( ThingTemplate, m_buildVariations ) }, diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 3ffdc71758e..fa0d78a384f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -361,9 +361,7 @@ static CanAttackResult canObjectForceAttack( Object *obj, const Object *victim, //that try to force attack in a location beyond their reach (range, LOS, etc). if( pos ) { - Object *testObj = obj; - if( obj->isKindOf( KINDOF_IMMOBILE ) || obj->isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) ) { SpawnBehaviorInterface *spawnInterface = obj->getSpawnBehaviorInterface(); @@ -589,7 +587,7 @@ void pickAndPlayUnitVoiceResponse( const DrawableList *list, GameMessage::Type m // Special case for GLA worker to use a different set of move voices when he has received the worker shoes upgrade Player *player = obj->getControllingPlayer(); static const UpgradeTemplate *workerShoeTemplate = TheUpgradeCenter->findUpgrade( "Upgrade_GLAWorkerShoes" ); - if (player && player->hasUpgradeComplete(workerShoeTemplate)) + if (player && workerShoeTemplate && player->hasUpgradeComplete(workerShoeTemplate)) { if (obj->isKindOf(KINDOF_INFANTRY) && obj->isKindOf(KINDOF_DOZER) && obj->isKindOf(KINDOF_HARVESTER)) // Only Workers fit all 3 { @@ -1212,7 +1210,9 @@ GameMessage::Type CommandTranslator::issueSpecialPowerCommand( const CommandButt msg = TheMessageStream->appendMessage( msgType ); msg->appendIntegerArgument( command->getSpecialPowerTemplate()->getID() ); msg->appendLocationArgument( *pos ); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_NETWORKING) msg->appendRealArgument( INVALID_ANGLE ); //We don't use the angle (unless we're using a construction special in PlaceEventTranslator). +#endif //Object in way.... some specials care, others don't ObjectID targetID = (target && target->getObject()) ? target->getObject()->getID() : INVALID_ID; msg->appendObjectIDArgument( targetID ); @@ -4050,15 +4050,9 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage break; } - //------------------------------------------------------------------------------- DEMO MESSAGES - #if defined(RTS_DEBUG) - //------------------------------------------------------------------------- BEGIN DEMO MESSAGES - //------------------------------------------------------------------------- BEGIN DEMO MESSAGES - //------------------------------------------------------------------------- BEGIN DEMO MESSAGES //------------------------------------------------------------------------------- DEMO MESSAGES //----------------------------------------------------------------------------------------- - //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_DEMO_SWITCH_TEAMS: { if (TheGameLogic->isInGame()) @@ -4727,7 +4721,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage disp = DESTROY_MESSAGE; break; - } + } //------------------------------------------------------------------------------- DEMO MESSAGES //----------------------------------------------------------------------------------------- diff --git a/scripts/cpp/unify_move_files.py b/scripts/cpp/unify_move_files.py index 936b08844e6..8f1cf10c140 100644 --- a/scripts/cpp/unify_move_files.py +++ b/scripts/cpp/unify_move_files.py @@ -536,7 +536,7 @@ def main(): #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/System/Damage.cpp", Game.CORE, "GameEngine/Source/GameLogic/System/Damage.cpp") #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp", Game.CORE, "GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp") #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/System/RankInfo.cpp", Game.CORE, "GameEngine/Source/GameLogic/System/RankInfo.cpp") - + #unify_file_lib(Game.ZEROHOUR, "Libraries/Source/WWVegas/WW3D2/dx8fvf.h", Game.CORE, "Libraries/Source/WWVegas/WW3D2/dx8fvf.h") #unify_file_lib(Game.ZEROHOUR, "Libraries/Source/WWVegas/WW3D2/dx8indexbuffer.h", Game.CORE, "Libraries/Source/WWVegas/WW3D2/dx8indexbuffer.h") #unify_file_lib(Game.ZEROHOUR, "Libraries/Source/WWVegas/WW3D2/dx8renderer.h", Game.CORE, "Libraries/Source/WWVegas/WW3D2/dx8renderer.h") @@ -546,6 +546,9 @@ def main(): #unify_file_lib(Game.ZEROHOUR, "Libraries/Source/WWVegas/WW3D2/dx8renderer.cpp", Game.CORE, "Libraries/Source/WWVegas/WW3D2/dx8renderer.cpp") #unify_file_lib(Game.ZEROHOUR, "Libraries/Source/WWVegas/WW3D2/dx8vertexbuffer.cpp", Game.CORE, "Libraries/Source/WWVegas/WW3D2/dx8vertexbuffer.cpp") + #unify_move_file(Game.ZEROHOUR, "GameEngine/Include/Common/AcademyStats.h", Game.CORE, "GameEngine/Include/Common/AcademyStats.h") + #unify_move_file(Game.ZEROHOUR, "GameEngine/Source/Common/RTS/AcademyStats.cpp", Game.CORE, "GameEngine/Source/Common/RTS/AcademyStats.cpp") + return