diff --git a/Generals/Code/GameEngine/Include/Common/DisabledTypes.h b/Generals/Code/GameEngine/Include/Common/DisabledTypes.h index eb7e004eeb3..e158c2a32a7 100644 --- a/Generals/Code/GameEngine/Include/Common/DisabledTypes.h +++ b/Generals/Code/GameEngine/Include/Common/DisabledTypes.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 @@ -48,11 +49,16 @@ enum DisabledType CPP_11(: Int) DISABLED_UNDERPOWERED,//Separate from ScriptUnderpowered, the owning player has insufficient power. Energy status controls this DISABLED_FREEFALL, //This unit has been disabled via being in free fall + DISABLED_AWESTRUCK, + DISABLED_BRAINWASHED, + DISABLED_SUBDUED, ///< Temporarily shut down by Subdual damage //These ones are specifically for scripts to enable/reenable! DISABLED_SCRIPT_DISABLED, DISABLED_SCRIPT_UNDERPOWERED, DISABLED_COUNT, + + DISABLED_ANY = 65535 ///< Do not use this value for setting disabled types (read-only) }; typedef BitFlags DisabledMaskType; @@ -102,6 +108,5 @@ inline void FLIP_DISABLEDMASK(DisabledMaskType& m) // defined in Common/System/DisabledTypes.cpp -extern const char *TheDisabledNames[]; extern DisabledMaskType DISABLEDMASK_NONE; // inits to all zeroes extern DisabledMaskType DISABLEDMASK_ALL; // inits to all bits set. diff --git a/Generals/Code/GameEngine/Include/Common/Player.h b/Generals/Code/GameEngine/Include/Common/Player.h index 464141a9a42..f92dcdcb7a0 100644 --- a/Generals/Code/GameEngine/Include/Common/Player.h +++ b/Generals/Code/GameEngine/Include/Common/Player.h @@ -276,6 +276,22 @@ class Player : public Snapshot if he has multiple, return one arbitrarily. */ Object* findNaturalCommandCenter(); + Object* findAnyExistingObjectWithThingTemplate( const ThingTemplate *thing ); + + // Finds a short-cut firing special power of specified type returning the first ready power or + // the most ready if none ready. + Object* findMostReadyShortcutSpecialPowerOfType( SpecialPowerType spType ); + + //Find specified thing template's most ready weapon. + Object* findMostReadyShortcutWeaponForThing( const ThingTemplate *thing, UnsignedInt &mostReadyPercentage ); + Object* findMostReadyShortcutSpecialPowerForThing( const ThingTemplate *thing, UnsignedInt &mostReadyPercentage ); + + // Finds a short-cut firing special power of any type arbitrarily. + Bool hasAnyShortcutSpecialPower(); + + // Counts available shortcut special power of specified type that can fire now. + Int countReadyShortcutSpecialPowersOfType( SpecialPowerType spType ); + /// return t if the player has the given science, either intrinsically, via specialization, or via capture. Bool hasScience(ScienceType t) const; Bool isScienceDisabled( ScienceType t ) const; ///< Can't purchase this science because of script reasons. diff --git a/Generals/Code/GameEngine/Include/GameClient/ControlBar.h b/Generals/Code/GameEngine/Include/GameClient/ControlBar.h index 89f0a46ddaa..88ae7d4abb0 100644 --- a/Generals/Code/GameEngine/Include/GameClient/ControlBar.h +++ b/Generals/Code/GameEngine/Include/GameClient/ControlBar.h @@ -56,6 +56,7 @@ class ControlBarSchemeManager; class UpgradeTemplate; class ControlBarResizer; class GameWindowTransitionsHandler; +class DisplayString; enum ProductionID CPP_11(: Int); @@ -96,6 +97,8 @@ enum CommandOption CPP_11(: Int) SCRIPT_ONLY = 0x00080000, // Only a script can use this command (not by users) IGNORES_UNDERPOWERED = 0x00100000, // this button isn't disabled if its object is merely underpowered USES_MINE_CLEARING_WEAPONSET= 0x00200000, // uses the special mine-clearing weaponset, even if not current + CAN_USE_WAYPOINTS = 0x00400000, // button has option to use a waypoint path + MUST_BE_STOPPED = 0x00800000, // Unit must be stopped in order to be able to use button. }; #ifdef DEFINE_COMMAND_OPTION_NAMES @@ -127,6 +130,8 @@ static const char *const TheCommandOptionNames[] = "SCRIPT_ONLY", "IGNORES_UNDERPOWERED", "USES_MINE_CLEARING_WEAPONSET", + "CAN_USE_WAYPOINTS", + "MUST_BE_STOPPED", nullptr }; @@ -181,7 +186,7 @@ enum GUICommandType CPP_11(: Int) GUI_COMMAND_FIRE_WEAPON, ///< fire a weapon GUI_COMMAND_SPECIAL_POWER, ///< do a special power GUI_COMMAND_PURCHASE_SCIENCE, ///< purchase science - GUI_COMMAND_HACK_INTERNET, ///< Hey author, write me! + GUI_COMMAND_HACK_INTERNET, ///< gain income from the ether (by hacking the internet) GUI_COMMAND_TOGGLE_OVERCHARGE, ///< Overcharge command for power plants #ifdef ALLOW_SURRENDER GUI_COMMAND_POW_RETURN_TO_PRISON, ///< POW Truck, return to prison @@ -189,9 +194,10 @@ enum GUICommandType CPP_11(: Int) GUI_COMMAND_COMBATDROP, ///< rappel contents to ground or bldg GUI_COMMAND_SWITCH_WEAPON, ///< switch weapon use - //Context senstive command modes + //Context sensitive command modes GUICOMMANDMODE_HIJACK_VEHICLE, GUICOMMANDMODE_CONVERT_TO_CARBOMB, + GUICOMMANDMODE_SABOTAGE_BUILDING, #ifdef ALLOW_SURRENDER GUICOMMANDMODE_PICK_UP_PRISONER, ///< POW Truck assigned to pick up a specific prisoner #endif @@ -199,7 +205,14 @@ enum GUICommandType CPP_11(: Int) // context-insensitive command mode(s) GUICOMMANDMODE_PLACE_BEACON, - GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER, ///< do a special power from localPlayer's command center, regardless of selection + GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT, ///< do a special power from localPlayer's command center, regardless of selection +#if RTS_GENERALS + GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER = GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT, ///< Legacy name +#endif + GUI_COMMAND_SPECIAL_POWER_CONSTRUCT, ///< do a special power using the construct building interface + GUI_COMMAND_SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT, ///< do a shortcut special power using the construct building interface + + GUI_COMMAND_SELECT_ALL_UNITS_OF_TYPE, // add more commands here, don't forget to update the string command list below too ... @@ -241,11 +254,19 @@ static const char *const TheGuiCommandNames[] = "SWITCH_WEAPON", "HIJACK_VEHICLE", "CONVERT_TO_CARBOMB", + "SABOTAGE_BUILDING", #ifdef ALLOW_SURRENDER "PICK_UP_PRISONER", #endif "PLACE_BEACON", - "SPECIAL_POWER_FROM_COMMAND_CENTER", +#if RTS_GENERALS + "SPECIAL_POWER_FROM_COMMAND_CENTER", ///< Legacy name +#else + "SPECIAL_POWER_FROM_SHORTCUT", +#endif + "SPECIAL_POWER_CONSTRUCT", + "SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT", + "SELECT_ALL_UNITS_OF_TYPE", nullptr }; @@ -386,16 +407,16 @@ class CommandButton : public Overridable /** Command sets are collections of configurable command buttons. They are used in the * command context sensitive window in the battle user interface */ //------------------------------------------------------------------------------------------------- -enum { MAX_COMMANDS_PER_SET = 12 }; // user interface max button limit for commands +enum { MAX_COMMANDS_PER_SET = 18 }; // user interface max is 14 (but internally it's 18 for script only buttons!) enum { MAX_RIGHT_HUD_UPGRADE_CAMEOS = 5}; enum { - MAX_PURCHASE_SCIENCE_RANK_1 = 3, - MAX_PURCHASE_SCIENCE_RANK_3 = 12, - MAX_PURCHASE_SCIENCE_RANK_8 = 1, + MAX_PURCHASE_SCIENCE_RANK_1 = 4, + MAX_PURCHASE_SCIENCE_RANK_3 = 15, + MAX_PURCHASE_SCIENCE_RANK_8 = 4, }; enum { MAX_STRUCTURE_INVENTORY_BUTTONS = 10 }; // there are this many physical buttons in "inventory" windows for structures enum { MAX_BUILD_QUEUE_BUTTONS = 9 };// physical button count for the build queue -enum { MAX_SPECIAL_POWER_SHORTCUTS = 5}; +enum { MAX_SPECIAL_POWER_SHORTCUTS = 11}; class CommandSet : public Overridable { @@ -760,6 +781,11 @@ class ControlBar : public SubsystemInterface void initSpecialPowershortcutBar( Player *player); void triggerRadarAttackGlow(); + + void drawSpecialPowerShortcutMultiplierText(); + + Bool hasAnyShortcutSelection() const; + protected: void updateRadarAttackGlow (); @@ -824,7 +850,7 @@ class ControlBar : public SubsystemInterface static void populateInvDataCallback( Object *obj, void *userData ); // the following methods are for updating the currently showing context - CommandAvailability getCommandAvailability( const CommandButton *command, Object *obj, GameWindow *win, Bool forceDisabledEvaluation = FALSE ) const; + CommandAvailability getCommandAvailability( const CommandButton *command, Object *obj, GameWindow *win, GameWindow *applyToWin = nullptr, Bool forceDisabledEvaluation = FALSE ) const; void updateContextMultiSelect(); void updateContextPurchaseScience(); void updateContextCommand(); @@ -893,6 +919,7 @@ class ControlBar : public SubsystemInterface GameWindow *m_sciencePurchaseWindowsRank8[ MAX_PURCHASE_SCIENCE_RANK_8 ]; ///< command window controls for easy access GameWindow *m_specialPowerShortcutButtons[ MAX_SPECIAL_POWER_SHORTCUTS ]; GameWindow *m_specialPowerShortcutButtonParents[ MAX_SPECIAL_POWER_SHORTCUTS ]; + DisplayString *m_shortcutDisplayStrings[ MAX_SPECIAL_POWER_SHORTCUTS ]; Int m_currentlyUsedSpecialPowersButtons; ///< Value will be <= MAX_SPECIAL_POWER_SHORTCUTS; diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h b/Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h index 6ca26ace2d5..3bcfe1b13c1 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h @@ -170,6 +170,11 @@ class ProductionUpdateInterface virtual void setHoldDoorOpen(ExitDoorType exitDoor, Bool holdIt) = 0; + //These functions keep track of the special power construction of a new building via a special power instead of standard production interface. + //This was added for the sneak attack building functionality. + virtual const CommandButton* getSpecialPowerConstructionCommandButton() const = 0; + virtual void setSpecialPowerConstructionCommandButton( const CommandButton *commandButton ) = 0; + }; //------------------------------------------------------------------------------------------------- @@ -224,6 +229,11 @@ class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface, virtual UpdateSleepTime update() override; ///< the update + //These functions keep track of the special power construction of a new building via a special power instead of standard production interface. + //This was added for the sneak attack building functionality. + virtual const CommandButton* getSpecialPowerConstructionCommandButton() const override { return m_specialPowerConstructionCommandButton; } + virtual void setSpecialPowerConstructionCommandButton( const CommandButton *commandButton ) override { m_specialPowerConstructionCommandButton = commandButton; } + // DieModuleInterface virtual void onDie( const DamageInfo *damageInfo ) override; @@ -243,6 +253,7 @@ class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface, Bool m_holdOpen; ///< if T, don't allow door to close }; + const CommandButton *m_specialPowerConstructionCommandButton; ///< In a mode to construct a specific building via a special power. (NO NEED TO SAVE DATA ON THIS FIELD) ProductionEntry* m_productionQueue; ///< queue of things we want to build ProductionEntry* m_productionQueueTail; ///< tail pointer for m_productionQueue ProductionID m_uniqueID; ///< unique ID counter for producing units diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/SpecialPowerModule.h b/Generals/Code/GameEngine/Include/GameLogic/Module/SpecialPowerModule.h index 5177857d1cc..d6e55578b82 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/SpecialPowerModule.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/SpecialPowerModule.h @@ -66,6 +66,7 @@ class SpecialPowerModuleInterface virtual void markSpecialPowerTriggered( const Coord3D *location ) = 0; virtual void startPowerRecharge() = 0; virtual const AudioEventRTS& getInitiateSound() const = 0; + virtual Bool isScriptOnly() const = 0; }; //------------------------------------------------------------------------------------------------- @@ -79,9 +80,10 @@ class SpecialPowerModuleData : public BehaviorModuleData static void buildFieldParse(MultiIniFieldParse& p); const SpecialPowerTemplate *m_specialPowerTemplate; ///< pointer to the special power template - Bool m_updateModuleStartsAttack; ///< update module determines when the special power actually starts! If true, update module is required. - Bool m_startsPaused; ///< Paused on creation, someone else will have to unpause (like upgrade module, or script) - AudioEventRTS m_initiateSound; + AudioEventRTS m_initiateSound; + Bool m_updateModuleStartsAttack; ///< update module determines when the special power actually starts! If true, update module is required. + Bool m_startsPaused; ///< Paused on creation, someone else will have to unpause (like upgrade module, or script) + Bool m_scriptedSpecialPowerOnly; }; //------------------------------------------------------------------------------------------------- @@ -152,6 +154,8 @@ class SpecialPowerModule : public BehaviorModule, virtual void startPowerRecharge() override; virtual const AudioEventRTS& getInitiateSound() const override; + virtual Bool isScriptOnly() const override; + protected: Bool initiateIntentToDoSpecialPower( const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ); diff --git a/Generals/Code/GameEngine/Include/GameLogic/Object.h b/Generals/Code/GameEngine/Include/GameLogic/Object.h index ad936c42ebb..25c9590f8a6 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Object.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Object.h @@ -455,6 +455,7 @@ class Object : public Thing, public Snapshot Bool hasAnyDamageWeapon() const; //Kris: a should be used for real weapons that directly inflict damage... not deploy, hack, etc. Bool hasWeaponToDealDamageType(DamageType typeToDeal) const; Real getLargestWeaponRange() const; + UnsignedInt getMostPercentReadyToFireAnyWeapon() const; Weapon* getWeaponInWeaponSlot(WeaponSlotType wslot) const { return m_weaponSet.getWeaponInWeaponSlot(wslot); } @@ -525,6 +526,7 @@ class Object : public Thing, public Snapshot /// return true if the template has the specified special power flag set // @todo: inline Bool hasSpecialPower( SpecialPowerType type ) const; + Bool hasAnySpecialPower() const; void setWeaponBonusCondition(WeaponBonusConditionType wst) { m_weaponBonusCondition |= (1 << wst); } void clearWeaponBonusCondition(WeaponBonusConditionType wst) { m_weaponBonusCondition &= ~(1 << wst); } diff --git a/Generals/Code/GameEngine/Include/GameLogic/WeaponSet.h b/Generals/Code/GameEngine/Include/GameLogic/WeaponSet.h index e0bc297f3d9..a4a611486fb 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/WeaponSet.h +++ b/Generals/Code/GameEngine/Include/GameLogic/WeaponSet.h @@ -211,6 +211,7 @@ class WeaponSet : public Snapshot WeaponSlotType getCurWeaponSlot() const { return m_curWeapon; } Weapon* findWaypointFollowingCapableWeapon(); const Weapon* findAmmoPipShowingWeapon() const; + UnsignedInt getMostPercentReadyToFireAnyWeapon() const; Bool setWeaponLock( WeaponSlotType weaponSlot, WeaponLockType lockType ); void releaseWeaponLock(WeaponLockType lockType); diff --git a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp index cca2ee3ebc7..24400891859 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -1126,41 +1126,336 @@ Player *Player::getCurrentEnemy() } //------------------------------------------------------------------------------------------------- -/** return the player's command center. - if he has none, return null. - if he has multiple, return one arbitrarily. */ +// PlayerObjectFindInfo is used to find a player's object. For example, we iterate through +// to find a player's command center, or a specific building capable of firing the specified +// special power. +// +// if he has none, return null. +// if he has multiple, return one arbitrarily. +//------------------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------------------- +// Iterator data struct +//------------------------------------------------------------------------------------------------- +struct PlayerObjectFindInfo +{ + Player* player; + Object* obj; + SpecialPowerType spType; + const ThingTemplate *thing; + UnsignedInt lowestReadyFrame; + UnsignedInt highestPercentage; + UnsignedInt numReady; +}; - struct FCCInfo +//------------------------------------------------------------------------------------------------- +// Iterator function +// Find the first available command center that is naturally ours (not captured from enemy). +//------------------------------------------------------------------------------------------------- +static void doFindCommandCenter(Object* obj, void* userData) +{ + if (!obj) + return; + + PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; + + if (info->obj == nullptr + && obj->isKindOf(KINDOF_COMMANDCENTER) + && obj->getTemplate()->getDefaultOwningSide() == info->player->getSide() + && !obj->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) + && !obj->testStatus(OBJECT_STATUS_SOLD)) { - Player* player; - Object* cmdCenter; - }; + info->obj = obj; + } +} + +//------------------------------------------------------------------------------------------------- +// Iterator function +// Find first object capable of firing specified special power right now. +//------------------------------------------------------------------------------------------------- +static void doFindSpecialPowerSourceObject( Object *obj, void *userData ) +{ + PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; - static void doFindCommandCenter(Object* obj, void* userData) + if( info->lowestReadyFrame == 0 ) { - if (!obj) - return; + //We already found the best case scenario, so no need to iterate any more. + return; + } + if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) + && !obj->testStatus( OBJECT_STATUS_SOLD ) + && !obj->isEffectivelyDead() ) + { + if( info->spType == SPECIAL_INVALID && obj->hasAnySpecialPower() ) + { + //We just care about find an object that has *any* shortcut capable special power. + //Iterate through the special power modules and look for one. + SpecialPowerModuleInterface *spmInterface = obj->findAnyShortcutSpecialPowerModuleInterface(); + if( spmInterface && !spmInterface->isScriptOnly() ) + { + info->obj = obj; + info->lowestReadyFrame = 0; + return; + } + } + else if( obj->hasSpecialPower( info->spType ) ) + { + SpecialPowerModuleInterface *spmInterface = obj->findSpecialPowerModuleInterface( info->spType ); + if( spmInterface && !spmInterface->isScriptOnly() ) + { + UnsignedInt readyFrame = spmInterface->getReadyFrame(); + +#if defined(RTS_DEBUG) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) + // Everything is ready if timers are debug off'd + if( ! TheGlobalData->m_specialPowerUsesDelay ) + readyFrame = 0; +#endif + // A disabled guy should only be considered as a last resort. We need it to be counted + // so that a disabled button can appear on the shortcut. + if( obj->isDisabled() ) + readyFrame = UINT_MAX - 10; + + if( readyFrame < TheGameLogic->getFrame()) + { + //This special power is ready now and matches, so simply return the + //first one. + info->obj = obj; + info->lowestReadyFrame = 0; + return; + } + else if( readyFrame < info->lowestReadyFrame ) + { + //This special power isn't ready, but it is going to be ready sooner than any others + //we checked (or it's the first one we checked). + info->obj = obj; + info->lowestReadyFrame = readyFrame; + return; + } + } + } + } +} + +//------------------------------------------------------------------------------------------------- +// Iterator function +// Count number of specified special powers that are ready to fire now. +//------------------------------------------------------------------------------------------------- +static void doCountSpecialPowersReady( Object *obj, void *userData ) +{ + PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; + + if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) + && !obj->testStatus( OBJECT_STATUS_SOLD ) + && !obj->isEffectivelyDead() ) + { + if( obj->hasSpecialPower( info->spType ) ) + { + SpecialPowerModuleInterface *spmInterface = obj->findSpecialPowerModuleInterface( info->spType ); + if( spmInterface && !spmInterface->isScriptOnly() ) + { + + if( spmInterface->getSpecialPowerTemplate()->isSharedNSync() && info->numReady == 1 ) + { + //Shared powers don't stack after the first one is counted. + return; + } + + UnsignedInt readyFrame = spmInterface->getReadyFrame(); + +#if defined(RTS_DEBUG) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) + // Everything is ready if timers are debug off'd + if( ! TheGlobalData->m_specialPowerUsesDelay ) + readyFrame = 0; +#endif + + // A disabled guy should only be considered as a last resort. We do not want him counted here + // so that Disabled guys do not go in to the number on the shortcut button. + if( obj->isDisabled() ) + readyFrame = UINT_MAX - 10; + + if( readyFrame < TheGameLogic->getFrame()) + { + //This special power is ready now and matches, so simply return the + //first one. + info->numReady++; + } + } + } + } +} + +//------------------------------------------------------------------------------------------------- +static void doFindMostReadyWeaponForThing( Object *obj, void *userData ) +{ + PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; + + if( info->highestPercentage >= 100 ) + { + //We already found the best case scenario, so no need to iterate any more. + return; + } - FCCInfo* info = (FCCInfo*)userData; + if( info->thing && info->thing->isEquivalentTo( obj->getTemplate() ) ) + { + if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) + && !obj->testStatus( OBJECT_STATUS_SOLD ) + && !obj->isEffectivelyDead() ) + { + if( obj->hasAnyWeapon() ) + { + UnsignedInt percentage = obj->getMostPercentReadyToFireAnyWeapon(); + if( percentage > info->highestPercentage ) + { + //This weapon is more ready than any others we've checked. + info->obj = obj; + info->highestPercentage = percentage; + return; + } + } + } + } +} + +//------------------------------------------------------------------------------------------------- +static void doFindMostReadySpecialPowerForThing( Object *obj, void *userData ) +{ + PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; + + if( info->highestPercentage >= 100 ) + { + //We already found the best case scenario, so no need to iterate any more. + return; + } + + if( info->thing && info->thing->isEquivalentTo( obj->getTemplate() ) ) + { + if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) + && !obj->testStatus( OBJECT_STATUS_SOLD ) + && !obj->isEffectivelyDead() ) + { + // search the modules for the one with the matching template + for( BehaviorModule** m = obj->getBehaviorModules(); *m; ++m ) + { + SpecialPowerModuleInterface* sp = (*m)->getSpecialPower(); + if (!sp) + continue; + + UnsignedInt percentage = sp->getPercentReady(); + if( percentage > info->highestPercentage ) + { + //This weapon is more ready than any others we've checked. + info->obj = obj; + info->highestPercentage = percentage; + } + } + } + } +} + +//------------------------------------------------------------------------------------------------- +static void doFindExistingObjectWithThingTemplate( Object *obj, void *userData ) +{ + PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; + + if( info->obj ) + { + //We already found a matching obj, so return + return; + } - if (info->cmdCenter == nullptr - && obj->isKindOf(KINDOF_COMMANDCENTER) - && obj->getTemplate()->getDefaultOwningSide() == info->player->getSide() - && !obj->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) - && !obj->testStatus(OBJECT_STATUS_SOLD)) + if( info->thing && info->thing->isEquivalentTo( obj->getTemplate() ) ) + { + if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) + && !obj->testStatus( OBJECT_STATUS_SOLD ) + && !obj->isEffectivelyDead() ) { - info->cmdCenter = obj; + //We found one. + info->obj = obj; } } +} +//------------------------------------------------------------------------------------------------- Object* Player::findNaturalCommandCenter() { - FCCInfo info; + PlayerObjectFindInfo info; info.player = this; - info.cmdCenter = nullptr; + info.obj = nullptr; iterateObjects(doFindCommandCenter, &info); - return info.cmdCenter; + return info.obj; +} + +//------------------------------------------------------------------------------------------------- +Object* Player::findMostReadyShortcutSpecialPowerOfType( SpecialPowerType spType ) +{ + PlayerObjectFindInfo info; + info.player = this; + info.obj = nullptr; + info.spType = spType; + info.lowestReadyFrame = 0xffffffff; + iterateObjects( doFindSpecialPowerSourceObject, &info ); + return info.obj; +} + +//------------------------------------------------------------------------------------------------- +Object* Player::findMostReadyShortcutWeaponForThing( const ThingTemplate *thing, UnsignedInt &mostReadyPercentage ) +{ + PlayerObjectFindInfo info; + info.player = this; + info.obj = nullptr; + info.thing = thing; + info.highestPercentage = 0; + iterateObjects( doFindMostReadyWeaponForThing, &info ); + mostReadyPercentage = info.highestPercentage; + return info.obj; +} + +//------------------------------------------------------------------------------------------------- +Object* Player::findMostReadyShortcutSpecialPowerForThing( const ThingTemplate *thing, UnsignedInt &mostReadyPercentage ) +{ + PlayerObjectFindInfo info; + info.player = this; + info.obj = nullptr; + info.thing = thing; + info.highestPercentage = 0; + iterateObjects( doFindMostReadySpecialPowerForThing, &info ); + mostReadyPercentage = info.highestPercentage; + return info.obj; +} + +//------------------------------------------------------------------------------------------------- +Object* Player::findAnyExistingObjectWithThingTemplate( const ThingTemplate *thing ) +{ + PlayerObjectFindInfo info; + info.player = this; + info.obj = nullptr; + info.thing = thing; + iterateObjects( doFindExistingObjectWithThingTemplate, &info ); + return info.obj; +} + +//------------------------------------------------------------------------------------------------- +// Finds a short-cut firing special power of any type arbitrarily. +//------------------------------------------------------------------------------------------------- +Bool Player::hasAnyShortcutSpecialPower() +{ + PlayerObjectFindInfo info; + info.player = this; + info.obj = nullptr; + info.spType = SPECIAL_INVALID; //Invalid dictates that we don't care about the type. + info.lowestReadyFrame = 0xffffffff; + iterateObjects( doFindSpecialPowerSourceObject, &info ); + return info.obj; +} + +//------------------------------------------------------------------------------------------------- +Int Player::countReadyShortcutSpecialPowersOfType( SpecialPowerType spType ) +{ + PlayerObjectFindInfo info; + info.spType = spType; + info.numReady = 0; + iterateObjects( doCountSpecialPowersReady, &info ); + return info.numReady; } //------------------------------------------------------------------------------------------------- diff --git a/Generals/Code/GameEngine/Source/Common/System/DisabledTypes.cpp b/Generals/Code/GameEngine/Source/Common/System/DisabledTypes.cpp index 93e30cbe4b0..5c0ffee7146 100644 --- a/Generals/Code/GameEngine/Source/Common/System/DisabledTypes.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/DisabledTypes.cpp @@ -42,6 +42,10 @@ const char* const DisabledMaskType::s_bitNameList[] = "DISABLED_UNDERPOWERED", "DISABLED_FREEFALL", + "DISABLED_AWESTRUCK", + "DISABLED_BRAINWASHED", + "DISABLED_SUBDUED", + "DISABLED_SCRIPT_DISABLED", "DISABLED_SCRIPT_UNDERPOWERED", diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index 9d7f4cd7e72..1cfd642a927 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -65,6 +65,7 @@ #include "GameClient/ControlBarScheme.h" #include "GameClient/Drawable.h" #include "GameClient/Display.h" +#include "GameClient/DisplayStringManager.h" #include "GameClient/GameClient.h" #include "GameClient/GameWindowManager.h" #include "GameClient/GameText.h" @@ -193,12 +194,16 @@ void ControlBar::populatePurchaseScience( Player* player ) commandSet8 = findCommandSet(player->getPlayerTemplate()->getPurchaseScienceCommandSetRank8()); // TEMP WILL CHANGE TO PROPER WAY ONCE WORKING for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_1; i++ ) - m_sciencePurchaseWindowsRank1[i]->winHide(TRUE); + if (m_sciencePurchaseWindowsRank1[i] != nullptr) + m_sciencePurchaseWindowsRank1[i]->winHide(TRUE); + for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_3; i++ ) - m_sciencePurchaseWindowsRank3[i]->winHide(TRUE); - for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) - m_sciencePurchaseWindowsRank8[i]->winHide(TRUE); + if (m_sciencePurchaseWindowsRank3[i] != nullptr) + m_sciencePurchaseWindowsRank3[i]->winHide(TRUE); + for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) + if (m_sciencePurchaseWindowsRank8[i] != nullptr) + m_sciencePurchaseWindowsRank8[i]->winHide(TRUE); // if no command set match is found hide all the buttons if( commandSet1 == nullptr || @@ -210,12 +215,14 @@ void ControlBar::populatePurchaseScience( Player* player ) const CommandButton *commandButton; for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_1; i++ ) { + if (m_sciencePurchaseWindowsRank1[i] == nullptr) + continue; // get command button commandButton = commandSet1->getCommandButton(i); // if button is not present, just hide the window - if( commandButton == nullptr ) + if( commandButton == nullptr || BitIsSet( commandButton->getOptions(), SCRIPT_ONLY ) ) { // hide window on interface m_sciencePurchaseWindowsRank1[ i ]->winHide( TRUE ); @@ -270,12 +277,14 @@ void ControlBar::populatePurchaseScience( Player* player ) for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_3; i++ ) { + if (m_sciencePurchaseWindowsRank3[i] == nullptr) + continue; // get command button commandButton = commandSet3->getCommandButton(i); // if button is not present, just hide the window - if( commandButton == nullptr ) + if( commandButton == nullptr || BitIsSet( commandButton->getOptions(), SCRIPT_ONLY ) ) { // hide window on interface m_sciencePurchaseWindowsRank3[ i ]->winHide( TRUE ); @@ -333,12 +342,14 @@ void ControlBar::populatePurchaseScience( Player* player ) for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) { + if (m_sciencePurchaseWindowsRank8[i] == nullptr) + continue; // get command button commandButton = commandSet8->getCommandButton(i); // if button is not present, just hide the window - if( commandButton == nullptr ) + if( commandButton == nullptr || BitIsSet( commandButton->getOptions(), SCRIPT_ONLY ) ) { // hide window on interface m_sciencePurchaseWindowsRank8[ i ]->winHide( TRUE ); @@ -399,12 +410,16 @@ void ControlBar::populatePurchaseScience( Player* player ) GadgetStaticTextSetText(win, tempUS); } +#if RTS_GENERALS win = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], TheNameKeyGenerator->nameToKey( "GeneralsExpPoints.wnd:StaticTextLevel" ) ); if(win) { tempUS.format(TheGameText->fetch("SCIENCE:Rank"), player->getRankLevel()); GadgetStaticTextSetText(win, tempUS); } +#else + // redundant to StaticTextTitle in the Zero Hour context +#endif win = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], TheNameKeyGenerator->nameToKey( "GeneralsExpPoints.wnd:ProgressBarExperience" ) ); if(win) @@ -642,9 +657,22 @@ Bool CommandButton::isValidToUseOn(const Object *sourceObj, const Object *target return false; } + Coord3D pos; + if( targetLocation ) + { + pos.set( targetLocation ); + } + if( BitIsSet( m_options, NEED_TARGET_POS ) && !targetLocation ) { - return false; + if( targetObj ) + { + pos.set( targetObj->getPosition() ); + } + else + { + return false; + } } if( BitIsSet( m_options, COMMAND_OPTION_NEED_OBJECT_TARGET ) ) @@ -654,7 +682,7 @@ Bool CommandButton::isValidToUseOn(const Object *sourceObj, const Object *target if( BitIsSet( m_options, NEED_TARGET_POS ) ) { - return TheActionManager->canDoSpecialPowerAtLocation( sourceObj, targetLocation, commandSource, m_specialPower, nullptr, m_options, false ); + return TheActionManager->canDoSpecialPowerAtLocation( sourceObj, &pos, commandSource, m_specialPower, nullptr, m_options, false ); } return TheActionManager->canDoSpecialPower( sourceObj, m_specialPower, commandSource, m_options, false ); @@ -702,6 +730,12 @@ const FieldParse CommandSet::m_commandSetFieldParseTable[] = { "10", CommandSet::parseCommandButton, (void *)9, offsetof( CommandSet, m_command ) }, { "11", CommandSet::parseCommandButton, (void *)10, offsetof( CommandSet, m_command ) }, { "12", CommandSet::parseCommandButton, (void *)11, offsetof( CommandSet, m_command ) }, + { "13", CommandSet::parseCommandButton, (void *)12, offsetof( CommandSet, m_command ) }, + { "14", CommandSet::parseCommandButton, (void *)13, offsetof( CommandSet, m_command ) }, + { "15", CommandSet::parseCommandButton, (void *)14, offsetof( CommandSet, m_command ) }, + { "16", CommandSet::parseCommandButton, (void *)15, offsetof( CommandSet, m_command ) }, + { "17", CommandSet::parseCommandButton, (void *)16, offsetof( CommandSet, m_command ) }, + { "18", CommandSet::parseCommandButton, (void *)17, offsetof( CommandSet, m_command ) }, { nullptr, nullptr, nullptr, 0 } }; @@ -853,10 +887,12 @@ ControlBar::ControlBar() m_observedPlayer = nullptr; m_buildToolTipLayout = nullptr; m_showBuildToolTipLayout = FALSE; + m_animateDownWin1Pos.x = m_animateDownWin1Pos.y = 0; m_animateDownWin1Size.x = m_animateDownWin1Size.y = 0; m_animateDownWin2Pos.x = m_animateDownWin2Pos.y = 0; m_animateDownWin2Size.x = m_animateDownWin2Size.y = 0; + m_animateDownWindow = nullptr; m_animTime = 0; @@ -1120,7 +1156,8 @@ void ControlBar::init() id = TheNameKeyGenerator->nameToKey( windowName.str() ); m_sciencePurchaseWindowsRank1[ i ] = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], id ); - m_sciencePurchaseWindowsRank1[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); + if (m_sciencePurchaseWindowsRank1[ i ] != nullptr) + m_sciencePurchaseWindowsRank1[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); } for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_3; i++ ) { @@ -1128,7 +1165,8 @@ void ControlBar::init() id = TheNameKeyGenerator->nameToKey( windowName.str() ); m_sciencePurchaseWindowsRank3[ i ] = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], id ); - m_sciencePurchaseWindowsRank3[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); + if (m_sciencePurchaseWindowsRank3[ i ] != nullptr) + m_sciencePurchaseWindowsRank3[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); } for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) @@ -1137,7 +1175,8 @@ void ControlBar::init() id = TheNameKeyGenerator->nameToKey( windowName.str() ); m_sciencePurchaseWindowsRank8[ i ] = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], id ); - m_sciencePurchaseWindowsRank8[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); + if (m_sciencePurchaseWindowsRank8[ i ] != nullptr) + m_sciencePurchaseWindowsRank8[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); } // keep a pointer to the window making up the right HUD display @@ -2598,7 +2637,7 @@ void ControlBar::setPortraitByObject( Object *obj ) setPortraitByObject( nullptr ); return; } - StealthUpdate* stealth = obj->getStealth(); + StealthUpdate *stealth = obj->getStealth(); if( stealth && stealth->isDisguised() ) { //Fake player upgrades too! @@ -3081,6 +3120,15 @@ void ControlBar::updateSlotExitImage( const Image *image ) if(!image) return; + //Kris: + //Other than this being a completely ridiculously retarded idea, I'm not inclined + //to recode this in a better way, yikes! Btw, I DID NOT CODE THIS! But this is + //what this does: The button images are overridden by a faction specific icon. + //The proper way to fix this would be to make a commandbutton option and loop + //through all buttons on init to replace the icon. We need a system like this + //for neutral buildings which can have a different empty inventory icon based + //on the faction player. + CommandButton *cmdButton = findNonConstCommandButton( "Command_StructureExit" ); if(cmdButton) cmdButton->setButtonImage(image); @@ -3088,6 +3136,15 @@ void ControlBar::updateSlotExitImage( const Image *image ) cmdButton = findNonConstCommandButton( "Command_TransportExit" ); if(cmdButton) cmdButton->setButtonImage(image); + + cmdButton = findNonConstCommandButton( "Command_BunkerExit" ); + if(cmdButton) + cmdButton->setButtonImage(image); + + cmdButton = findNonConstCommandButton( "Command_FireBaseExit" ); + if(cmdButton) + cmdButton->setButtonImage(image); + } void ControlBar::updateUpDownImages( const Image *toggleButtonUpIn, const Image *toggleButtonUpOn, const Image *toggleButtonUpPushed, @@ -3218,24 +3275,26 @@ void ControlBar::initSpecialPowershortcutBar( Player *player) parentName = layoutName; parentName.concat(":ButtonParent%d"); m_currentlyUsedSpecialPowersButtons = MIN(pt->getSpecialPowerShortcutButtonCount(), MAX_SPECIAL_POWER_SHORTCUTS); - for( i = 0; i < m_currentlyUsedSpecialPowersButtons; i++ ) + for( i = 0; i < MAX_SPECIAL_POWER_SHORTCUTS; i++ ) { windowName.format( tempName, i+1 ); id = TheNameKeyGenerator->nameToKey( windowName.str() ); m_specialPowerShortcutButtons[ i ] = TheWindowManager->winGetWindowFromId( m_specialPowerShortcutParent, id ); - m_specialPowerShortcutButtons[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); - - windowName.format( parentName, i+1 ); - id = TheNameKeyGenerator->nameToKey( windowName.str() ); - m_specialPowerShortcutButtonParents[ i ] = - TheWindowManager->winGetWindowFromId( m_specialPowerShortcutParent, id ); + if (m_specialPowerShortcutButtons[ i ] != nullptr) + { + m_specialPowerShortcutButtons[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); + // Oh god... this is a total hack for shortcut buttons to handle rendering text top left corner... + m_specialPowerShortcutButtons[ i ]->winSetStatus( WIN_STATUS_SHORTCUT_BUTTON ); + windowName.format( parentName, i+1 ); + id = TheNameKeyGenerator->nameToKey( windowName.str() ); + m_specialPowerShortcutButtonParents[ i ] = + TheWindowManager->winGetWindowFromId( m_specialPowerShortcutParent, id ); + } } - - } void ControlBar::populateSpecialPowerShortcut( Player *player) @@ -3246,7 +3305,7 @@ void ControlBar::populateSpecialPowerShortcut( Player *player) || !player->isLocalPlayer() || m_currentlyUsedSpecialPowersButtons == 0 || m_specialPowerShortcutButtons == nullptr || m_specialPowerShortcutButtonParents == nullptr) return; - for( i = 0; i < m_currentlyUsedSpecialPowersButtons; ++i ) + for( i = 0; i < MAX_SPECIAL_POWER_SHORTCUTS; ++i ) { if (m_specialPowerShortcutButtons[i]) m_specialPowerShortcutButtons[i]->winHide(TRUE); @@ -3281,87 +3340,182 @@ void ControlBar::populateSpecialPowerShortcut( Player *player) else { + if( BitIsSet( commandButton->getOptions(), NEED_UPGRADE ) ) + { + const UpgradeTemplate *upgrade = commandButton->getUpgradeTemplate(); + if( upgrade && !ThePlayerList->getLocalPlayer()->hasUpgradeComplete( upgrade->getUpgradeMask() ) ) + { + //Kris: 8/13/03 - Don't show shortcut buttons that require upgrades we don't have. As far as + //I know, only the radar van scan has this. The MOAB is handled differently (sciences). + continue; + } + } + + // + // commands that require sciences we don't have are hidden so they never show up + // cause we can never pick "another" general technology throughout the game + // + if( BitIsSet( commandButton->getOptions(), NEED_SPECIAL_POWER_SCIENCE ) ) + { + const SpecialPowerTemplate *power = commandButton->getSpecialPowerTemplate(); + if( !power ) + { + //Should have the power.. button is probably missing the SpecialPower = xxx entry. + DEBUG_CRASH( ("CommandButton %s needs a SpecialPower entry, but it's either incorrect or missing.", commandButton->getName().str()) ); + continue; + } - // - // commands that require sciences we don't have are hidden so they never show up - // cause we can never pick "another" general technology throughout the game - // - if( BitIsSet( commandButton->getOptions(), NEED_SPECIAL_POWER_SCIENCE ) ) + //We just need to find something that has the power. + Object *obj = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( commandButton->getSpecialPowerTemplate()->getSpecialPowerType() ); + if( !obj ) { - const SpecialPowerTemplate *power = commandButton->getSpecialPowerTemplate(); + continue; + } - if( power && power->getRequiredScience() != SCIENCE_INVALID ) + if( power->getRequiredScience() != SCIENCE_INVALID ) + { + if( player->hasScience( power->getRequiredScience() ) == FALSE ) { - if( player->hasScience( power->getRequiredScience() ) == FALSE ) + //Hide the power + //m_specialPowerShortcutButtons[ i ]->winHide( TRUE ); + continue; + } + else + { + //The player does have the special power! Now determine if the images require + //enhancement based on upgraded versions. This is determined by the command + //button specifying a vector of sciences in the command button. + Int bestIndex = -1; + ScienceType science; + for( size_t scienceIndex = 0; scienceIndex < commandButton->getScienceVec().size(); ++scienceIndex ) { - //Hide the power - //m_specialPowerShortcutButtons[ i ]->winHide( TRUE ); - continue; + science = commandButton->getScienceVec()[ scienceIndex ]; + + //Keep going until we reach the end or don't have the required science! + if( player->hasScience( science ) ) + { + bestIndex = scienceIndex; + } + else + { + break; + } } - else + + if( bestIndex != -1 ) { - //The player does have the special power! Now determine if the images require - //enhancement based on upgraded versions. This is determined by the command - //button specifying a vector of sciences in the command button. - Int bestIndex = -1; - ScienceType science; - for( size_t scienceIndex = 0; scienceIndex < commandButton->getScienceVec().size(); ++scienceIndex ) + //Now get the best sciencetype. + science = commandButton->getScienceVec()[ bestIndex ]; + + const CommandSet *commandSet1; + const CommandSet *commandSet3; + const CommandSet *commandSet8; + Int i; + + // get command set + if( !player || !player->getPlayerTemplate() + || player->getPlayerTemplate()->getPurchaseScienceCommandSetRank1().isEmpty() + || player->getPlayerTemplate()->getPurchaseScienceCommandSetRank3().isEmpty() + || player->getPlayerTemplate()->getPurchaseScienceCommandSetRank8().isEmpty() ) + { + continue; + } + commandSet1 = findCommandSet( player->getPlayerTemplate()->getPurchaseScienceCommandSetRank1() ); + commandSet3 = findCommandSet( player->getPlayerTemplate()->getPurchaseScienceCommandSetRank3() ); + commandSet8 = findCommandSet( player->getPlayerTemplate()->getPurchaseScienceCommandSetRank8() ); + + if( !commandSet1 || !commandSet3 || !commandSet8 ) { - science = commandButton->getScienceVec()[ scienceIndex ]; + continue; + } - //Keep going until we reach the end or don't have the required science! - if( player->hasScience( science ) ) + Bool found = FALSE; + for( i = 0; !found && i < MAX_PURCHASE_SCIENCE_RANK_1; i++ ) + { + const CommandButton *command = commandSet1->getCommandButton( i ); + if( command && command->getCommandType() == GUI_COMMAND_PURCHASE_SCIENCE ) { - bestIndex = scienceIndex; + //All purchase sciences specify a single science. + if( command->getScienceVec().empty() ) + { + DEBUG_CRASH( ("Commandbutton %s is a purchase science button without any science! Please add it.", command->getName().str() ) ); + } + else if( command->getScienceVec()[0] == science ) + { + commandButton->copyImagesFrom( command, TRUE ); + commandButton->copyButtonTextFrom( command, TRUE, TRUE ); + found = TRUE; + break; + } } - else + } + for( i = 0; !found && i < MAX_PURCHASE_SCIENCE_RANK_3; i++ ) + { + const CommandButton *command = commandSet3->getCommandButton( i ); + if( command && command->getCommandType() == GUI_COMMAND_PURCHASE_SCIENCE ) { - break; + //All purchase sciences specify a single science. + if( command->getScienceVec().empty() ) + { + DEBUG_CRASH( ("Commandbutton %s is a purchase science button without any science! Please add it.", command->getName().str() ) ); + } + else if( command->getScienceVec()[0] == science ) + { + commandButton->copyImagesFrom( command, TRUE ); + commandButton->copyButtonTextFrom( command, TRUE, TRUE ); + found = TRUE; + break; + } } } - - if( bestIndex != -1 ) + for( i = 0; !found && i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) { - //Now get the best sciencetype. - science = commandButton->getScienceVec()[ bestIndex ]; - - //Now we have to search through the command buttons to find a matching purchase science button. - for( const CommandButton *command = m_commandButtons; command; command = command->getNext() ) + const CommandButton *command = commandSet8->getCommandButton( i ); + if( command && command->getCommandType() == GUI_COMMAND_PURCHASE_SCIENCE ) { - if( command->getCommandType() == GUI_COMMAND_PURCHASE_SCIENCE ) + //All purchase sciences specify a single science. + if( command->getScienceVec().empty() ) + { + DEBUG_CRASH( ("Commandbutton %s is a purchase science button without any science! Please add it.", command->getName().str() ) ); + } + else if( command->getScienceVec()[0] == science ) { - //All purchase sciences specify a single science. - if( command->getScienceVec().empty() ) - { - DEBUG_CRASH( ("Commandbutton %s is a purchase science button without any science! Please add it.", command->getName().str() ) ); - } - else if( command->getScienceVec()[0] == science ) - { - commandButton->copyImagesFrom( command, true ); - } + commandButton->copyImagesFrom( command, TRUE ); + commandButton->copyButtonTextFrom( command, TRUE, TRUE ); + found = TRUE; + break; } } } - - } } } - // make sure the window is not hidden - m_specialPowerShortcutButtons[ currentButton ]->winHide( FALSE ); - m_specialPowerShortcutButtonParents[ currentButton ]->winHide( FALSE ); - // enable by default - m_specialPowerShortcutButtons[ currentButton ]->winEnable( TRUE ); - m_specialPowerShortcutButtonParents[ currentButton ]->winEnable( TRUE ); - - // populate the visible button with data from the command button - setControlCommand( m_specialPowerShortcutButtons[ currentButton ], commandButton ); - GadgetButtonSetAltSound(m_specialPowerShortcutButtons[ currentButton ], "GUIGenShortcutClick"); - currentButton++; - + } + else if( commandButton->getCommandType() == GUI_COMMAND_SELECT_ALL_UNITS_OF_TYPE ) + { + //Make sure we actually have an object of type that we want to be able to select. + Object *obj = ThePlayerList->getLocalPlayer()->findAnyExistingObjectWithThingTemplate( commandButton->getThingTemplate() ); + if( !obj ) + { + continue; + } } + // make sure the window is not hidden + m_specialPowerShortcutButtons[ currentButton ]->winHide( FALSE ); + m_specialPowerShortcutButtonParents[ currentButton ]->winHide( FALSE ); + // enable by default + m_specialPowerShortcutButtons[ currentButton ]->winEnable( TRUE ); + m_specialPowerShortcutButtonParents[ currentButton ]->winEnable( TRUE ); + + // populate the visible button with data from the command button + setControlCommand( m_specialPowerShortcutButtons[ currentButton ], commandButton ); + GadgetButtonSetAltSound(m_specialPowerShortcutButtons[ currentButton ], "GUIGenShortcutClick"); + currentButton++; + + } + } if(m_contextParent[ CP_MASTER ] && !m_contextParent[ CP_MASTER ]->winIsHidden() && m_specialPowerShortcutParent->winIsHidden()) { @@ -3371,19 +3525,55 @@ void ControlBar::populateSpecialPowerShortcut( Player *player) updateSpecialPowerShortcut(); } +//------------------------------------------------------------------------------------------------- +Bool ControlBar::hasAnyShortcutSelection() const +{ + for( Int i = 0; i < m_currentlyUsedSpecialPowersButtons; i++ ) + { + GameWindow *win; + const CommandButton *command; + + win = m_specialPowerShortcutButtons[ i ]; + if( win->winIsHidden() == TRUE ) + continue; + + // get the command from the control + command = (const CommandButton *)GadgetButtonGetData(win); + if( !command ) + continue; + + if( command->getCommandType() == GUI_COMMAND_SELECT_ALL_UNITS_OF_TYPE ) + { + //We found one, so we'll always show shortcuts! + return TRUE; + } + } + return FALSE; +} + +//------------------------------------------------------------------------------------------------- void ControlBar::updateSpecialPowerShortcut() { if(!m_specialPowerShortcutParent || !m_specialPowerShortcutButtons || !ThePlayerList || !ThePlayerList->getLocalPlayer()) return; - if(ThePlayerList->getLocalPlayer()->findNaturalCommandCenter() && - m_specialPowerShortcutParent->winIsHidden() && - m_contextParent[ CP_MASTER ] && !m_contextParent[ CP_MASTER ]->winIsHidden()) + + Bool hasShortcutSelectionButtons = hasAnyShortcutSelection(); + Bool hasAnyShortcutSpecialPower = ThePlayerList->getLocalPlayer()->hasAnyShortcutSpecialPower(); + + Bool hasValidShortcutButton = hasShortcutSelectionButtons || hasAnyShortcutSpecialPower; + + if( hasValidShortcutButton + && m_specialPowerShortcutParent->winIsHidden() + && m_contextParent[ CP_MASTER ] + && !m_contextParent[ CP_MASTER ]->winIsHidden() ) { showSpecialPowerShortcut(); animateSpecialPowerShortcut(TRUE); } - else if( !ThePlayerList->getLocalPlayer()->findNaturalCommandCenter() && !m_specialPowerShortcutParent->winIsHidden() && m_animateWindowManagerForGenShortcuts->isFinished()) + else if( !hasValidShortcutButton + && !m_specialPowerShortcutParent->winIsHidden() + && m_animateWindowManagerForGenShortcuts->isFinished() ) { animateSpecialPowerShortcut(FALSE); } @@ -3422,8 +3612,47 @@ void ControlBar::updateSpecialPowerShortcut() // is the command available CommandAvailability availability = COMMAND_RESTRICTED; - if(ThePlayerList->getLocalPlayer()->findNaturalCommandCenter()) - availability = getCommandAvailability( command,ThePlayerList->getLocalPlayer()->findNaturalCommandCenter() , win ); + + const SpecialPowerTemplate *spTemplate = command->getSpecialPowerTemplate(); + Object *obj = nullptr; + if( spTemplate ) + { + obj = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( command->getSpecialPowerTemplate()->getSpecialPowerType() ); + availability = getCommandAvailability( command, obj, win ); + } + else if( command->getCommandType() == GUI_COMMAND_SELECT_ALL_UNITS_OF_TYPE ) + { + availability = COMMAND_HIDDEN; + Object *obj = ThePlayerList->getLocalPlayer()->findAnyExistingObjectWithThingTemplate( command->getThingTemplate() ); + if( obj ) + { + //Make command available if it isn't a special power template shortcut power. + availability = COMMAND_AVAILABLE; + + UnsignedInt mostReadyPercentage; + obj = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerForThing( command->getThingTemplate(), mostReadyPercentage ); + if( obj ) + { + //Ugh... hacky. + //Look for a command button for a special power and if so, then get the command availability for it. + const CommandSet *commandSet = findCommandSet( obj->getCommandSetString() ); + if( commandSet ) + { + for( Int commandIndex = 0; commandIndex < MAX_COMMANDS_PER_SET; commandIndex++ ) + { + const CommandButton *evalButton = commandSet->getCommandButton( commandIndex ); + GameWindow *evalButtonWin = m_commandWindows[ commandIndex ]; + if( evalButton && evalButton->getCommandType() == GUI_COMMAND_SPECIAL_POWER ) + { + //We want to evaluate the special powerbutton... but apply the clock overlay to our button! + availability = getCommandAvailability( evalButton, obj, evalButtonWin, win ); + break; + } + } + } + } + } + } // enable/disable the window control switch( availability ) @@ -3447,8 +3676,62 @@ void ControlBar::updateSpecialPowerShortcut() break; } } +} + +//------------------------------------------------------------------------------------------------- +void ControlBar::drawSpecialPowerShortcutMultiplierText() +{ + for( Int i = 0; i < m_currentlyUsedSpecialPowersButtons; i++ ) + { + GameWindow *win; + const CommandButton *command; + // get the window + win = m_specialPowerShortcutButtons[ i ]; + + if( win->winIsHidden() == TRUE ) + continue; + // get the command from the control + command = (const CommandButton *)GadgetButtonGetData(win); + //command = (const CommandButton *)win->winGetUserData(); + if( command == nullptr ) + continue; + //draw superweapon ready multipliers + for( int i = 0; i < MAX_SPECIAL_POWER_SHORTCUTS; i++ ) + { + if( !m_shortcutDisplayStrings[ i ] ) + { + //m_shortcutDisplayStrings[ i ] = TheDisplayStringManager->newDisplayString(); + //m_shortcutDisplayStrings[ i ]->setFont( TheFontLibrary->getFont( "Arial", 16, false ) ); + } + + const SpecialPowerTemplate *spTemplate = command->getSpecialPowerTemplate(); + Int numReady = 0; + if( spTemplate ) + { + numReady = ThePlayerList->getLocalPlayer()->countReadyShortcutSpecialPowersOfType( spTemplate->getSpecialPowerType() ); + } + if( numReady > 1 ) // Lorenzen changed... Displaying a "1" is superfluous + { + UnicodeString unibuffer; + unibuffer.format( L"%d", numReady ); + + GadgetButtonSetText( win, unibuffer ); + + //m_shortcutDisplayStrings[ i ]->setText( unibuffer ); + //TheControlBar->m_shortcutDisplayStrings[ i ]->draw( 600, i * 40 + 40, GameMakeColor(255,255,255,255), GameMakeColor(0,0,0,0), 0, 0 ); + } + else + { + UnicodeString unibuffer; + GadgetButtonSetText( win, unibuffer ); + //TheDisplayStringManager->freeDisplayString( m_shortcutDisplayStrings[ i ] ); + //m_shortcutDisplayStrings[ i ] = nullptr; + } + } + } } + void ControlBar::animateSpecialPowerShortcut( Bool isOn ) { if(!m_specialPowerShortcutParent || !m_animateWindowManagerForGenShortcuts || !m_currentlyUsedSpecialPowersButtons) @@ -3490,7 +3773,7 @@ void ControlBar::showSpecialPowerShortcut() break; } } - if(dontAnimate || !ThePlayerList->getLocalPlayer()->findNaturalCommandCenter()) + if( dontAnimate || (!ThePlayerList->getLocalPlayer()->hasAnyShortcutSpecialPower() && !hasAnyShortcutSelection()) ) return; m_specialPowerShortcutParent->winHide(FALSE); populateSpecialPowerShortcut(ThePlayerList->getLocalPlayer()); diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp index d65db406013..f5e1cc6070e 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp @@ -35,6 +35,7 @@ #include "Common/ThingFactory.h" #include "Common/Player.h" #include "Common/PlayerList.h" +#include "Common/PlayerTemplate.h" #include "Common/SpecialPower.h" #include "Common/Upgrade.h" #include "Common/BuildAssistant.h" @@ -346,51 +347,120 @@ void ControlBar::populateCommand( Object *obj ) if( power && power->getRequiredScience() != SCIENCE_INVALID ) { - if( player->hasScience( power->getRequiredScience() ) == FALSE ) + if( commandButton->getCommandType() != GUI_COMMAND_PURCHASE_SCIENCE && + commandButton->getCommandType() != GUI_COMMAND_PLAYER_UPGRADE && + commandButton->getCommandType() != GUI_COMMAND_OBJECT_UPGRADE ) { - //Hide the power - m_commandWindows[ i ]->winHide( TRUE ); - } - else - { - //The player does have the special power! Now determine if the images require - //enhancement based on upgraded versions. This is determined by the command - //button specifying a vector of sciences in the command button. - Int bestIndex = -1; - ScienceType science; - for( size_t scienceIndex = 0; scienceIndex < commandButton->getScienceVec().size(); ++scienceIndex ) + if( player->hasScience( power->getRequiredScience() ) == FALSE ) { - science = commandButton->getScienceVec()[ scienceIndex ]; - - //Keep going until we reach the end or don't have the required science! - if( player->hasScience( science ) ) + //Hide the power + m_commandWindows[ i ]->winHide( TRUE ); + } + else + { + //The player does have the special power! Now determine if the images require + //enhancement based on upgraded versions. This is determined by the command + //button specifying a vector of sciences in the command button. + Int bestIndex = -1; + ScienceType science; + for( size_t scienceIndex = 0; scienceIndex < commandButton->getScienceVec().size(); ++scienceIndex ) { - bestIndex = scienceIndex; + science = commandButton->getScienceVec()[ scienceIndex ]; + + //Keep going until we reach the end or don't have the required science! + if( player->hasScience( science ) ) + { + bestIndex = scienceIndex; + } + else + { + break; + } } - else + + if( bestIndex != -1 ) { - break; - } - } + //Now get the best sciencetype. + science = commandButton->getScienceVec()[ bestIndex ]; + + const CommandSet *commandSet1; + const CommandSet *commandSet3; + const CommandSet *commandSet8; + Int i; + + // get command set + if( !player || !player->getPlayerTemplate() + || player->getPlayerTemplate()->getPurchaseScienceCommandSetRank1().isEmpty() + || player->getPlayerTemplate()->getPurchaseScienceCommandSetRank3().isEmpty() + || player->getPlayerTemplate()->getPurchaseScienceCommandSetRank8().isEmpty() ) + { + continue; + } + commandSet1 = findCommandSet( player->getPlayerTemplate()->getPurchaseScienceCommandSetRank1() ); + commandSet3 = findCommandSet( player->getPlayerTemplate()->getPurchaseScienceCommandSetRank3() ); + commandSet8 = findCommandSet( player->getPlayerTemplate()->getPurchaseScienceCommandSetRank8() ); - if( bestIndex != -1 ) - { - //Now get the best sciencetype. - science = commandButton->getScienceVec()[ bestIndex ]; + if( !commandSet1 || !commandSet3 || !commandSet8 ) + { + continue; + } - //Now we have to search through the command buttons to find a matching purchase science button. - for( const CommandButton *command = m_commandButtons; command; command = command->getNext() ) - { - if( command && command->getCommandType() == GUI_COMMAND_PURCHASE_SCIENCE ) + Bool found = FALSE; + for( i = 0; !found && i < MAX_PURCHASE_SCIENCE_RANK_1; i++ ) { - //All purchase sciences specify a single science. - if( command->getScienceVec().empty() ) + const CommandButton *command = commandSet1->getCommandButton( i ); + if( command && command->getCommandType() == GUI_COMMAND_PURCHASE_SCIENCE ) { - DEBUG_CRASH( ("Commandbutton %s is a purchase science button without any science! Please add it.", command->getName().str() ) ); + //All purchase sciences specify a single science. + if( command->getScienceVec().empty() ) + { + DEBUG_CRASH( ("Commandbutton %s is a purchase science button without any science! Please add it.", command->getName().str() ) ); + } + else if( command->getScienceVec()[0] == science ) + { + commandButton->copyImagesFrom( command, TRUE ); + commandButton->copyButtonTextFrom( command, FALSE, TRUE ); + found = TRUE; + break; + } } - else if( command->getScienceVec()[0] == science ) + } + for( i = 0; !found && i < MAX_PURCHASE_SCIENCE_RANK_3; i++ ) + { + const CommandButton *command = commandSet3->getCommandButton( i ); + if( command && command->getCommandType() == GUI_COMMAND_PURCHASE_SCIENCE ) + { + //All purchase sciences specify a single science. + if( command->getScienceVec().empty() ) + { + DEBUG_CRASH( ("Commandbutton %s is a purchase science button without any science! Please add it.", command->getName().str() ) ); + } + else if( command->getScienceVec()[0] == science ) + { + commandButton->copyImagesFrom( command, TRUE ); + commandButton->copyButtonTextFrom( command, FALSE, TRUE ); + found = TRUE; + break; + } + } + } + for( i = 0; !found && i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) + { + const CommandButton *command = commandSet8->getCommandButton( i ); + if( command && command->getCommandType() == GUI_COMMAND_PURCHASE_SCIENCE ) { - commandButton->copyImagesFrom( command, true ); + //All purchase sciences specify a single science. + if( command->getScienceVec().empty() ) + { + DEBUG_CRASH( ("Commandbutton %s is a purchase science button without any science! Please add it.", command->getName().str() ) ); + } + else if( command->getScienceVec()[0] == science ) + { + commandButton->copyImagesFrom( command, TRUE ); + commandButton->copyButtonTextFrom( command, FALSE, TRUE ); + found = TRUE; + break; + } } } } @@ -754,13 +824,17 @@ void ControlBar::updateContextCommand() if( command == nullptr ) continue; - // ignore transport/structure inventory commands, they are handled elsewhere - if( command->getCommandType() == GUI_COMMAND_EXIT_CONTAINER ) - { - win->winSetStatus( WIN_STATUS_ALWAYS_COLOR ); //Don't let these buttons render in grayscale ever! - continue; - } - else + +// LORENZEN COMMENTED THIS OUT 8/11 + // Reason: ExitCameos can be greyed out when the container object gets subdued + +// // ignore transport/structure inventory commands, they are handled elsewhere +// if( command->getCommandType() == GUI_COMMAND_EXIT_CONTAINER ) +// { +// win->winSetStatus( WIN_STATUS_ALWAYS_COLOR ); //Don't let these buttons render in grayscale ever! +// continue; +// } +// else { win->winClearStatus( WIN_STATUS_NOT_READY ); win->winClearStatus( WIN_STATUS_ALWAYS_COLOR ); @@ -793,8 +867,12 @@ void ControlBar::updateContextCommand() //Determine by the production type of this button, whether or not the created object //will have a veterancy rank - const Image *image = calculateVeterancyOverlayForThing( command->getThingTemplate() ); - GadgetButtonDrawOverlayImage( win, image ); + if( command->getCommandType() != GUI_COMMAND_EXIT_CONTAINER ) + { + //Already handled for contained members -- see ControlBar::populateButtonProc() + const Image *image = calculateVeterancyOverlayForThing( command->getThingTemplate() ); + GadgetButtonDrawOverlayImage( win, image ); + } // // for check-like commands we will keep the push button "pushed" or "unpushed" depending @@ -853,15 +931,21 @@ const Image* ControlBar::calculateVeterancyOverlayForThing( const ThingTemplate if( !modName.compare( "VeterancyGainCreate" ) ) { data = (const VeterancyGainCreateModuleData*)mi.getNthData( modIdx ); - break; - } - } - //It does, so see if the player has that upgrade - if( data && player->hasScience( data->m_scienceRequired ) ) - { - //We do! So now check to see what the veterancy level would be. - level = data->m_startingLevel; + //It does, so see if the player has that upgrade + if( data ) + { + //If no science is specified, he gets it automatically (or check the science). + if( data->m_scienceRequired == SCIENCE_INVALID || player->hasScience( data->m_scienceRequired ) ) + { + //We do! So now check to see what the veterancy level would be. + if( data->m_startingLevel > level ) + { + level = data->m_startingLevel; + } + } + } + } } //Return the appropriate image (including nullptr if no veterancy levels) @@ -924,16 +1008,25 @@ static Int getRappellerCount(Object* obj) CommandAvailability ControlBar::getCommandAvailability( const CommandButton *command, Object *obj, GameWindow *win, + GameWindow *applyToWin, Bool forceDisabledEvaluation ) const { - if (command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER) + if( command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT + || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT ) { if (ThePlayerList && ThePlayerList->getLocalPlayer()) - obj = ThePlayerList->getLocalPlayer()->findNaturalCommandCenter(); + obj = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( command->getSpecialPowerTemplate()->getSpecialPowerType() ); else obj = nullptr; } + //If we modify the button (like a gadget clock overlay), then sometimes we may wish to apply it to a specific different button. + //But if we don't specify anything (default), then make them the same. + if( !applyToWin ) + { + applyToWin = win; + } + if (obj == nullptr) return COMMAND_HIDDEN; // probably better than crashing.... @@ -957,6 +1050,16 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com return COMMAND_RESTRICTED; } + if( BitIsSet( command->getOptions(), MUST_BE_STOPPED ) ) + { + //This button can only be activated when the unit isn't moving! + AIUpdateInterface *ai = obj->getAI(); + if( ai && ai->isMoving() ) + { + return COMMAND_RESTRICTED; + } + } + //Other disabled objects are unable to use buttons -- so gray them out. Bool disabled = obj->isDisabled(); @@ -978,9 +1081,10 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com commandType != GUI_COMMAND_EXIT_CONTAINER && commandType != GUI_COMMAND_BEACON_DELETE && commandType != GUI_COMMAND_SET_RALLY_POINT && + commandType != GUI_COMMAND_STOP && commandType != GUI_COMMAND_SWITCH_WEAPON ) { - if( getCommandAvailability( command, obj, win, TRUE ) == COMMAND_HIDDEN ) + if( getCommandAvailability( command, obj, win, applyToWin, TRUE ) == COMMAND_HIDDEN ) { return COMMAND_HIDDEN; } @@ -1022,11 +1126,12 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com { case GUI_COMMAND_DOZER_CONSTRUCT: { + const ThingTemplate * whatToBuild = command->getThingTemplate(); // if the command is a dozer construct task and the object dozer is building anything // this command is not available - if(command->getThingTemplate()) + if(whatToBuild) { - BuildableStatus bStatus = command->getThingTemplate()->getBuildable(); + BuildableStatus bStatus = whatToBuild->getBuildable(); if (bStatus == BSTATUS_NO || (bStatus == BSTATUS_ONLY_BY_AI && obj->getControllingPlayer()->getPlayerType() != PLAYER_COMPUTER)) return COMMAND_HIDDEN; } @@ -1051,10 +1156,10 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com return COMMAND_RESTRICTED; // return whether or not the player can build this thing - if( player->canBuild( command->getThingTemplate() ) == FALSE ) + if( player->canBuild( whatToBuild ) == FALSE ) return COMMAND_RESTRICTED; - if( !player->canAffordBuild( command->getThingTemplate() ) ) + if( !player->canAffordBuild( whatToBuild ) ) { return COMMAND_RESTRICTED;//COMMAND_CANT_AFFORD; } @@ -1068,6 +1173,11 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com // if so, remove the button, otherwise, its available if (obj->testScriptStatusBit(OBJECT_STATUS_SCRIPT_UNSELLABLE)) return COMMAND_HIDDEN; + + //since the container can be subdued, , M Lorenzen 8/11 + if ( obj->isDisabledByType( DISABLED_SUBDUED ) ) + return COMMAND_RESTRICTED; + break; } @@ -1120,6 +1230,14 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com if( TheUpgradeCenter->canAffordUpgrade( player, command->getUpgradeTemplate() ) == FALSE ) return COMMAND_RESTRICTED;//COMMAND_CANT_AFFORD; + for( size_t i = 0; i < command->getScienceVec().size(); i++ ) + { + ScienceType st = command->getScienceVec()[ i ]; + if( !player->hasScience( st ) ) + { + return COMMAND_RESTRICTED; + } + } break; } @@ -1147,6 +1265,15 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com if( TheUpgradeCenter->canAffordUpgrade( player, command->getUpgradeTemplate() ) == FALSE ) return COMMAND_RESTRICTED;//COMMAND_CANT_AFFORD; + + for( size_t i = 0; i < command->getScienceVec().size(); i++ ) + { + ScienceType st = command->getScienceVec()[ i ]; + if( !player->hasScience( st ) ) + { + return COMMAND_RESTRICTED; + } + } break; } @@ -1191,7 +1318,7 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com if ( w->getStatus() == RELOADING_CLIP) { Int percent = w->getPercentReadyToFire() * 100; - GadgetButtonDrawInverseClock(win, percent, m_buildUpClockColor); + GadgetButtonDrawInverseClock( applyToWin, percent, m_buildUpClockColor ); } return COMMAND_NOT_READY; } @@ -1237,6 +1364,11 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com // disable logic handled elsewhere, where if the contained count of the entire // container changes the UI is completely repopulated // + + //since the container can be subdued, the above is no longer true, M Lorenzen 8/11 + if ( obj->isDisabledByType( DISABLED_SUBDUED ) ) + return COMMAND_RESTRICTED; + break; } @@ -1246,6 +1378,10 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com // if we have no contained objects we can't evacuate anything if( !obj->getContain() || obj->getContain()->getContainCount() <= 0 ) return COMMAND_RESTRICTED; + + if ( obj->isDisabledByType( DISABLED_SUBDUED ) ) + return COMMAND_RESTRICTED; + break; } @@ -1259,8 +1395,10 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com break; } - case GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER: case GUI_COMMAND_SPECIAL_POWER: + case GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT: + case GUI_COMMAND_SPECIAL_POWER_CONSTRUCT: + case GUI_COMMAND_SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT: { // sanity DEBUG_ASSERTCRASH( command->getSpecialPowerTemplate() != nullptr, @@ -1272,14 +1410,14 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com { // sanity ... we must have a module for the special power, if we don't somebody probably // forgot to put it in the object - DEBUG_CRASH(( "Object does not contain special power module (%s) to execute. Did you forget to add it to the object INI?", - command->getSpecialPowerTemplate()->getName().str() )); + DEBUG_CRASH(( "Object %s does not contain special power module (%s) to execute. Did you forget to add it to the object INI?", + obj->getTemplate()->getName().str(), command->getSpecialPowerTemplate()->getName().str() )); } else if( mod->isReady() == FALSE ) { Int percent = mod->getPercentReady() * 100; - GadgetButtonDrawInverseClock(win, percent, m_buildUpClockColor); + GadgetButtonDrawInverseClock( applyToWin, percent, m_buildUpClockColor ); return COMMAND_NOT_READY; } else if( SpecialAbilityUpdate *spUpdate = obj->findSpecialAbilityUpdate( command->getSpecialPowerTemplate()->getSpecialPowerType() ) ) @@ -1377,6 +1515,12 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com } return COMMAND_AVAILABLE; } + + case GUI_COMMAND_SELECT_ALL_UNITS_OF_TYPE: + { + //We can *always* select a unit :) + return COMMAND_AVAILABLE; + } } // all is well with the command diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp index 7081e2ad9b7..e0a1b226257 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp @@ -57,6 +57,42 @@ #include "GameLogic/Module/ProductionUpdate.h" + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +struct SelectObjectsInfo +{ + const ThingTemplate *thingTemplate; + GameMessage *msg; +}; + +//------------------------------------------------------------------------------------------------- + +void selectObjectOfType( Object* obj, void* selectObjectsInfo ) +{ + SelectObjectsInfo *soInfo = (SelectObjectsInfo*)selectObjectsInfo; + if( !obj || !soInfo ) + { + return; + } + + //Do the templates match? + if( obj->getTemplate()->isEquivalentTo( soInfo->thingTemplate ) ) + { + //Okay, then add it to the selected group. + soInfo->msg->appendObjectIDArgument( obj->getID() ); + + Drawable *draw = obj->getDrawable(); + if( draw ) + { + TheInGameUI->selectDrawable( draw ); + } + } +} +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- + + //------------------------------------------------------------------------------------------------- /** Process a button transition message from the window system that should be for one of * our GUI commands */ @@ -97,12 +133,13 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, } // sanity, we won't process messages if we have no source object, - // unless we're CB_CONTEXT_PURCHASE_SCIENCE or GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER + // unless we're CB_CONTEXT_PURCHASE_SCIENCE or GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT if( m_currContext != CB_CONTEXT_MULTI_SELECT && commandButton->getCommandType() != GUI_COMMAND_PURCHASE_SCIENCE && - commandButton->getCommandType() != GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER && - (m_currentSelectedDrawable == nullptr || - m_currentSelectedDrawable->getObject() == nullptr) ) + commandButton->getCommandType() != GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT && + commandButton->getCommandType() != GUI_COMMAND_SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT && + commandButton->getCommandType() != GUI_COMMAND_SELECT_ALL_UNITS_OF_TYPE && + (m_currentSelectedDrawable == nullptr || m_currentSelectedDrawable->getObject() == nullptr) ) { if( m_currContext != CB_CONTEXT_NONE ) @@ -139,7 +176,9 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, Object *obj = nullptr; if( m_currContext != CB_CONTEXT_MULTI_SELECT && commandButton->getCommandType() != GUI_COMMAND_PURCHASE_SCIENCE && - commandButton->getCommandType() != GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER) + commandButton->getCommandType() != GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT && + commandButton->getCommandType() != GUI_COMMAND_SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT && + commandButton->getCommandType() != GUI_COMMAND_SELECT_ALL_UNITS_OF_TYPE ) obj = m_currentSelectedDrawable->getObject(); //@todo Kris -- Special case code so convoy trucks can detonate nuke trucks -- if other things need this, @@ -233,6 +272,97 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, } + + case GUI_COMMAND_SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT: + { + //Determine the object that would construct it. + const SpecialPowerTemplate *spTemplate = commandButton->getSpecialPowerTemplate(); + SpecialPowerType spType = spTemplate->getSpecialPowerType(); + Object* obj = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( spType ); + if( !obj ) + break; + Drawable *draw = obj->getDrawable(); + + const ThingTemplate *whatToBuild = commandButton->getThingTemplate(); + + CanMakeType cmt = TheBuildAssistant->canMakeUnit( obj, whatToBuild ); + if (cmt == CANMAKE_NO_MONEY) + { + TheEva->setShouldPlay(EVA_InsufficientFunds); + TheInGameUI->message( "GUI:NotEnoughMoneyToBuild" ); + break; + } + else if (cmt == CANMAKE_QUEUE_FULL) + { + TheInGameUI->message( "GUI:ProductionQueueFull" ); + break; + } + else if (cmt == CANMAKE_PARKING_PLACES_FULL) + { + TheInGameUI->message( "GUI:ParkingPlacesFull" ); + break; + } + else if( cmt == CANMAKE_MAXED_OUT_FOR_PLAYER ) + { + TheInGameUI->message( "GUI:UnitMaxedOut" ); + break; + } + + // tell the UI that we want to build something so we get a building at the cursor + TheInGameUI->placeBuildAvailable( commandButton->getThingTemplate(), draw ); + + ProductionUpdateInterface* pu = obj->getProductionUpdateInterface(); + if( pu ) + { + pu->setSpecialPowerConstructionCommandButton( commandButton ); + } + + break; + } + case GUI_COMMAND_SPECIAL_POWER_CONSTRUCT: + { + // sanity + if( m_currentSelectedDrawable == nullptr ) + break; + + const ThingTemplate *whatToBuild = commandButton->getThingTemplate(); + + CanMakeType cmt = TheBuildAssistant->canMakeUnit( obj, whatToBuild ); + if (cmt == CANMAKE_NO_MONEY) + { + TheEva->setShouldPlay(EVA_InsufficientFunds); + TheInGameUI->message( "GUI:NotEnoughMoneyToBuild" ); + break; + } + else if (cmt == CANMAKE_QUEUE_FULL) + { + TheInGameUI->message( "GUI:ProductionQueueFull" ); + break; + } + else if (cmt == CANMAKE_PARKING_PLACES_FULL) + { + TheInGameUI->message( "GUI:ParkingPlacesFull" ); + break; + } + else if( cmt == CANMAKE_MAXED_OUT_FOR_PLAYER ) + { + TheInGameUI->message( "GUI:UnitMaxedOut" ); + break; + } + + // tell the UI that we want to build something so we get a building at the cursor + TheInGameUI->placeBuildAvailable( commandButton->getThingTemplate(), m_currentSelectedDrawable ); + + ProductionUpdateInterface* pu = obj->getProductionUpdateInterface(); + if( pu ) + { + pu->setSpecialPowerConstructionCommandButton( commandButton ); + } + + break; + + } + //--------------------------------------------------------------------------------------------- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL: { @@ -503,6 +633,38 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, break; } + case GUI_COMMAND_SELECT_ALL_UNITS_OF_TYPE: + { + Player* localPlayer = ThePlayerList->getLocalPlayer(); + if( !localPlayer ) + { + break; + } + + const ThingTemplate *thing = commandButton->getThingTemplate(); + if( !thing ) + { + break; + } + + //deselect other units + TheInGameUI->deselectAllDrawables(); + + // create a new group. + GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP ); + + //New group or add to group? Passed in value is true if we are creating a new group. + teamMsg->appendBooleanArgument( TRUE ); + + //Iterate through the player's entire team and select each member that matches the template. + SelectObjectsInfo info; + info.thingTemplate = thing; + info.msg = teamMsg; + localPlayer->iterateObjects( selectObjectOfType, (void*)&info ); + + break; + } + //--------------------------------------------------------------------------------------------- case GUI_COMMAND_WAYPOINTS: break; @@ -548,6 +710,8 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, } + //what if container is subdued... assert a logic failure, perhaps? + // send message to exit GameMessage *exitMsg = TheMessageStream->appendMessage( GameMessage::MSG_EXIT ); exitMsg->appendObjectIDArgument( objWantingExit->getID() ); // 0 is the thing inside coming out @@ -669,17 +833,20 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, } //--------------------------------------------------------------------------------------------- - case GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER: + case GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT: { - Object* cmdCenter = ThePlayerList->getLocalPlayer()->findNaturalCommandCenter(); - if (cmdCenter == nullptr) + const SpecialPowerTemplate *spTemplate = commandButton->getSpecialPowerTemplate(); + SpecialPowerType spType = spTemplate->getSpecialPowerType(); + + Object* obj = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( spType ); + if( !obj ) break; // command needs no additional data, send the message GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_DO_SPECIAL_POWER ); - msg->appendIntegerArgument( commandButton->getSpecialPowerTemplate()->getID() ); + msg->appendIntegerArgument( spTemplate->getID() ); msg->appendIntegerArgument( commandButton->getOptions() ); - msg->appendObjectIDArgument( cmdCenter->getID() ); + msg->appendObjectIDArgument( obj->getID() ); break; } diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 42612d42aca..edda6839d12 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -1111,6 +1111,12 @@ Bool Object::hasAnyDamageWeapon() const return TRUE; } +//============================================================================= +UnsignedInt Object::getMostPercentReadyToFireAnyWeapon() const +{ + return m_weaponSet.getMostPercentReadyToFireAnyWeapon(); +} + //============================================================================= Bool Object::hasWeaponToDealDamageType(DamageType typeToDeal) const { @@ -2800,6 +2806,12 @@ Bool Object::hasSpecialPower( SpecialPowerType type ) const return TEST_SPECIALPOWERMASK( m_specialPowerBits, type ); } +//------------------------------------------------------------------------------------------------- +Bool Object::hasAnySpecialPower() const +{ + return SPECIALPOWERMASK_ANY_SET( m_specialPowerBits ); +} + //------------------------------------------------------------------------------------------------- void Object::onVeterancyLevelChanged( VeterancyLevel oldLevel, VeterancyLevel newLevel, Bool provideFeedback ) { diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp index d397d59a0ab..24db1aab346 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp @@ -63,6 +63,7 @@ SpecialPowerModuleData::SpecialPowerModuleData() m_specialPowerTemplate = nullptr; m_updateModuleStartsAttack = false; m_startsPaused = FALSE; + m_scriptedSpecialPowerOnly = FALSE; } @@ -74,10 +75,11 @@ SpecialPowerModuleData::SpecialPowerModuleData() static const FieldParse dataFieldParse[] = { - { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, nullptr, offsetof( SpecialPowerModuleData, m_specialPowerTemplate ) }, - { "UpdateModuleStartsAttack", INI::parseBool, nullptr, offsetof( SpecialPowerModuleData, m_updateModuleStartsAttack ) }, - { "StartsPaused", INI::parseBool, nullptr, offsetof( SpecialPowerModuleData, m_startsPaused ) }, - { "InitiateSound", INI::parseAudioEventRTS, nullptr, offsetof( SpecialPowerModuleData, m_initiateSound ) }, + { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, nullptr, offsetof( SpecialPowerModuleData, m_specialPowerTemplate ) }, + { "UpdateModuleStartsAttack", INI::parseBool, nullptr, offsetof( SpecialPowerModuleData, m_updateModuleStartsAttack ) }, + { "StartsPaused", INI::parseBool, nullptr, offsetof( SpecialPowerModuleData, m_startsPaused ) }, + { "InitiateSound", INI::parseAudioEventRTS, nullptr, offsetof( SpecialPowerModuleData, m_initiateSound ) }, + { "ScriptedSpecialPowerOnly", INI::parseBool, nullptr, offsetof( SpecialPowerModuleData, m_scriptedSpecialPowerOnly ) }, { nullptr, nullptr, nullptr, 0 } }; p.add(dataFieldParse); @@ -343,6 +345,18 @@ Real SpecialPowerModule::getPercentReady() const return percent; } +//------------------------------------------------------------------------------------------------- +// A special power module that is only supposed to be fired via scripts. An example of this +// are the various cargo plane units we have. Scripters can launch specials from them after +// specifying a waypoint path for them to follow them. +//------------------------------------------------------------------------------------------------- +Bool SpecialPowerModule::isScriptOnly() const +{ + const SpecialPowerModuleData *modData = getSpecialPowerModuleData(); + return modData->m_scriptedSpecialPowerOnly; +} + + //------------------------------------------------------------------------------------------------- /** A special power has been used ... start the recharge process by computing the frame * we will become fully available on in the future again */ diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp index ac7ef9765c5..ea5a60488e3 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp @@ -194,6 +194,7 @@ ProductionUpdate::ProductionUpdate( Thing *thing, const ModuleData* moduleData ) m_clearFlags.clear(); m_setFlags.clear(); m_flagsDirty = FALSE; + m_specialPowerConstructionCommandButton = nullptr; } diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp index d9932f1a375..8fa04cdb48d 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp @@ -1002,6 +1002,28 @@ Weapon* WeaponSet::findWaypointFollowingCapableWeapon() return nullptr; } +//------------------------------------------------------------------------------------------------- +UnsignedInt WeaponSet::getMostPercentReadyToFireAnyWeapon() const +{ + UnsignedInt mostReady = 0; + for( Int i = 0; i < WEAPONSLOT_COUNT; i++ ) + { + if( m_weapons[ i ] ) + { + UnsignedInt percentage = (UnsignedInt)(m_weapons[ i ]->getPercentReadyToFire() * 100.0f); + if( percentage > mostReady ) + { + mostReady = percentage; + } + if( mostReady >= 100 ) + { + return mostReady; + } + } + } + return mostReady; +} + //------------------------------------------------------------------------------------------------- // A special type of command demands that you use this (normally unchooseable) weapon // until told otherwise. diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/ControlBar.h b/GeneralsMD/Code/GameEngine/Include/GameClient/ControlBar.h index c98ff132065..62fb8f39f63 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/ControlBar.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/ControlBar.h @@ -206,6 +206,9 @@ enum GUICommandType CPP_11(: Int) GUICOMMANDMODE_PLACE_BEACON, GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT, ///< do a special power from localPlayer's command center, regardless of selection +#if RTS_GENERALS + GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER = GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT, ///< Legacy name +#endif GUI_COMMAND_SPECIAL_POWER_CONSTRUCT, ///< do a special power using the construct building interface GUI_COMMAND_SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT, ///< do a shortcut special power using the construct building interface @@ -256,7 +259,11 @@ static const char *const TheGuiCommandNames[] = "PICK_UP_PRISONER", #endif "PLACE_BEACON", +#if RTS_GENERALS + "SPECIAL_POWER_FROM_COMMAND_CENTER", ///< Legacy name +#else "SPECIAL_POWER_FROM_SHORTCUT", +#endif "SPECIAL_POWER_CONSTRUCT", "SPECIAL_POWER_CONSTRUCT_FROM_SHORTCUT", "SELECT_ALL_UNITS_OF_TYPE", diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index f07e61c8caf..1c9475a7d84 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -194,12 +194,16 @@ void ControlBar::populatePurchaseScience( Player* player ) commandSet8 = findCommandSet(player->getPlayerTemplate()->getPurchaseScienceCommandSetRank8()); // TEMP WILL CHANGE TO PROPER WAY ONCE WORKING for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_1; i++ ) - m_sciencePurchaseWindowsRank1[i]->winHide(TRUE); + if (m_sciencePurchaseWindowsRank1[i] != nullptr) + m_sciencePurchaseWindowsRank1[i]->winHide(TRUE); + for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_3; i++ ) - m_sciencePurchaseWindowsRank3[i]->winHide(TRUE); - for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) - m_sciencePurchaseWindowsRank8[i]->winHide(TRUE); + if (m_sciencePurchaseWindowsRank3[i] != nullptr) + m_sciencePurchaseWindowsRank3[i]->winHide(TRUE); + for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) + if (m_sciencePurchaseWindowsRank8[i] != nullptr) + m_sciencePurchaseWindowsRank8[i]->winHide(TRUE); // if no command set match is found hide all the buttons if( commandSet1 == nullptr || @@ -211,6 +215,8 @@ void ControlBar::populatePurchaseScience( Player* player ) const CommandButton *commandButton; for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_1; i++ ) { + if (m_sciencePurchaseWindowsRank1[i] == nullptr) + continue; // get command button commandButton = commandSet1->getCommandButton(i); @@ -271,6 +277,8 @@ void ControlBar::populatePurchaseScience( Player* player ) for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_3; i++ ) { + if (m_sciencePurchaseWindowsRank3[i] == nullptr) + continue; // get command button commandButton = commandSet3->getCommandButton(i); @@ -334,6 +342,8 @@ void ControlBar::populatePurchaseScience( Player* player ) for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) { + if (m_sciencePurchaseWindowsRank8[i] == nullptr) + continue; // get command button commandButton = commandSet8->getCommandButton(i); @@ -400,15 +410,16 @@ void ControlBar::populatePurchaseScience( Player* player ) GadgetStaticTextSetText(win, tempUS); } -// redundant to StaticTextTitle in the Zero Hour context -/* +#if RTS_GENERALS win = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], TheNameKeyGenerator->nameToKey( "GeneralsExpPoints.wnd:StaticTextLevel" ) ); if(win) { tempUS.format(TheGameText->fetch("SCIENCE:Rank"), player->getRankLevel()); GadgetStaticTextSetText(win, tempUS); } -*/ +#else + // redundant to StaticTextTitle in the Zero Hour context +#endif win = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], TheNameKeyGenerator->nameToKey( "GeneralsExpPoints.wnd:ProgressBarExperience" ) ); if(win) @@ -1145,7 +1156,8 @@ void ControlBar::init() id = TheNameKeyGenerator->nameToKey( windowName.str() ); m_sciencePurchaseWindowsRank1[ i ] = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], id ); - m_sciencePurchaseWindowsRank1[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); + if (m_sciencePurchaseWindowsRank1[ i ] != nullptr) + m_sciencePurchaseWindowsRank1[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); } for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_3; i++ ) { @@ -1153,7 +1165,8 @@ void ControlBar::init() id = TheNameKeyGenerator->nameToKey( windowName.str() ); m_sciencePurchaseWindowsRank3[ i ] = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], id ); - m_sciencePurchaseWindowsRank3[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); + if (m_sciencePurchaseWindowsRank3[ i ] != nullptr) + m_sciencePurchaseWindowsRank3[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); } for( i = 0; i < MAX_PURCHASE_SCIENCE_RANK_8; i++ ) @@ -1162,7 +1175,8 @@ void ControlBar::init() id = TheNameKeyGenerator->nameToKey( windowName.str() ); m_sciencePurchaseWindowsRank8[ i ] = TheWindowManager->winGetWindowFromId( m_contextParent[ CP_PURCHASE_SCIENCE ], id ); - m_sciencePurchaseWindowsRank8[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); + if (m_sciencePurchaseWindowsRank8[ i ] != nullptr) + m_sciencePurchaseWindowsRank8[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); } // keep a pointer to the window making up the right HUD display @@ -2623,7 +2637,7 @@ void ControlBar::setPortraitByObject( Object *obj ) setPortraitByObject( nullptr ); return; } - StealthUpdate *stealth = obj->getStealth(); + StealthUpdate *stealth = obj->getStealth(); if( stealth && stealth->isDisguised() ) { //Fake player upgrades too! @@ -3267,14 +3281,18 @@ void ControlBar::initSpecialPowershortcutBar( Player *player) id = TheNameKeyGenerator->nameToKey( windowName.str() ); m_specialPowerShortcutButtons[ i ] = TheWindowManager->winGetWindowFromId( m_specialPowerShortcutParent, id ); - m_specialPowerShortcutButtons[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); - // Oh god... this is a total hack for shortcut buttons to handle rendering text top left corner... - m_specialPowerShortcutButtons[ i ]->winSetStatus( WIN_STATUS_SHORTCUT_BUTTON ); - windowName.format( parentName, i+1 ); - id = TheNameKeyGenerator->nameToKey( windowName.str() ); - m_specialPowerShortcutButtonParents[ i ] = - TheWindowManager->winGetWindowFromId( m_specialPowerShortcutParent, id ); + if (m_specialPowerShortcutButtons[ i ] != nullptr) + { + m_specialPowerShortcutButtons[ i ]->winSetStatus( WIN_STATUS_USE_OVERLAY_STATES ); + // Oh god... this is a total hack for shortcut buttons to handle rendering text top left corner... + m_specialPowerShortcutButtons[ i ]->winSetStatus( WIN_STATUS_SHORTCUT_BUTTON ); + + windowName.format( parentName, i+1 ); + id = TheNameKeyGenerator->nameToKey( windowName.str() ); + m_specialPowerShortcutButtonParents[ i ] = + TheWindowManager->winGetWindowFromId( m_specialPowerShortcutParent, id ); + } } } @@ -3657,7 +3675,6 @@ void ControlBar::updateSpecialPowerShortcut() win->winEnable( TRUE ); break; } - } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp index f7617211343..5a9099af8a9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp @@ -1164,7 +1164,6 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com return COMMAND_RESTRICTED;//COMMAND_CANT_AFFORD; } - break; } @@ -1383,7 +1382,6 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com if ( obj->isDisabledByType( DISABLED_SUBDUED ) ) return COMMAND_RESTRICTED; - break; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp index 29058e1b099..b57ae030e2d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp @@ -363,8 +363,6 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, } - - //--------------------------------------------------------------------------------------------- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL: { @@ -388,7 +386,6 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, //--------------------------------------------------------------------------------------------- case GUI_COMMAND_UNIT_BUILD: { - // const ThingTemplate *whatToBuild = commandButton->getThingTemplate(); // get the "factory" object that is going to make the thing