diff --git a/main/delta.lst b/main/delta.lst index 11ebb95c..c8680345 100644 --- a/main/delta.lst +++ b/main/delta.lst @@ -117,7 +117,7 @@ entity_state_t gamedll Entity_Encode DEFINE_DELTA( playerclass, DT_INTEGER, 1, 1.0 ), - //DEFINE_DELTA( iuser2, DT_INTEGER, 32, 1.0 ), // NS custom code (used for entity ids for research) + DEFINE_DELTA( iuser2, DT_INTEGER, 8, 1.0 ), // NS custom code (used for entity ids for research) DEFINE_DELTA( iuser3, DT_INTEGER, 6, 1.0 ), // NS custom code DEFINE_DELTA( iuser4, DT_INTEGER, 32, 1.0 ), // NS custom parameter (needs 32 for AvHSpecialSound) DEFINE_DELTA( fuser1, DT_FLOAT, 32, 1.0 ) // Use 32-bits because two values fit in here sometimes diff --git a/main/source/mod/AvHActionButtons.cpp b/main/source/mod/AvHActionButtons.cpp index fa6855d0..c8bf8df5 100644 --- a/main/source/mod/AvHActionButtons.cpp +++ b/main/source/mod/AvHActionButtons.cpp @@ -151,8 +151,9 @@ void ActionButton::Localize(const AvHTechTree& inTechNodes) // Localize help string this->mHelpText = ""; + string theCostAndTimeText; - if(gHUD.GetHelpForMessage(this->mMessageID, this->mHelpText)) + if(gHUD.GetHelpForMessage(this->mMessageID, this->mHelpText, theCostAndTimeText)) { // Add hotkey accelerator if(this->mButtonIndex >= 0) @@ -167,6 +168,8 @@ void ActionButton::Localize(const AvHTechTree& inTechNodes) string theHotkeyText = string("(") + theHotkeyChar + string(")"); this->mHelpText += string(" "); this->mHelpText += theHotkeyText; + if (theCostAndTimeText != "") + this->mHelpText += string("\n") + theCostAndTimeText; } } diff --git a/main/source/mod/AvHBaseBuildable.cpp b/main/source/mod/AvHBaseBuildable.cpp index 6038ec20..8eb95ab8 100644 --- a/main/source/mod/AvHBaseBuildable.cpp +++ b/main/source/mod/AvHBaseBuildable.cpp @@ -89,6 +89,7 @@ #include "AvHPlayerUpgrade.h" #include "../dlls/animation.h" #include "AvHMovementUtil.h" +#include "AvHNetworkMessages.h" const int kBaseBuildableSpawnAnimation = 0; const int kBaseBuildableDeployAnimation = 1; @@ -702,6 +703,25 @@ AvHTeamNumber AvHBaseBuildable::GetTeamNumber() const void AvHBaseBuildable::Killed(entvars_t* pevAttacker, int iGib) { bool theInReset = GetGameRules()->GetIsGameInReset(); + + // Send a Cancel notification so any research can be removed from the research tracker. + if (GetGameRules()->GetGameStarted()) + { + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + bool theShowNotification = false; + + // Show to friendlies... + if (theEntity->pev->team == this->pev->team) + { + theShowNotification = true; + } + + if (theShowNotification) + { + NetMsg_PlayHUDNotification(theEntity->pev, 1, MESSAGE_CANCEL, this->pev->origin.x, this->pev->origin.y); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + } AvHBaseBuildable::SetHasBeenKilled(); GetGameRules()->RemoveEntityUnderAttack( this->entindex() ); diff --git a/main/source/mod/AvHCommanderModeHandler.cpp b/main/source/mod/AvHCommanderModeHandler.cpp index 038ef44d..756fae2f 100644 --- a/main/source/mod/AvHCommanderModeHandler.cpp +++ b/main/source/mod/AvHCommanderModeHandler.cpp @@ -257,12 +257,52 @@ void AvHCommanderModeHandler::RecalculateBuildResearchText() LocalizeString(kResearchingPrefix, theHelpPrefix); } + //iuser2 gets set when research starts so it doesn't work for building. string theHelpText; AvHMessageID theResearchID = (AvHMessageID)(theEntity->curstate.iuser2); if(theResearchID != MESSAGE_NULL) { if(ActionButton::GetLabelForMessage(theResearchID, theHelpText)) { + //Research timer and refund UI. Assumes no cheats or combat mode. Building timer would need to check the number of people building or its build rate. + if (theIsResearching) + { + bool theResearchable; + float timeToBuild; + int researchCost; + gHUD.GetResearchInfo(theResearchID, theResearchable, researchCost, timeToBuild); + //Display time to completion. + int timeLeft = (1 - thePercentage) * timeToBuild; + + int theMinutesLeft = timeLeft / 60; + int theSecondsLeft = timeLeft % 60; + + if (theMinutesLeft) + theHelpText += " (" + MakeStringFromInt(theMinutesLeft) + "m " + MakeStringFromInt(theSecondsLeft) + "s)"; + else + theHelpText += " (" + MakeStringFromInt(theSecondsLeft) + "s)"; + + //Display amount refunded if canceled. + if (researchCost) + { + int theRefund = round((1 - thePercentage) * researchCost); + + //Some lazy centering that's good enough. Should be making a new UI label for this or matching width. + string theRefundText = "Cancellation refund: " + MakeStringFromInt(theRefund); + int helpLength = theHelpPrefix.length() + theHelpText.length(); + int refundLength = theRefundText.length(); + + //int padNumber = ((helpLength - refundLength) / 2); + int padNumber = (helpLength - refundLength) - 1; + + if (padNumber > 0) + { + theHelpText += "\n\n" + string(padNumber, ' ') + theRefundText; + } + else + theHelpText += "\n\n" + theRefundText; + } + } string theFinalMessage = theHelpPrefix + theHelpText; this->mBuildResearchText = theFinalMessage; } diff --git a/main/source/mod/AvHHud.cpp b/main/source/mod/AvHHud.cpp index 1ed03c7c..c70b02a7 100644 --- a/main/source/mod/AvHHud.cpp +++ b/main/source/mod/AvHHud.cpp @@ -4813,7 +4813,7 @@ void AvHHud::InitExploitPrevention() { ForceCvar("gl_d3dflip", gl_d3dflip, 1.0f); ForceCvar("s_show", s_show, 0.0f); ForceCvar("r_detailtextures", r_detailtextures, 0.0f); - ForceCvar("gl_max_size", gl_max_size, 256.0f); + ForceCvar("gl_max_size", gl_max_size, 512.0f); RemoveAlias("lightgamma"); if(lightgamma && lightgamma->value < 2.0) { @@ -4845,7 +4845,7 @@ void AvHHud::UpdateExploitPrevention() ForceCvar("gl_d3dflip", gl_d3dflip, 1.0f); ForceCvar("s_show", s_show, 0.0f); ForceCvar("r_detailtextures", r_detailtextures, 0.0f); - ForceCvar("gl_max_size", gl_max_size, 256.0f); + ForceCvar("gl_max_size", gl_max_size, 512.0f); if(lightgamma && lightgamma->value < 2.0) { ForceCvar("lightgamma", lightgamma, 2.0f); @@ -5231,7 +5231,7 @@ bool AvHHud::GetDoesPlayerHaveOrder() const } -bool AvHHud::GetHelpForMessage(int inMessageID, string& outHelpText) const +bool AvHHud::GetHelpForMessage(int inMessageID, string& outHelpText, string& outCostAndTimeText) const { bool theSuccess = false; @@ -5262,20 +5262,32 @@ bool AvHHud::GetHelpForMessage(int inMessageID, string& outHelpText) const if(AvHSHUGetDoesTechCostEnergy((AvHMessageID)inMessageID)) { LocalizeString(kEnergyPrefix, theCostString); + outCostAndTimeText = theCostString + " " + MakeStringFromInt(theCost); } else { LocalizeString(kMessageButtonCost, theCostString); + //Research or build time. TODO: localize. + string theResearchOrBuildString; + theResearchOrBuildString = AvHSHUGetIsBuilding((AvHMessageID)inMessageID) ? string("Build") : string("Research"); + + outCostAndTimeText = theCostString + " " + MakeStringFromInt(theCost); + if (theTime > 0) + { + int timeToFinish = theTime; + int theMinutes = timeToFinish / 60; + int theSeconds = timeToFinish % 60; + + if (theMinutes) + outCostAndTimeText += "\n" + theResearchOrBuildString + " time " + MakeStringFromInt(theMinutes) + "m " + MakeStringFromInt(theSeconds) + "s"; + else + outCostAndTimeText += "\n" + theResearchOrBuildString + " time " + MakeStringFromInt(theSeconds) + "s"; + } } - outHelpText += " "; - outHelpText += theCostString; - outHelpText += " "; - outHelpText += MakeStringFromInt(theCost); - // Draw description below - //outHelpText += "\n"; - //outHelpText += theTechNodeHelp; + //outCostAndTimeText += "\n"; + //outCostAndTimeText += theTechNodeHelp; } } } @@ -5634,6 +5646,11 @@ const AvHTechTree& AvHHud::GetTechNodes() const return this->mTechNodes; } +bool AvHHud::GetResearchInfo(AvHMessageID inMessageID, bool& outIsResearchable, int& outCost, float& outTime) const +{ + return this->mTechNodes.GetResearchInfo(inMessageID, outIsResearchable, outCost, outTime); +} + void AvHHud::GetTooltipDrawingInfo(float& outNormX, float& outNormY) const { outNormX = kHelpMessageLeftEdgeInset; @@ -5832,12 +5849,41 @@ void AvHHud::UpdateTooltips(float inCurrentTime) void AvHHud::UpdateStructureNotification(float inCurrentTime) { - const float kTimeToDisplayIcon = 6.0f; const int kMaxIcons = 5; + Vector cancelLocation; + AvHTeamNumber theCurrentTeam = this->GetHUDTeam(); + + // Reset on a team change + if (this->mLastTeamNumber != theCurrentTeam) + this->mStructureNotificationList.clear(); for(StructureHUDNotificationListType::iterator theIter = this->mStructureNotificationList.begin(); theIter != this->mStructureNotificationList.end(); /* no inc */) { - if((inCurrentTime > (theIter->mTime + kTimeToDisplayIcon)) || (this->mStructureNotificationList.size() > kMaxIcons)) + int theCost; + bool theResearchable; + float theBuildOrResearchTime; + AvHMessageID theTech = theIter->mStructureID; + this->mTechNodes.GetResearchInfo(theTech, theResearchable, theCost, theBuildOrResearchTime); + bool isResearch = AvHSHUGetIsResearchTech(theTech); + float timeToDisplayIcon = 6.0f; + + if (isResearch) + { + theIter->mResearchTimer = max(0, theBuildOrResearchTime - (inCurrentTime - theIter->mTime)); + timeToDisplayIcon = theBuildOrResearchTime; + } + else + { + theIter->mResearchTimer = 0; + } + + if (theTech == MESSAGE_CANCEL) + { + cancelLocation = theIter->mLocation; + this->mStructureNotificationList.erase(theIter); + theIter = this->mStructureNotificationList.begin(); + } + else if ((inCurrentTime > (theIter->mTime + timeToDisplayIcon)) || (!(theIter->mResearchTimer > 0) && this->mStructureNotificationList.size() > kMaxIcons ) || (theIter->mLocation == cancelLocation)) { theIter = this->mStructureNotificationList.erase(theIter); } @@ -6316,29 +6362,38 @@ void AvHHud::UpdateBuildResearchText() if(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) { Label* theHelpTextLabel = NULL; - if(this->GetManager().GetVGUIComponentNamed(kTechHelpText, theHelpTextLabel)) + //Label* theRefundTextLabel = NULL; + if(this->GetManager().GetVGUIComponentNamed(kTechHelpText, theHelpTextLabel)/* && this->GetManager().GetVGUIComponentNamed(kTechHelpText, theRefundTextLabel)*/) { gCommanderHandler.RecalculateBuildResearchText(); // Display build/research text string theBuildResearchText = gCommanderHandler.GetBuildResearchText(); theHelpTextLabel->setText(theBuildResearchText.c_str()); + //string theBuildRefundText = gCommanderHandler.GetBuildRefundText(); + //theRefundTextLabel->setText(theBuildRefundText.c_str()); // Center it - int theWidth, theHeight; + int theWidth, theHeight/*, theWidth2, theHeight2*/; theHelpTextLabel->getTextSize(theWidth, theHeight); + //theRefundTextLabel->getTextSize(theWidth2, theHeight2); - int theX, theY; + int theX, theY/*, theX2, theY2*/; theHelpTextLabel->getPos(theX, theY); + //theRefundTextLabel->getPos(theX2, theY2); int theScreenWidth = ScreenWidth(); theHelpTextLabel->setPos(theScreenWidth/2 - theWidth/2, theY); + //theRefundTextLabel->setPos(theScreenWidth / 2 - theWidth2 / 2, theY2); + //gEngfuncs.Con_Printf("y2:%d\n", theY2); // Vanish if no text (but keep build/research text visible) theHelpTextLabel->setVisible(true); + //theRefundTextLabel->setVisible(true); if(theBuildResearchText.length() == 0)// || ((this->mTimeLastHelpTextChanged != -1) && (this->mTimeLastHelpTextChanged + kHelpTextInterval < this->mTimeOfLastUpdate))) { theHelpTextLabel->setVisible(false); + //theRefundTextLabel->setVisible(false); } // Display action button tool tip diff --git a/main/source/mod/AvHHud.h b/main/source/mod/AvHHud.h index 8bd248d7..67edd580 100644 --- a/main/source/mod/AvHHud.h +++ b/main/source/mod/AvHHud.h @@ -241,7 +241,7 @@ public: AvHPlayMode GetPlayMode(void) const; bool GetAlienHelpForMessage(int inMessageID, string& outHelpText, int& outPointCost) const; bool GetDoesPlayerHaveOrder() const; - bool GetHelpForMessage(int inMessageID, string& outHelpText) const; + bool GetHelpForMessage(int inMessageID, string& outHelpText, string& outCostAndTimeText) const; bool GetInTopDownMode() const; bool GetIsSelecting() const; OrderListType GetOrderList() const; @@ -334,6 +334,7 @@ public: int GetLocalUpgrades() const; string GetNameOfLocation(vec3_t inLocation) const; const AvHTechTree& GetTechNodes() const; + bool GetResearchInfo(AvHMessageID inMessageID, bool& outIsResearchable, int& outCost, float& outTime) const; UIMode GetUIMode() const; bool SwitchUIMode(UIMode inNewMode); @@ -843,6 +844,7 @@ private: { AvHMessageID mStructureID; float mTime; + float mResearchTimer; int mPlayerIndex; Vector mLocation; } HUDNotificationType; diff --git a/main/source/mod/AvHHudRender.cpp b/main/source/mod/AvHHudRender.cpp index c0578e57..2ee56ce4 100644 --- a/main/source/mod/AvHHudRender.cpp +++ b/main/source/mod/AvHHudRender.cpp @@ -1458,13 +1458,14 @@ void AvHHud::DrawHelpIcons() // inDrawMode determines if we're drawing text or sprites void AvHHud::DrawHUDStructureNotification() { - const float kHUDStructureNotificationStartX = .02f; - const float kHUDStructureNotificationStartY = .11f; + const float kHUDStructureNotificationStartX = .01f; + const float kHUDStructureNotificationStartY = .08f; const float kHUDStructureNotificationIconWidth = .03f; const float kHUDStructureNotificationIconHeight = kHUDStructureNotificationIconWidth*1.333f; const float kHUDStructureNotificationIconHorizontalSpacing = .01f; const float kHUDStructureNotificationIconVerticalSpacing = kHUDStructureNotificationIconHorizontalSpacing*1.333f; - const float kHUDStructureNotificationMaxTextWidth = .2f; + //const float kHUDStructureNotificationMaxTextWidth = .2f; + const float kTextHeightCenteringFactor = 0.25f; // Draw them all in order if(this->GetIsAlive() && CVAR_GET_FLOAT(kvBuildMessages)) @@ -1473,23 +1474,65 @@ void AvHHud::DrawHUDStructureNotification() float theCurrentX = kHUDStructureNotificationStartX; float theCurrentY = kHUDStructureNotificationStartY; + //bool inTopDown = GetInTopDownMode(); + AvHTeamNumber theCurrentTeam = this->GetHUDTeam(); + float kSmallScaleFactor; + //Don't make building icons smaller if alien. + (theCurrentTeam == TEAM_TWO) ? kSmallScaleFactor = 1.0f : kSmallScaleFactor = 0.75f; + const float kIconWidthSmall = kHUDStructureNotificationIconWidth * kSmallScaleFactor; + const float kIconHeightSmall = kHUDStructureNotificationIconHeight * kSmallScaleFactor; for(StructureHUDNotificationListType::iterator theIter = this->mStructureNotificationList.begin(); theIter != this->mStructureNotificationList.end(); theIter++) { // Draw icon AvHMessageID theIconTech = theIter->mStructureID; int theFrame = 0; - this->DrawTechTreeSprite(theIconTech, theCurrentX*ScreenWidth(), theCurrentY*ScreenHeight(), kHUDStructureNotificationIconWidth*ScreenWidth(), kHUDStructureNotificationIconHeight*ScreenHeight(), theFrame); string theLocationName = this->GetNameOfLocation(theIter->mLocation); - if(theLocationName != "") + int theStartX = (theCurrentX + kHUDStructureNotificationIconWidth + kHUDStructureNotificationIconHorizontalSpacing)*ScreenWidth(); + bool isResearch = AvHSHUGetIsResearchTech(theIconTech); + + //Don't draw cancel notifications. Still getting sent them in UpdateStructureNotification so they can be read to remove research. + if (theIconTech != MESSAGE_CANCEL) { - int theStartX = (theCurrentX + kHUDStructureNotificationIconWidth + kHUDStructureNotificationIconHorizontalSpacing)*ScreenWidth(); - this->DrawTranslatedString(theStartX, theCurrentY*ScreenHeight(), theLocationName.c_str(), false, true); + if (isResearch) + { + string theResearchTimerText; + ActionButton::GetLabelForMessage(theIter->mStructureID, theResearchTimerText); + + int timeLeft = theIter->mResearchTimer; + int theMinutesLeft = timeLeft / 60; + int theSecondsLeft = timeLeft % 60; + + if (theMinutesLeft) + theResearchTimerText += " - " + MakeStringFromInt(theMinutesLeft) + "m " + MakeStringFromInt(theSecondsLeft) + "s"; + else + theResearchTimerText += " - " + MakeStringFromInt(theSecondsLeft) + "s"; + + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, true, false); + char theCharBuffer[512]; + sprintf(theCharBuffer, "%s", theResearchTimerText.c_str()); + + float theCurrentTextY = theCurrentY + (kHUDStructureNotificationIconHeight * kTextHeightCenteringFactor); + + this->DrawTechTreeSprite(theIconTech, theCurrentX*ScreenWidth(), theCurrentY*ScreenHeight(), kHUDStructureNotificationIconWidth*ScreenWidth(), kHUDStructureNotificationIconHeight*ScreenHeight(), theFrame); + this->DrawHudString(theStartX, theCurrentTextY*ScreenHeight(), ScreenWidth(), theCharBuffer, theR, theG, theB); + } + else + { + this->DrawTechTreeSprite(theIconTech, theCurrentX*ScreenWidth(), theCurrentY*ScreenHeight(), kIconWidthSmall*ScreenWidth(), kIconHeightSmall*ScreenHeight(), theFrame); + + int theStartXsmall = theStartX * kSmallScaleFactor; + float theCurrentTextY = theCurrentY + (kIconHeightSmall * kTextHeightCenteringFactor); + + if (theLocationName != "") + this->DrawTranslatedString(theStartXsmall, theCurrentTextY*ScreenHeight(), theLocationName.c_str(), false, true); + } + + // Increment coords + theCurrentY += (kHUDStructureNotificationIconHeight + kHUDStructureNotificationIconVerticalSpacing); } - - // Increment coords - theCurrentY += (kHUDStructureNotificationIconHeight + kHUDStructureNotificationIconVerticalSpacing); } } @@ -2493,6 +2536,9 @@ void AvHHud::DrawBuildHealthEffectsForEntity(int inEntityIndex, float inAlpha) // It's a friendly entity and we're a builder OR (theIsOnOurTeam && (this->GetHUDUser3() == AVH_USER3_ALIEN_PLAYER2)) || + // It's a friendly player with <95% armor and we have a welder in our inventory OR + (theIsOnOurTeam && theEntityIsPlayer && theHealthPercentage < 0.95f && this->mHasWelder) || + // welder/healing spray is selected (this->mCurrentWeaponID == 18 || this->mCurrentWeaponID == 27) diff --git a/main/source/mod/AvHPlayer.cpp b/main/source/mod/AvHPlayer.cpp index 76621c5c..88f8494e 100644 --- a/main/source/mod/AvHPlayer.cpp +++ b/main/source/mod/AvHPlayer.cpp @@ -4958,11 +4958,11 @@ void AvHPlayer::PlayHUDStructureNotification(AvHMessageID inMessageID, const Vec // This player built a structure. Tell all his teammates FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetIsRelevant() && !theEntity->GetIsBeingDigested()) - { + //if(theEntity->GetIsRelevant() && !theEntity->GetIsBeingDigested()) + //{ // Don't send our own messages to ourself unless cheats are enabled - if(GetGameRules()->GetCheatsEnabled() || (this != theEntity)) - { + //if(GetGameRules()->GetCheatsEnabled() || (this != theEntity)) + //{ bool theShowNotification = false; // Show to friendlies... @@ -4975,8 +4975,8 @@ void AvHPlayer::PlayHUDStructureNotification(AvHMessageID inMessageID, const Vec { NetMsg_PlayHUDNotification( theEntity->pev, 1, inMessageID, inLocation.x, inLocation.y ); } - } - } + // } + //} END_FOR_ALL_ENTITIES(kAvHPlayerClassName); } } @@ -5354,6 +5354,8 @@ void AvHPlayer::Research(AvHMessageID inUpgrade, int inEntityIndex) theRefund = min(theRefund, (float)theResearchCost); this->SetResources(this->GetResources() + theRefund); } + //Tell team about cancel so it can update research notifications + this->PlayHUDStructureNotification(inUpgrade, theEntity->pev->origin); char* theResearchName = NULL; if(AvHSHUGetResearchTechName(inUpgrade, theResearchName))