#include "cl_dll/hud.h" #include "cl_dll/cl_util.h" #include "../common/const.h" #include "../common/entity_state.h" #include "../common/cl_entity.h" #include "ui/UITags.h" #include "AvHOverviewMap.h" #include "AvHSharedUtil.h" #include "AvHSpecials.h" #include "AvHMiniMap.h" #include "ui/UIUtil.h" #include "AvHPlayerUpgrade.h" #include "AvHSpriteAPI.h" #include "AvHSprites.h" #include "AvHClientVariables.h" using std::string; class DrawingOrderSort { public: bool operator()(const DrawableEntity& entity1, const DrawableEntity& entity2) { // Draw resource nodes all the way on the bottom. if (entity1.mUser3 == AVH_USER3_FUNC_RESOURCE) { if (entity2.mUser3 == AVH_USER3_FUNC_RESOURCE) { return entity1.mEntityNumber > entity2.mEntityNumber; } else { return true; } } else if (entity2.mUser3 == AVH_USER3_FUNC_RESOURCE) { return false; } // Draw the local player on top of everything. if (entity1.mIsLocalPlayer) { return false; } else if (entity2.mIsLocalPlayer) { return true; } // Draw players on top of structures. return (entity1.mEntityNumber > entity2.mEntityNumber); } }; AvHOverviewMap::AvHOverviewMap() { this->Init(); } extern globalvars_t *gpGlobals; void AvHOverviewMap::Init() { this->mUser3 = AVH_USER3_NONE; this->mTeam = TEAM_IND; this->mDrawableEntityList.clear(); // Approximately 1/max world dimension this->mMapExtents.ResetMapExtents(); this->mMiniMapSprite = 0; this->mReticleSprite = 0; // : 1066 reset overview map this->mLastMinimap = -1; this->mMapName=""; this->mBlinkTime=0.0f; this->mBlinkOn=false; mMapNamesFontLoaded = false; mLastUpdateTime = 0; } void AvHOverviewMap::Clear() { this->Init(); } bool getIsStructure(int user3) { return user3 == AVH_USER3_HIVE || user3 == AVH_USER3_COMMANDER_STATION || user3 == AVH_USER3_TURRET_FACTORY || user3 == AVH_USER3_ARMORY || user3 == AVH_USER3_ADVANCED_ARMORY || user3 == AVH_USER3_ARMSLAB || user3 == AVH_USER3_PROTOTYPE_LAB || user3 == AVH_USER3_OBSERVATORY || user3 == AVH_USER3_TURRET || user3 == AVH_USER3_SIEGETURRET || user3 == AVH_USER3_RESTOWER || user3 == AVH_USER3_INFANTRYPORTAL || user3 == AVH_USER3_PHASEGATE || user3 == AVH_USER3_DEFENSE_CHAMBER || user3 == AVH_USER3_MOVEMENT_CHAMBER || user3 == AVH_USER3_OFFENSE_CHAMBER || user3 == AVH_USER3_SENSORY_CHAMBER || user3 == AVH_USER3_ALIENRESTOWER || user3 == AVH_USER3_ADVANCED_TURRET_FACTORY; } bool getIsOnCommMinimap(int user3) { return user3 == AVH_USER3_WAYPOINT || user3 == AVH_USER3_MARINE_PLAYER || user3 == AVH_USER3_HEAVY || user3 == AVH_USER3_COMMANDER_STATION || user3 == AVH_USER3_TURRET_FACTORY || user3 == AVH_USER3_ADVANCED_TURRET_FACTORY || user3 == AVH_USER3_ARMORY || user3 == AVH_USER3_ADVANCED_ARMORY || user3 == AVH_USER3_ARMSLAB || user3 == AVH_USER3_PROTOTYPE_LAB || user3 == AVH_USER3_OBSERVATORY || user3 == AVH_USER3_TURRET || user3 == AVH_USER3_SIEGETURRET || user3 == AVH_USER3_RESTOWER || user3 == AVH_USER3_INFANTRYPORTAL || user3 == AVH_USER3_PHASEGATE || user3 == AVH_USER3_DEFENSE_CHAMBER || user3 == AVH_USER3_MOVEMENT_CHAMBER || user3 == AVH_USER3_OFFENSE_CHAMBER || user3 == AVH_USER3_SENSORY_CHAMBER || user3 == AVH_USER3_ALIENRESTOWER || user3 == AVH_USER3_HIVE || user3 == AVH_USER3_ALIEN_PLAYER1 || user3 == AVH_USER3_ALIEN_PLAYER2 || user3 == AVH_USER3_ALIEN_PLAYER3 || user3 == AVH_USER3_ALIEN_PLAYER4 || user3 == AVH_USER3_ALIEN_PLAYER5 || user3 == AVH_USER3_ALIEN_EMBRYO || user3 == AVH_USER3_FUNC_RESOURCE || user3 == AVH_USER3_WELD; } void AvHOverviewMap::GetSpriteForEntity(const DrawableEntity& entity, AVHHSPRITE& outSprite, int& outFrame, int& outRenderMode, bool commanderOverview) { outRenderMode = kRenderTransTexture; if (entity.mUser3 == AVH_USER3_UNKNOWN) { outSprite = SPR_Load(kCommBlipSprite); outFrame=1; } else if (commanderOverview && this->mUser3 == AVH_USER3_COMMANDER_PLAYER ) { if ( getIsOnCommMinimap(entity.mUser3 ) ) { bool isStructure=getIsStructure(entity.mUser3); bool isFriendly=entity.mTeam == mTeam; outSprite = SPR_Load(kCommBlipSprite); outFrame=1; if ( entity.mUser3 == AVH_USER3_HIVE ) { outFrame=4; } else if ( (entity.mUser3 == AVH_USER3_ALIENRESTOWER) || (entity.mUser3 == AVH_USER3_FUNC_RESOURCE) || (entity.mUser3 == AVH_USER3_RESTOWER)) { outFrame=3; } else if ( entity.mUser3 == AVH_USER3_MINE ) { outFrame=2; } else if ( entity.mUser3 == AVH_USER3_WELD ) { outFrame=5; } else if ( isStructure ) { outFrame=0; } } else { outSprite=0; outFrame=0; } } else { gHUD.GetSpriteForUser3(entity.mUser3, outSprite, outFrame, outRenderMode); } } void AvHOverviewMap::GetColorForEntity(const DrawableEntity& entity, float& outR, float& outG, float& outB) { static float attackBlinkPeriod=0.4f; bool isStructure=entity.mUser3 == AVH_USER3_HIVE || entity.mUser3 == AVH_USER3_COMMANDER_STATION || entity.mUser3 == AVH_USER3_TURRET_FACTORY || entity.mUser3 == AVH_USER3_ARMORY || entity.mUser3 == AVH_USER3_ADVANCED_ARMORY || entity.mUser3 == AVH_USER3_ARMSLAB || entity.mUser3 == AVH_USER3_PROTOTYPE_LAB || entity.mUser3 == AVH_USER3_OBSERVATORY || entity.mUser3 == AVH_USER3_TURRET || entity.mUser3 == AVH_USER3_SIEGETURRET || entity.mUser3 == AVH_USER3_RESTOWER || entity.mUser3 == AVH_USER3_INFANTRYPORTAL || entity.mUser3 == AVH_USER3_PHASEGATE || entity.mUser3 == AVH_USER3_DEFENSE_CHAMBER || entity.mUser3 == AVH_USER3_MOVEMENT_CHAMBER || entity.mUser3 == AVH_USER3_OFFENSE_CHAMBER || entity.mUser3 == AVH_USER3_SENSORY_CHAMBER || entity.mUser3 == AVH_USER3_ALIENRESTOWER || entity.mUser3 == AVH_USER3_ADVANCED_TURRET_FACTORY; if ( entity.mIsUnderAttack && (entity.mTeam == mTeam || gEngfuncs.IsSpectateOnly() ) ) { if ( gpGlobals && (gpGlobals->time > this->mBlinkTime + attackBlinkPeriod) ) { this->mBlinkOn=!mBlinkOn; this->mBlinkTime=gpGlobals->time; } if ( this->mBlinkOn ) { outR = 1.0; outG = 0.0; outB = 0.0; return; } } if (entity.mUser3 == AVH_USER3_WAYPOINT) { outR = 0.1; outG = 0.8; outB = 1.0; } else if (entity.mUser3 == AVH_USER3_WELD) { outR = 1.0; outG = 0.7; outB = 0.3; } else if ( entity.mUser3 == AVH_USER3_MINE ) { outR = 0.05; outG = 0.44; outB = 0.61; } else if (entity.mTeam == TEAM_IND) { outR = 0.5; outG = 0.5; outB = 0.5; } else if (entity.mTeam == mTeam && !isStructure) { outR = 1.0; outG = 1.0; outB = 1.0; int localPlayerSquad; if (g_iUser1 == OBS_NONE) { localPlayerSquad = gHUD.GetCurrentSquad(); } else { // We don't have access to the squad information for player's // we're spectating. localPlayerSquad = 0; } if (mUser3 != AVH_USER3_COMMANDER_PLAYER) { if (entity.mIsLocalPlayer ) { outR = 0.0; outG = 1.0; outB = 0.0; } if (entity.mSquadNumber != 0 && entity.mSquadNumber == localPlayerSquad){ outR = 0.0; outG = 1.0; outB = 0.0; } } } else { if ( entity.mTeam == TEAM_ONE ) { outR=0.33; outG=0.95; outB=1.0; } else if ( entity.mTeam == TEAM_TWO ) { if ( entity.mUser3 == AVH_USER3_UNKNOWN ) { outR=1.0; outG=0.72; outB=0.0; } else { outR=1.0; outG=0.85; outB=0.0; } } else if ( entity.mTeam == TEAM_THREE ) { outR=0.92; outG=0.1; outB=0.47; } else if ( entity.mTeam == TEAM_FOUR ) { outR=0.65; outG=0.92; outB=0.0; } else { outR=0.0; outG=0.0; outB=0.0; } if ( isStructure ) { if ( entity.mTeam == TEAM_ONE ) { outR=0.43; outG=0.70; outB=1.0; } else if ( entity.mTeam == TEAM_TWO ) { outR=0.88; outG=0.45; outB=0.00; } } } } void AvHOverviewMap::DrawMiniMapEntity(const DrawInfo& inDrawInfo, const DrawableEntity& inEntity) { if (!GetHasData()) { return; } float theEntityPosX = inEntity.mX; float theEntityPosY = inEntity.mY; // Draw local player smoothly and predicted. if (inEntity.mIsLocalPlayer) { theEntityPosX = this->mWorldPlayerX; theEntityPosY = this->mWorldPlayerY; } AVHHSPRITE theSprite = 0; int theFrame = 0; int theRenderMode; GetSpriteForEntity(inEntity, theSprite, theFrame, theRenderMode, inDrawInfo.mCommander); if (theSprite > 0) { int theSprWidth = SPR_Width(theSprite, theFrame); int theSprHeight = SPR_Height(theSprite, theFrame); int theX = inDrawInfo.mX; int theY = inDrawInfo.mY; int theWidth = inDrawInfo.mWidth; int theHeight = inDrawInfo.mHeight; float viewWorldXSize = inDrawInfo.mViewWorldMaxX - inDrawInfo.mViewWorldMinX; float viewWorldYSize = inDrawInfo.mViewWorldMaxY - inDrawInfo.mViewWorldMinY; float scale = 0.75 * inDrawInfo.mZoomScale; // How much to scale the sprite. bool isPlayer = inEntity.mUser3 == AVH_USER3_MARINE_PLAYER || inEntity.mUser3 == AVH_USER3_HEAVY; //heavy used for player in minimap system bool theIsWaypoint = inEntity.mUser3 == AVH_USER3_WAYPOINT; float w = theSprWidth * scale; float h = theSprHeight * scale; float entityMiniMapX = theX + ((theEntityPosX - inDrawInfo.mViewWorldMinX) / viewWorldXSize) * theWidth; float entityMiniMapY = theY + ((inDrawInfo.mViewWorldMaxY - theEntityPosY) / viewWorldYSize) * theHeight; float x = entityMiniMapX - w / 2.0f; float y = entityMiniMapY - h / 2.0f; if (theIsWaypoint) { float theFractionalLastUpdate = mLastUpdateTime - (int)mLastUpdateTime; if (theFractionalLastUpdate < .25f) { return; } } // Perform gross culling of sprites. if (x + w >= theX && y + h >= theY && x < theX + theWidth && y < theY + theHeight) { float r, g, b; GetColorForEntity(inEntity, r, g, b); AvHSpriteSetColor(r, g, b); // If it's the local player, draw the FOV. if (inEntity.mIsLocalPlayer && mUser3 != AVH_USER3_COMMANDER_PLAYER) { AVHHSPRITE theSprite = SPR_Load("sprites/fov.spr"); int theFrame = 0; int theSprWidth = SPR_Width(theSprite, theFrame); int theSprHeight = SPR_Height(theSprite, theFrame); float w2 = theSprWidth * scale; float h2 = theSprHeight * scale; float x2 = entityMiniMapX; float y2 = entityMiniMapY - h2 / 2; AvHSpriteSetRotation(-inEntity.mAngleRadians * 180 / M_PI, x2, y2 + h2 / 2); AvHSpriteSetColor(1, 1, 1); AvHSpriteSetRenderMode(kRenderTransAdd); AvHSpriteDraw(theSprite, theFrame, x2, y2, x2 + w2, y2 + h2, 0, 0, 1, 1); } if (mUser3 != AVH_USER3_COMMANDER_PLAYER) { AvHSpriteSetRotation(-inEntity.mAngleRadians * 180 / M_PI, x + w / 2, y + h / 2); } else { AvHSpriteSetRotation(0, 0, 0); } AvHSpriteSetColor(r, g, b); AvHSpriteSetRenderMode(theRenderMode); AvHSpriteDraw(theSprite, theFrame, x, y, x + w, y + h, 0, 0, 1, 1); //alien's minimap names //if (inEntity.mIsLocalPlayer) if (CVAR_GET_FLOAT("hud_mapnames") != 0 && ((mUser3 != AVH_USER3_COMMANDER_PLAYER) || CVAR_GET_FLOAT("hud_mapnamesComm") != 0)) { if (!mMapNamesFontLoaded) { tFont.Load("sprites/nl/font_arial"); //font_arialsmall mMapNamesFontLoaded = true; } //string theText; char bufferb[128]; if ((inEntity.mEntityNumber >= 1) && (inEntity.mEntityNumber <= gEngfuncs.GetMaxClients())) { hud_player_info_t thePlayerInfo; gEngfuncs.pfnGetPlayerInfo(inEntity.mEntityNumber, &thePlayerInfo); if (thePlayerInfo.name && !thePlayerInfo.thisplayer && inEntity.mTeam == this->mTeam) { //outEntityInfoString += thePlayerInfo.name; //theText = thePlayerInfo.name; //sprintf(bufferb, "%s", theText.c_str()); int tR = CVAR_GET_FLOAT("hud_mapnamesRed"); int tG = CVAR_GET_FLOAT("hud_mapnamesGreen"); int tB = CVAR_GET_FLOAT("hud_mapnamesBlue"); sprintf(bufferb, "%s", thePlayerInfo.name); if (bufferb) { std::string text(bufferb); if (CVAR_GET_FLOAT("hud_mapnames") >= 2) { //3 letter max //strcpy(rgDeathNoticeList[i].szVictim, killed_with.c_str() + 2); //while (text.length() > 3) { // text[3] //} //bufferb text = text.substr(0, max((int)CVAR_GET_FLOAT("hud_mapnames"), 1)); } tFont.DrawStringCustom(x + 12 - text.length() * 3, y - 18, text.c_str(), tR, tG, tB, kRenderTransAdd); } } } } } if (isPlayer || theIsWaypoint) { const float border = 2; if (!(x + w / 2 >= theX && y + h / 2 >= theY && x + w / 2 < theX + theWidth && y + h / 2 < theY + theHeight)) { // Draw friendly players as little arrows on the edge of the minimap. AVHHSPRITE theSprite = SPR_Load(kMarinePlayersSprite); int theFrame = theIsWaypoint ? 4 : 3; ASSERT(theSprite != 0); int theSprWidth = SPR_Width(theSprite, theFrame); int theSprHeight = SPR_Height(theSprite, theFrame); float tipX = entityMiniMapX; float tipY = entityMiniMapY; if (tipX < theX + border) tipX = theX + border; if (tipY < theY + border) tipY = theY + border; if (tipX > theX + theWidth - border) tipX = theX + theWidth - border; if (tipY > theY + theHeight - border) tipY = theY + theHeight - border; float dx = tipX - entityMiniMapX; float dy = tipY - entityMiniMapY; float angle = atan2(dy, dx) * 180 / M_PI; w = theSprWidth; h = theSprHeight; int renderMode = kRenderTransTexture; if (theIsWaypoint) { renderMode = kRenderTransAdd; } AvHSpriteSetRenderMode(renderMode); float r, g, b; GetColorForEntity(inEntity, r, g, b); AvHSpriteSetColor(r, g, b); AvHSpriteSetRotation(angle, tipX, tipY); AvHSpriteDraw(theSprite, theFrame, tipX, tipY - h / 2 , tipX + w, tipY + h / 2, 0, 0, 1, 1); } } } } bool AvHOverviewMap::GetHasData() const { return this->mDrawableEntityList.size() > 0; } void AvHOverviewMap::GetWorldPosFromMouse(const DrawInfo& inDrawInfo, int inMouseX, int inMouseY, float& outWorldX, float& outWorldY) { float viewWorldXSize = inDrawInfo.mViewWorldMaxX - inDrawInfo.mViewWorldMinX; float viewWorldYSize = inDrawInfo.mViewWorldMaxY - inDrawInfo.mViewWorldMinY; outWorldX = ((float)(inMouseX) / inDrawInfo.mWidth) * viewWorldXSize + inDrawInfo.mViewWorldMinX; outWorldY = inDrawInfo.mViewWorldMaxY - ((float)(inMouseY) / inDrawInfo.mHeight) * viewWorldYSize; } void AvHOverviewMap::KillOldAlerts(float inCurrentTime) { for(MapAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); /* no inc */) { if(inCurrentTime > theIter->mExpireTime) { theIter = this->mAlertList.erase(theIter); } else { ++theIter; } } } void AvHOverviewMap::DrawMiniMap(const DrawInfo& inDrawInfo) { // : 1064 // Use labelled minimaps if cl_labelmaps is 1 // Load the mini-map sprite if it's not already loaded. if ( mMapName != "" ) { int drawLabels=CVAR_GET_FLOAT(kvLabelMaps); if ( mLastMinimap != drawLabels || mMiniMapSprite == -1 ) { AVHHSPRITE tmpSpr=0; for ( int i=drawLabels; i >=0 && tmpSpr == 0 ; i-- ) { string theMiniMapName = AvHMiniMap::GetSpriteNameFromMap(ScreenWidth(), mMapName, i); tmpSpr = SPR_Load(theMiniMapName.c_str()); } if ( tmpSpr != 0 ) { mMiniMapSprite=tmpSpr; mLastMinimap=drawLabels; } } } // : if (!mMiniMapSprite) { return; } float mapXSize = mMapExtents.GetMaxMapX() - mMapExtents.GetMinMapX(); float mapYSize = mMapExtents.GetMaxMapY() - mMapExtents.GetMinMapY(); float mapXCenter = (mMapExtents.GetMaxMapX() + mMapExtents.GetMinMapX()) / 2; float mapYCenter = (mMapExtents.GetMaxMapY() + mMapExtents.GetMinMapY()) / 2; float aspectRatio = mapXSize / mapYSize; float xScale; float yScale; if(mapXSize > mapYSize) { xScale = 1.0f; yScale = mapYSize / mapXSize; } else { xScale = mapYSize / mapXSize; yScale = 1.0f; } float x1 = mapXCenter - mapXSize * xScale / 2; float y1 = mapYCenter + mapYSize * yScale / 2; WorldToMiniMapCoords(inDrawInfo, x1, y1); float x2 = mapXCenter + mapXSize * xScale / 2; float y2 = mapYCenter - mapYSize * yScale / 2; WorldToMiniMapCoords(inDrawInfo, x2, y2); AvHSpriteSetRenderMode(kRenderTransTexture); AvHSpriteSetRotation(0, 0, 0); // TODO this should be based on a flag not the user3 if (mUser3 == AVH_USER3_COMMANDER_PLAYER) { // Use the small map if it's the commander view. AvHSpriteDraw(mMiniMapSprite, 4, x1, y1, x2, y2, 0, 0, 1, 1); } else { AvHSpriteDrawTiles(mMiniMapSprite, 2, 2, x1, y1, x2, y2, 0, 0, 1, 1); } // Reset orientation after potentially orienting above to make sure it doesn't affect subsequent code AvHSpriteSetRotation(0, 0, 0); } void AvHOverviewMap::WorldToMiniMapCoords(const DrawInfo& inDrawInfo, float& x, float& y) { float viewWorldXSize = inDrawInfo.mViewWorldMaxX - inDrawInfo.mViewWorldMinX; float viewWorldYSize = inDrawInfo.mViewWorldMaxY - inDrawInfo.mViewWorldMinY; x = inDrawInfo.mX + ((x - inDrawInfo.mViewWorldMinX) / viewWorldXSize) * inDrawInfo.mWidth; y = inDrawInfo.mY + ((inDrawInfo.mViewWorldMaxY - y) / viewWorldYSize) * inDrawInfo.mHeight; } void AvHOverviewMap::DrawAlerts(const DrawInfo& inDrawInfo) { int theX = inDrawInfo.mX; int theY = inDrawInfo.mY; int theWidth = inDrawInfo.mWidth; int theHeight = inDrawInfo.mHeight; AvHSpriteEnableClippingRect(true); AvHSpriteSetClippingRect(theX, theY, theX + theWidth, theY + theHeight); AVHHSPRITE theSprite = SPR_Load(kAlertSprite); int theFrame = 0; ASSERT(theSprite != 0); int theSprWidth = SPR_Width(theSprite, theFrame); int theSprHeight = SPR_Height(theSprite, theFrame); float viewWorldXSize = inDrawInfo.mViewWorldMaxX - inDrawInfo.mViewWorldMinX; float viewWorldYSize = inDrawInfo.mViewWorldMaxY - inDrawInfo.mViewWorldMinY; for (unsigned int i = 0; i < mAlertList.size(); ++i) { float maxAlertSize = 5; float minAlertSize = 0.4; float transitionTime = 1; float dt = (mLastUpdateTime - mAlertList[i].mStartTime) / transitionTime; if (dt > 1) dt = 1; float scale = (1 - sqrt(dt)) * (maxAlertSize - minAlertSize) + minAlertSize; float w = theSprWidth * scale; float h = theSprHeight * scale; float cx = mAlertList[i].mX; float cy = mAlertList[i].mY; WorldToMiniMapCoords(inDrawInfo, cx, cy); float angle = dt * 180; float fadeOutStartTime = mAlertList[i].mExpireTime - (transitionTime / 2); float alpha = 1 - (mLastUpdateTime - fadeOutStartTime) / (transitionTime / 2); if (alpha < 0) { alpha = 0; } AvHSpriteSetRenderMode(kRenderTransAdd); AvHSpriteSetColor(1, 1, 1, alpha * 0.1); AvHSpriteSetRotation(angle, cx, cy); AvHSpriteDraw(theSprite, theFrame, cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2, 0, 0, 1, 1); } } void AvHOverviewMap::AddAlert(float x, float y) { MapAlert alert; alert.mStartTime = mLastUpdateTime; alert.mExpireTime = mLastUpdateTime + BALANCE_VAR(kAlertExpireTime) / 5; alert.mX = x; alert.mY = y; mAlertList.push_back(alert); } void AvHOverviewMap::Draw(const DrawInfo& inDrawInfo) { int theX = inDrawInfo.mX; int theY = inDrawInfo.mY; int theCompWidth = inDrawInfo.mWidth; int theCompHeight = inDrawInfo.mHeight; AvHSpriteBeginFrame(); AvHSpriteEnableClippingRect(true); AvHSpriteSetClippingRect(theX, theY, theX + theCompWidth, theY + theCompHeight); // Draw the minimap background. DrawMiniMap(inDrawInfo); // Draw the entities on the minimap. if (mUser3 == AVH_USER3_COMMANDER_PLAYER) { AvHSpriteEnableClippingRect(false); } // render order: structures -> structures under attack -> players -> players under attack DrawableEntityListType attackedStructures; DrawableEntityListType attackedPlayers; DrawableEntityListType players; for (DrawableEntityListType::const_iterator theIter = this->mDrawableEntityList.begin(); theIter != this->mDrawableEntityList.end(); theIter++) { if ( (*theIter).mUser3 > AVH_USER3_NONE && (*theIter).mUser3 <= AVH_USER3_ALIEN_EMBRYO ) { if ( (*theIter).mIsUnderAttack == true ) attackedPlayers.push_back(*theIter); else players.push_back(*theIter); } else if ( (*theIter).mIsUnderAttack == true ) attackedStructures.push_back(*theIter); else DrawMiniMapEntity(inDrawInfo, *theIter); } for (DrawableEntityListType::const_iterator theIter = attackedStructures.begin(); theIter != attackedStructures.end(); theIter++) { DrawMiniMapEntity(inDrawInfo, *theIter); } for (DrawableEntityListType::const_iterator theIter = players.begin(); theIter != players.end(); theIter++) { DrawMiniMapEntity(inDrawInfo, *theIter); } for (DrawableEntityListType::const_iterator theIter = attackedPlayers.begin(); theIter != attackedPlayers.end(); theIter++) { DrawMiniMapEntity(inDrawInfo, *theIter); } // Draw the way points as entities. { for (MapOrderListType::const_iterator theIter = mMapOrderList.begin(); theIter != mMapOrderList.end(); theIter++) { DrawableEntity drawableEntity; drawableEntity.mUser3 = AVH_USER3_WAYPOINT; drawableEntity.mX = theIter->mX; drawableEntity.mY = theIter->mY; DrawMiniMapEntity(inDrawInfo, drawableEntity); } } // Draw the alerts. DrawAlerts(inDrawInfo); // Draw the reticle. if(this->mUser3 == AVH_USER3_COMMANDER_PLAYER) { int theFrame = 0; if (!this->mReticleSprite) { this->mReticleSprite = SPR_Load(kReticleSprite); } if (this->mReticleSprite) { float x = mWorldPlayerX; float y = mWorldPlayerY; WorldToMiniMapCoords(inDrawInfo, x, y); float w = SPR_Width(this->mReticleSprite, theFrame); float h = SPR_Height(this->mReticleSprite, theFrame); AvHSpriteSetRenderMode(kRenderTransAdd); AvHSpriteSetColor(1, 1, 1); AvHSpriteSetRotation(0, 0, 0); AvHSpriteDraw(mReticleSprite, theFrame, x - w / 2, y - h / 2, x + w / 2, y + h / 2, 0, 0, 1, 1); } } // Reset orientation after potentially orienting above to make sure it doesn't affect subsequent code AvHSpriteSetRotation(0, 0, 0); AvHSpriteEndFrame(); } int AvHOverviewMap::GetEntityAtWorldPosition(float inWorldX, float inWorldY, float inRadius) const { for (int i = 0; i < (int)mDrawableEntityList.size(); ++i) { float dx = mDrawableEntityList[i].mX - inWorldX; float dy = mDrawableEntityList[i].mY - inWorldY; if (dx * dx + dy * dy < inRadius * inRadius) { return mDrawableEntityList[i].mEntityNumber; } } return 0; } void AvHOverviewMap::SetMapExtents(const string& inMapName, const AvHMapExtents& inMapExtents) { this->mMapName = inMapName; this->mMapExtents = inMapExtents; } void AvHOverviewMap::GetMapExtents(AvHMapExtents& outMapExtents) { outMapExtents = mMapExtents; } void AvHOverviewMap::SetUser3(AvHUser3 inUser3) { this->mUser3 = inUser3; } void AvHOverviewMap::SetWorldPosition(float inPlayerX, float inPlayerY) { mWorldPlayerX = inPlayerX; mWorldPlayerY = inPlayerY; } void AvHOverviewMap::GetWorldPosition(float& outWorldX, float& outWorldY) { outWorldX = mWorldPlayerX; outWorldY = mWorldPlayerY; } void AvHOverviewMap::Update(float inCurrentTime) { mLastUpdateTime = inCurrentTime; // if(gHUD.GetGameStarted()) // { // this->mMap.Update(inCurrentTime, this->mMapExtents.GetMinMapX(), this->mMapExtents.GetMinMapY(), this->mMapExtents.GetMaxMapX(), this->mMapExtents.GetMaxMapY(), this->mViewHeight); // } // Get player data from engine and store for use during draw this->UpdateDrawData(inCurrentTime); // Kill off any old alerts this->KillOldAlerts(inCurrentTime); } const float kPositionNetworkConstant = 2.0f; void AvHOverviewMap::UpdateDrawData(float inCurrentTime) { int theLocalPlayerIndex; if (g_iUser1 == OBS_NONE) { cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); theLocalPlayerIndex = thePlayer->index; } else { theLocalPlayerIndex = g_iUser2; } cl_entity_s* thePlayer = gEngfuncs.GetEntityByIndex(theLocalPlayerIndex); mTeam = (AvHTeamNumber)(thePlayer->curstate.team); // Clear list of drawable entities this->mDrawableEntityList.clear(); // Get all entities MapEntityMap theEntityList; gHUD.GetEntityHierarchy().GetEntityInfoList(theEntityList); // For each entity for(MapEntityMap::iterator theIter = theEntityList.begin(); theIter != theEntityList.end(); theIter++) { // If the player has no leader, then he IS a leader int theCurrentPlayerIndex = theIter->first; bool theIsLocalPlayer = (theLocalPlayerIndex == theCurrentPlayerIndex); DrawableEntity theDrawableEntity; theDrawableEntity.mEntityNumber = theIter->first; theDrawableEntity.mX = theIter->second.mX; theDrawableEntity.mY = theIter->second.mY; theDrawableEntity.mUser3 = theIter->second.mUser3; theDrawableEntity.mTeam = theIter->second.mTeam; theDrawableEntity.mAngleRadians = theIter->second.mAngle * M_PI / 180; theDrawableEntity.mSquadNumber = theIter->second.mSquadNumber; theDrawableEntity.mIsUnderAttack = theIter->second.mUnderAttack; // Returns position relative to minimap, so add it back in // commented this out here, commented out corresponding shift in AvHEntityHierarchy::BuildFromTeam at line 234 // theDrawableEntity.mX += this->mMapExtents.GetMinMapX(); // theDrawableEntity.mY += this->mMapExtents.GetMinMapY(); theDrawableEntity.mIsLocalPlayer = theIsLocalPlayer; // Get additional information about the entity from the client state. cl_entity_t* clientEntity = gEngfuncs.GetEntityByIndex(theDrawableEntity.mEntityNumber); if(clientEntity) { if (clientEntity->index >= 32) { theDrawableEntity.mAngleRadians = clientEntity->angles[1] * M_PI / 180; } // Update the information for this entity from the client information // if they're in the local player's PVS. // We really want to check if the client data is more recent than the // minimap data, but I don't know how to get the timestamp on the minimap // data. if (clientEntity->curstate.messagenum >= thePlayer->curstate.messagenum) { //theDrawableEntity.mUser3 = (AvHUser3)(clientEntity->curstate.iuser3); // Brush entities don't have the correct position information, so // don't update them from the client data. if (theDrawableEntity.mUser3 != AVH_USER3_WELD) { theDrawableEntity.mX = clientEntity->origin.x; theDrawableEntity.mY = clientEntity->origin.y; theDrawableEntity.mAngleRadians = clientEntity->angles[1] * M_PI / 180; } } else { // If the difference between the minimap position and the client data // position is less than the minimap quantization error, then use // the client position to avoid popping when the entity goes out of the // PVS. float dx = fabs(theDrawableEntity.mX - clientEntity->origin.x); float dy = fabs(theDrawableEntity.mY - clientEntity->origin.y); if (dx < kPositionNetworkConstant && dy < kPositionNetworkConstant) { theDrawableEntity.mX = clientEntity->origin.x; theDrawableEntity.mY = clientEntity->origin.y; } } if (theDrawableEntity.mUser3 != AVH_USER3_COMMANDER_PLAYER) { this->mDrawableEntityList.push_back(theDrawableEntity); } } } /* cl_entity_t* clientEntity = gEngfuncs.GetEntityByIndex(theLocalPlayerIndex); if(clientEntity) { DrawableEntity theDrawableEntity; theDrawableEntity.mX = clientEntity->origin.x; theDrawableEntity.mY = clientEntity->origin.y; theDrawableEntity.mAngleRadians = clientEntity->angles[1] * M_PI / 180; this->mDrawableEntityList.push_back(theDrawableEntity); } */ std::sort(mDrawableEntityList.begin(), mDrawableEntityList.end(), DrawingOrderSort()); } void AvHOverviewMap::UpdateOrders(const OrderListType& inOrderList, const EntityListType& inDrawPlayers) { // Look for orders which apply to the players in the draw players list. mMapOrderList.clear(); if (mUser3 == AVH_USER3_COMMANDER_PLAYER) { return; } for (OrderListType::const_iterator theIter = inOrderList.begin(); theIter != inOrderList.end(); ++theIter) { // Draw the order if the order is for any plays that are in our draw player list bool theDrawWaypoint = false; EntityInfo theReceiverPlayer = theIter->GetReceiver(); EntityListType::const_iterator theSearchIter = std::find(inDrawPlayers.begin(), inDrawPlayers.end(), theReceiverPlayer); if(theSearchIter != inDrawPlayers.end()) { theDrawWaypoint = true; } if (theDrawWaypoint) { vec3_t position; theIter->GetLocation(position); MapOrder mapOrder; mapOrder.mX = position[0]; mapOrder.mY = position[1]; mMapOrderList.push_back(mapOrder); } } } void AvHOverviewMap::VidInit(void) { this->mMiniMapSprite = 0; this->mReticleSprite = 0; this->mMapName = ""; }