mirror of
https://github.com/ENSL/NS.git
synced 2024-12-15 07:01:33 +00:00
081798e78b
git-svn-id: https://unknownworlds.svn.cloudforge.com/ns1@526 67975925-1194-0748-b3d5-c16f83f1a3a1
1016 lines
No EOL
29 KiB
C++
1016 lines
No EOL
29 KiB
C++
#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 "mod/AvHOverviewMap.h"
|
|
#include "mod/AvHSharedUtil.h"
|
|
#include "mod/AvHSpecials.h"
|
|
#include "mod/AvHMiniMap.h"
|
|
#include "ui/UIUtil.h"
|
|
#include "mod/AvHPlayerUpgrade.h"
|
|
#include "mod/AvHSpriteAPI.h"
|
|
#include "mod/AvHSprites.h"
|
|
#include "mod/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;
|
|
// puzl: 1066 reset overview map
|
|
this->mLastMinimapName = "";
|
|
this->mBlinkTime=0.0f;
|
|
this->mBlinkOn=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, int& outSprite, int& outFrame, int& outRenderMode)
|
|
{
|
|
outRenderMode = kRenderTransTexture;
|
|
|
|
if (entity.mUser3 == AVH_USER3_UNKNOWN)
|
|
{
|
|
outSprite = Safe_SPR_Load(kCommBlipSprite);
|
|
outFrame=1;
|
|
}
|
|
else if (this->mUser3 == AVH_USER3_COMMANDER_PLAYER)
|
|
{
|
|
if ( getIsOnCommMinimap(entity.mUser3 ) ) {
|
|
bool isStructure=getIsStructure(entity.mUser3);
|
|
bool isFriendly=entity.mTeam == mTeam;
|
|
outSprite = Safe_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.3f;
|
|
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->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.43;
|
|
outG=0.70;
|
|
outB=1.0;
|
|
}
|
|
else if ( entity.mTeam == TEAM_TWO ) {
|
|
outR=1.0;
|
|
outG=0.73;
|
|
outB=0.0;
|
|
}
|
|
else if ( entity.mTeam == TEAM_THREE ) {
|
|
outR=0.92;
|
|
outG=0.0;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int theSprite = 0;
|
|
int theFrame = 0;
|
|
int theRenderMode;
|
|
|
|
GetSpriteForEntity(inEntity, theSprite, theFrame, theRenderMode);
|
|
|
|
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; // 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)
|
|
{
|
|
|
|
int theSprite = Safe_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);
|
|
|
|
}
|
|
|
|
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.
|
|
|
|
int theSprite = Safe_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)
|
|
{
|
|
|
|
// puzl: 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);
|
|
string theMiniMapName = AvHMiniMap::GetSpriteNameFromMap(ScreenWidth(), mMapName, drawLabels);
|
|
if ( mLastMinimapName != theMiniMapName )
|
|
{
|
|
mMiniMapSprite = Safe_SPR_Load(theMiniMapName.c_str());
|
|
|
|
// We want to preserve the last minimap even if we fail. There's no point in failing again until the player
|
|
// changes the value of the cvar.
|
|
mLastMinimapName=theMiniMapName;
|
|
|
|
// Draw normal minimap if no labelled map exists ( for custom maps )
|
|
if ( !mMiniMapSprite && drawLabels ) {
|
|
theMiniMapName = AvHMiniMap::GetSpriteNameFromMap(ScreenWidth(), mMapName, 0);
|
|
mMiniMapSprite = Safe_SPR_Load(theMiniMapName.c_str());
|
|
}
|
|
}
|
|
}
|
|
// :puzl
|
|
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);
|
|
|
|
int theSprite = Safe_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);
|
|
}
|
|
|
|
for (DrawableEntityListType::const_iterator theIter = this->mDrawableEntityList.begin(); theIter != this->mDrawableEntityList.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 = Safe_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 = "";
|
|
} |