diff --git a/main/source/Natural_Selection.ncb b/main/source/Natural_Selection.ncb index 764fe88..695495f 100644 Binary files a/main/source/Natural_Selection.ncb and b/main/source/Natural_Selection.ncb differ diff --git a/main/source/Natural_Selection.suo b/main/source/Natural_Selection.suo index 3dd4cc2..8418e98 100644 Binary files a/main/source/Natural_Selection.suo and b/main/source/Natural_Selection.suo differ diff --git a/main/source/cl_dll/ammo.cpp b/main/source/cl_dll/ammo.cpp index d44b869..5c7a972 100644 --- a/main/source/cl_dll/ammo.cpp +++ b/main/source/cl_dll/ammo.cpp @@ -1,1357 +1,1461 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -// -// Ammo.cpp -// -// implementation of CHudAmmo class -// - -#include "hud.h" -#include "cl_util.h" - -#include -#include - -#include "ammohistory.h" -#include "vgui_TeamFortressViewport.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHScrollHandler.h" -#include "mod/AvHNetworkMessages.h" - -WEAPON *gpActiveSel; // NULL means off, 1 means just the menu bar, otherwise - // this points to the active weapon menu item -WEAPON *gpLastSel; // Last weapon menu selection - -bool HUD_GetWeaponEnabled(int inID); -client_sprite_t *GetSpriteList(client_sprite_t *pList, const char *psz, int iRes, int iCount); - -WeaponsResource gWR; - -int g_weaponselect = 0; - -//Equivalent to DECLARE_COMMAND(lastinv,LastInv) except we use gWR instead of gHud -void __CmdFunc_LastInv(void) -{ gWR.UserCmd_LastInv(); } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WeaponsResource::WeaponsResource(void) : lastWeapon(NULL), iOldWeaponBits(0) {} -WeaponsResource::~WeaponsResource(void) {} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::Init( void ) -{ - memset( rgWeapons, 0, sizeof(WEAPON)*MAX_WEAPONS ); - Reset(); - HOOK_COMMAND("lastinv",LastInv); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::Reset( void ) -{ - lastWeapon = NULL; - iOldWeaponBits = 0; - memset( rgSlots, 0, sizeof(WEAPON*)*MAX_WEAPON_SLOTS*MAX_WEAPON_POSITIONS ); - memset( riAmmo, 0, sizeof(int)*MAX_AMMO_TYPES ); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource :: LoadAllWeaponSprites( void ) -{ - for (int i = 0; i < MAX_WEAPONS; i++) - { - if ( rgWeapons[i].iId ) - LoadWeaponSprites( &rgWeapons[i] ); - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -inline void LoadWeaponSprite( client_sprite_t* ptr, HSPRITE& sprite, wrect_t& bounds ) -{ - if( ptr ) - { - string name( "sprites/" ); - name.append( ptr->szSprite ); - name.append( ".spr" ); - sprite = Safe_SPR_Load(name.c_str()); - bounds = ptr->rc; - } - else - { - sprite = NULL; - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource :: LoadWeaponSprites( WEAPON *pWeapon ) -{ - int i, iRes; - - if (ScreenWidth() < 640) - iRes = 320; - else - iRes = 640; - - char sz[128]; - - if ( !pWeapon ) - return; - - memset( &pWeapon->rcActive, 0, sizeof(wrect_t) ); - memset( &pWeapon->rcInactive, 0, sizeof(wrect_t) ); - memset( &pWeapon->rcAmmo, 0, sizeof(wrect_t) ); - memset( &pWeapon->rcAmmo2, 0, sizeof(wrect_t) ); - pWeapon->hInactive = 0; - pWeapon->hActive = 0; - pWeapon->hAmmo = 0; - pWeapon->hAmmo2 = 0; - - sprintf(sz, "sprites/%s.txt", pWeapon->szName); - client_sprite_t *pList = SPR_GetList(sz, &i); - - if (!pList) - { - ASSERT(pList); - return; - } - - LoadWeaponSprite( GetSpriteList( pList, "crosshair", iRes, i ), pWeapon->hCrosshair, pWeapon->rcCrosshair ); - LoadWeaponSprite( GetSpriteList( pList, "autoaim", iRes, i ), pWeapon->hAutoaim, pWeapon->rcAutoaim ); - LoadWeaponSprite( GetSpriteList( pList, "zoom", iRes, i ), pWeapon->hZoomedCrosshair, pWeapon->rcZoomedCrosshair ); - LoadWeaponSprite( GetSpriteList( pList, "zoom_autoaim", iRes, i ), pWeapon->hZoomedAutoaim, pWeapon->rcZoomedAutoaim ); - LoadWeaponSprite( GetSpriteList( pList, "weapon", iRes, i ), pWeapon->hInactive, pWeapon->rcInactive ); - LoadWeaponSprite( GetSpriteList( pList, "weapon_s", iRes, i ), pWeapon->hActive, pWeapon->rcActive ); - LoadWeaponSprite( GetSpriteList( pList, "ammo", iRes, i ), pWeapon->hAmmo, pWeapon->rcAmmo ); - LoadWeaponSprite( GetSpriteList( pList, "ammo2", iRes, i ), pWeapon->hAmmo2, pWeapon->rcAmmo2 ); - - if( pWeapon->hZoomedCrosshair == NULL ) //default to non-zoomed crosshair - { - pWeapon->hZoomedCrosshair = pWeapon->hCrosshair; - pWeapon->rcZoomedCrosshair = pWeapon->rcCrosshair; - } - - if( pWeapon->hAutoaim == NULL ) //default to non-autoaim crosshair - { - pWeapon->hAutoaim = pWeapon->hCrosshair; - pWeapon->rcAutoaim = pWeapon->rcCrosshair; - } - - if( pWeapon->hZoomedAutoaim == NULL ) //default to non-autoaim zoomed crosshair - { - pWeapon->hZoomedAutoaim = pWeapon->hZoomedCrosshair; - pWeapon->rcZoomedAutoaim = pWeapon->rcZoomedCrosshair; - } - - if( pWeapon->hActive || pWeapon->hInactive || pWeapon->hAmmo || pWeapon->hAmmo2 ) - { gHR.iHistoryGap = max( gHR.iHistoryGap, pWeapon->rcActive.bottom - pWeapon->rcActive.top ); } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WEAPON* WeaponsResource::GetWeapon( int iId ) -{ - if( iId < 0 || iId >= MAX_WEAPONS ) { return NULL; } - return rgWeapons[iId].iId ? &rgWeapons[iId] : NULL; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WEAPON* WeaponsResource::GetWeaponSlot( int slot, int pos ) { return rgSlots[slot][pos]; } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WEAPON* WeaponsResource::GetFirstPos( int iSlot ) -{ - WEAPON *returnVal = NULL; - ASSERT( iSlot < MAX_WEAPON_SLOTS && iSlot >= 0 ); - - for( int counter = 0; counter < MAX_WEAPON_POSITIONS; ++counter ) - { - if( this->IsSelectable(rgSlots[iSlot][counter]) ) - { - returnVal = rgSlots[iSlot][counter]; - break; - } - } - - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -WEAPON* WeaponsResource::GetNextActivePos( int iSlot, int iSlotPos ) -{ - WEAPON* returnVal = NULL; - ASSERT( iSlot < MAX_WEAPON_SLOTS && iSlot >= 0 ); - ASSERT( iSlotPos >= 0 ); - - for( int counter = iSlotPos+1; counter < MAX_WEAPON_POSITIONS; ++counter ) - { - if( this->IsSelectable(rgSlots[iSlot][counter]) ) - { - returnVal = rgSlots[iSlot][counter]; - break; - } - } - - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool WeaponsResource::IsEnabled(WEAPON* p) -{ - if( p == NULL ) { return false; } - return HUD_GetWeaponEnabled(p->iId) && this->HasAmmo(p); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool WeaponsResource::IsSelectable(WEAPON* p) -{ - if( p == NULL ) { return false; } - return HUD_GetWeaponEnabled(p->iId); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool WeaponsResource::HasAmmo( WEAPON* p ) -{ - if( p == NULL) { return false; } - //note : if max ammo capacity is -1, this has always returned true in spite of not - // having actual ammo -- KGP - return (p->iAmmoType == -1) || (p->iMax1 == -1) || p->iClip > 0 || CountAmmo(p->iAmmoType) - || CountAmmo(p->iAmmo2Type) || (p->iFlags & WEAPON_FLAGS_SELECTIONEMPTY ); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -int WeaponsResource::CountAmmo( int iId ) -{ - ASSERT( iId < MAX_AMMO_TYPES ); - if( iId < 0 ) return 0; - return riAmmo[iId]; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -int WeaponsResource::GetAmmo( int iId ) -{ - ASSERT( iId < MAX_AMMO_TYPES && iId > -1 ); - return riAmmo[iId]; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::SetAmmo( int iId, int iCount ) -{ - ASSERT( iId < MAX_AMMO_TYPES && iId > -1 ); - riAmmo[iId] = iCount; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -HSPRITE* WeaponsResource::GetAmmoPicFromWeapon( int iAmmoId, wrect_t& rect ) -{ - for ( int i = 0; i < MAX_WEAPONS; i++ ) - { - if ( rgWeapons[i].iAmmoType == iAmmoId ) - { - rect = rgWeapons[i].rcAmmo; - return &rgWeapons[i].hAmmo; - } - else if ( rgWeapons[i].iAmmo2Type == iAmmoId ) - { - rect = rgWeapons[i].rcAmmo2; - return &rgWeapons[i].hAmmo2; - } - } - - return NULL; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::AddWeapon( WEAPON *wp ) -{ - rgWeapons[ wp->iId ] = *wp; - LoadWeaponSprites( &rgWeapons[ wp->iId ] ); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::PickupWeapon( WEAPON *wp ) -{ - rgSlots[ wp->iSlot ][ wp->iSlotPos ] = wp; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::DropWeapon( WEAPON *wp ) -{ - rgSlots[ wp->iSlot ][ wp->iSlotPos ] = NULL; - if(lastWeapon == wp) //dropped last weapon, remove it from the list - { lastWeapon = NULL; } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::DropAllWeapons( void ) -{ - for ( int i = 0; i < MAX_WEAPONS; i++ ) - { - if ( rgWeapons[i].iId ) - DropWeapon( &rgWeapons[i] ); - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::UserCmd_LastInv(void) -{ - if(this->IsSelectable(this->lastWeapon)) - { - this->SetCurrentWeapon(lastWeapon); - const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_ON); - gHUD.PlayHUDSound(theSound, kHUDSoundVolume); - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::SetValidWeapon(void) -{ - WEAPON* p = this->GetFirstPos(0); //alien attack 1 or primary marine weapon - if(gHUD.GetIsAlien()) - { - this->SetCurrentWeapon(p); - } - else - { - if(this->IsSelectable(p) && this->HasAmmo(p)) - { - this->SetCurrentWeapon(p); - } - else - { - p = this->GetFirstPos(1); //pistol slot - if(this->IsSelectable(p) && this->HasAmmo(p)) - { - this->SetCurrentWeapon(p); - } - else - { - p = this->GetFirstPos(2); //knife slot - this->SetCurrentWeapon(p); - } - } - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource::SetCurrentWeapon(WEAPON* newWeapon) -{ - WEAPON* currentWeapon = this->GetWeapon(gHUD.GetCurrentWeaponID()); - // puzl: 497 - Because weapon state can get out of sync, we should allow this even if the weapons are the same - // && newWeapon != currentWeapon - if( newWeapon != NULL ) - { - if( newWeapon != currentWeapon ) - { lastWeapon = currentWeapon; } - ServerCmd(newWeapon->szName); - g_weaponselect = newWeapon->iId; - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void WeaponsResource :: SelectSlot( int iSlot, int fAdvance, int iDirection ) -{ - if ( gHUD.m_Menu.m_fMenuDisplayed && (fAdvance == FALSE) && (iDirection == 1) ) - { // menu is overriding slot use commands - gHUD.m_Menu.SelectMenuItem( iSlot + 1 ); // slots are one off the key numbers - return; - } - - if ( iSlot >= MAX_WEAPON_SLOTS ) - return; - - if ( gHUD.m_fPlayerDead || gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL ) ) - return; - - if (!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) )) //require suit - return; - - if ( ! ( gHUD.m_iWeaponBits & ~(1<<(WEAPON_SUIT)) )) //require something besides suit - return; - - WEAPON *p = NULL; - bool fastSwitch = CVAR_GET_FLOAT( "hud_fastswitch" ) != 0; - if ((gpActiveSel == NULL) || (gpActiveSel == (WEAPON *)1) || (iSlot != gpActiveSel->iSlot)) - { - p = GetFirstPos(iSlot); - const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_ON); - gHUD.PlayHUDSound(theSound, kHUDSoundVolume); - - if (this->IsSelectable(p) && fastSwitch) //check to see if we can use fastSwitch - { - WEAPON *p2 = GetNextActivePos( p->iSlot, p->iSlotPos ); - if (!this->IsSelectable(p2)) //only one target in the bucket - { - this->SetCurrentWeapon(p); - return; - } - } - } - else - { - const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_MOVE_SELECT); - gHUD.PlayHUDSound(theSound, kHUDSoundVolume); - - if ( gpActiveSel ) - p = GetNextActivePos( gpActiveSel->iSlot, gpActiveSel->iSlotPos ); - if ( !p ) - p = GetFirstPos( iSlot ); - } - - if (!this->IsSelectable(p)) // no valid selection found - { - // if fastSwitch is on, ignore, else turn on the menu - if ( !fastSwitch ) { - gpActiveSel = (WEAPON *)1; - } - else { - gpActiveSel = NULL; - } - } - else - { - gpActiveSel = p; - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -int giBucketHeight, giBucketWidth, giABHeight, giABWidth; // Ammo Bar width and height - -HSPRITE ghsprBuckets; // Sprite for top row of weapons menu - -DECLARE_MESSAGE(m_Ammo, CurWeapon ); // Current weapon and clip -DECLARE_MESSAGE(m_Ammo, WeaponList); // new weapon type -DECLARE_MESSAGE(m_Ammo, AmmoX); // update known ammo type's count -DECLARE_MESSAGE(m_Ammo, AmmoPickup); // flashes an ammo pickup record -DECLARE_MESSAGE(m_Ammo, WeapPickup); // flashes a weapon pickup record -DECLARE_MESSAGE(m_Ammo, HideWeapon); // hides the weapon, ammo, and crosshair displays temporarily -DECLARE_MESSAGE(m_Ammo, ItemPickup); - -DECLARE_COMMAND(m_Ammo, Slot1); -DECLARE_COMMAND(m_Ammo, Slot2); -DECLARE_COMMAND(m_Ammo, Slot3); -DECLARE_COMMAND(m_Ammo, Slot4); -DECLARE_COMMAND(m_Ammo, Slot5); -DECLARE_COMMAND(m_Ammo, Slot6); -DECLARE_COMMAND(m_Ammo, Slot7); -DECLARE_COMMAND(m_Ammo, Slot8); -DECLARE_COMMAND(m_Ammo, Slot9); -DECLARE_COMMAND(m_Ammo, Slot10); -DECLARE_COMMAND(m_Ammo, Close); -DECLARE_COMMAND(m_Ammo, NextWeapon); -DECLARE_COMMAND(m_Ammo, PrevWeapon); - -// width of ammo fonts -#define AMMO_SMALL_WIDTH 10 -#define AMMO_LARGE_WIDTH 20 - -#define HISTORY_DRAW_TIME "5" - -int CHudAmmo::Init(void) -{ - gHUD.AddHudElem(this); - - HOOK_MESSAGE(CurWeapon); - HOOK_MESSAGE(WeaponList); - HOOK_MESSAGE(AmmoPickup); - HOOK_MESSAGE(WeapPickup); - HOOK_MESSAGE(ItemPickup); - HOOK_MESSAGE(HideWeapon); - HOOK_MESSAGE(AmmoX); - - HOOK_COMMAND("slot1", Slot1); - HOOK_COMMAND("slot2", Slot2); - HOOK_COMMAND("slot3", Slot3); - HOOK_COMMAND("slot4", Slot4); - HOOK_COMMAND("slot5", Slot5); - HOOK_COMMAND("slot6", Slot6); - HOOK_COMMAND("slot7", Slot7); - HOOK_COMMAND("slot8", Slot8); - HOOK_COMMAND("slot9", Slot9); - HOOK_COMMAND("slot10", Slot10); - HOOK_COMMAND("cancelselect", Close); - HOOK_COMMAND("invnext", NextWeapon); - HOOK_COMMAND("invprev", PrevWeapon); - - Reset(); - - CVAR_CREATE( "hud_drawhistory_time", HISTORY_DRAW_TIME, 0 ); - CVAR_CREATE( "hud_fastswitch", "0", FCVAR_ARCHIVE ); // controls whether or not weapons can be selected in one keypress - - m_iFlags |= HUD_ACTIVE; //!!! - - gWR.Init(); - gHR.Init(); - - return 1; -}; - -void CHudAmmo::Reset(void) -{ - m_fFade = 0; - m_iFlags |= HUD_ACTIVE; //!!! - - gpActiveSel = NULL; - gHUD.m_iHideHUDDisplay = 0; - - gWR.Reset(); - gHR.Reset(); - - // VidInit(); - -} - -int CHudAmmo::VidInit(void) -{ - // Load sprites for buckets (top row of weapon menu) - m_HUD_bucket0 = gHUD.GetSpriteIndex( "bucket1" ); - m_HUD_selection = gHUD.GetSpriteIndex( "selection" ); - - ghsprBuckets = gHUD.GetSprite(m_HUD_bucket0); - giBucketWidth = gHUD.GetSpriteRect(m_HUD_bucket0).right - gHUD.GetSpriteRect(m_HUD_bucket0).left; - giBucketHeight = gHUD.GetSpriteRect(m_HUD_bucket0).bottom - gHUD.GetSpriteRect(m_HUD_bucket0).top; - - gHR.iHistoryGap = max( gHR.iHistoryGap, gHUD.GetSpriteRect(m_HUD_bucket0).bottom - gHUD.GetSpriteRect(m_HUD_bucket0).top); - - // If we've already loaded weapons, let's get new sprites - gWR.LoadAllWeaponSprites(); - - if (ScreenWidth() >= 640) - { - giABWidth = 20; - giABHeight = 4; - } - else - { - giABWidth = 10; - giABHeight = 2; - } - - return 1; -} - -// -// Think: -// Used for selection of weapon menu item. -// -void CHudAmmo::Think(void) -{ - if ( gHUD.m_fPlayerDead ) - return; - - if ( gHUD.m_iWeaponBits != gWR.iOldWeaponBits ) - { - gWR.iOldWeaponBits = gHUD.m_iWeaponBits; - bool droppedCurrent = false; - - for (int i = MAX_WEAPONS-1; i > 0; i-- ) - { - WEAPON *p = gWR.GetWeapon(i); - - if ( p ) - { - if ( gHUD.m_iWeaponBits & ( 1 << p->iId ) ) - gWR.PickupWeapon( p ); - else - gWR.DropWeapon( p ); - } - } - } - - if(gHUD.GetIsAlien()) //check for hive death causing loss of current weapon - { - WEAPON* currentWeapon = gWR.GetWeapon(gHUD.GetCurrentWeaponID()); - if(!gWR.IsSelectable(currentWeapon)) //current weapon isn't valid - { - gWR.SetValidWeapon(); //get best option - } - } - - if (!gpActiveSel) - return; - - // has the player selected one? - if (gHUD.m_iKeyBits & IN_ATTACK) - { - if (gpActiveSel != (WEAPON *)1) - { - gWR.SetCurrentWeapon(gpActiveSel); - } - - gpLastSel = gpActiveSel; - gpActiveSel = NULL; - gHUD.m_iKeyBits &= ~IN_ATTACK; - - const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_SELECT); - gHUD.PlayHUDSound(theSound, kHUDSoundVolume); - } -} - - -//------------------------------------------------------------------------ -// Message Handlers -//------------------------------------------------------------------------ - -// -// AmmoX -- Update the count of a known type of ammo -// -int CHudAmmo::MsgFunc_AmmoX(const char *pszName, int iSize, void *pbuf) -{ - int iIndex, iCount; - NetMsg_AmmoX( pbuf, iSize, iIndex, iCount ); - gWR.SetAmmo( iIndex, abs(iCount) ); - - return 1; -} - -int CHudAmmo::MsgFunc_AmmoPickup( const char *pszName, int iSize, void *pbuf ) -{ - int iIndex, iCount; - NetMsg_AmmoPickup( pbuf, iSize, iIndex, iCount ); - - // Add ammo to the history - gHR.AddToHistory( HISTSLOT_AMMO, iIndex, abs(iCount) ); - - return 1; -} - -int CHudAmmo::MsgFunc_WeapPickup( const char *pszName, int iSize, void *pbuf ) -{ - int iIndex; - NetMsg_WeapPickup( pbuf, iSize, iIndex ); - - // Add the weapon to the history - gHR.AddToHistory( HISTSLOT_WEAP, iIndex ); - - return 1; -} - -int CHudAmmo::MsgFunc_ItemPickup( const char *pszName, int iSize, void *pbuf ) -{ - string szName; - NetMsg_ItemPickup( pbuf, iSize, szName ); - - // Add the weapon to the history - gHR.AddToHistory( HISTSLOT_ITEM, szName.c_str() ); - - return 1; -} - - -int CHudAmmo::MsgFunc_HideWeapon( const char *pszName, int iSize, void *pbuf ) -{ - NetMsg_HideWeapon( pbuf, iSize, gHUD.m_iHideHUDDisplay ); - - if (gEngfuncs.IsSpectateOnly()) - return 1; - if ( gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL ) ) - { - static wrect_t nullrc; - gpActiveSel = NULL; - gHUD.SetCurrentCrosshair( 0, nullrc, 0, 0, 0 ); - } - else - { - if ( m_pWeapon ) - gHUD.SetCurrentCrosshair( m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255 ); - } - - return 1; -} - -// -// CurWeapon: Update hud state with the current weapon and clip count. Ammo -// counts are updated with AmmoX. Server assures that the Weapon ammo type -// numbers match a real ammo type. -// -int CHudAmmo::MsgFunc_CurWeapon(const char *pszName, int iSize, void *pbuf ) -{ - static wrect_t nullrc; - - int iState, iId, iClip; - NetMsg_CurWeapon( pbuf, iSize, iState, iId, iClip ); - - if ( iId < 1 ) //signal kills crosshairs if this condition is met... - { - gHUD.SetCurrentCrosshair(0, nullrc, 0, 0, 0); - return 0; - } - - if ( g_iUser1 != OBS_IN_EYE ) - { - if ( iId == -1 && iClip == -1 ) //this conditional is never true due to iId < 1 check above! - { - gHUD.m_fPlayerDead = TRUE; - gpActiveSel = NULL; - return 1; - } - - gHUD.m_fPlayerDead = FALSE; - } - - WEAPON *pWeapon = gWR.GetWeapon( iId ); - if( pWeapon == NULL ) //don't have the weapon described in our resource list - { return 0; } - - bool bOnTarget = (iState & WEAPON_ON_TARGET) != 0; //used to track autoaim state - bool bIsCurrent = (iState & WEAPON_IS_CURRENT) != 0; - pWeapon->iEnabled = (iState & WEAPON_IS_ENABLED) != 0 ? TRUE : FALSE; - pWeapon->iClip = abs(iClip); - - if( !bIsCurrent ) - { return 1; } - - m_pWeapon = pWeapon; - - if ( !(gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL )) ) - { - if ( gHUD.m_iFOV >= 90 ) - { // normal crosshairs - if (bOnTarget && m_pWeapon->hAutoaim) - gHUD.SetCurrentCrosshair(m_pWeapon->hAutoaim, m_pWeapon->rcAutoaim, 255, 255, 255); - else - gHUD.SetCurrentCrosshair(m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255); - } - else - { // zoomed crosshairs - if (bOnTarget && m_pWeapon->hZoomedAutoaim) - gHUD.SetCurrentCrosshair(m_pWeapon->hZoomedAutoaim, m_pWeapon->rcZoomedAutoaim, 255, 255, 255); - else - gHUD.SetCurrentCrosshair(m_pWeapon->hZoomedCrosshair, m_pWeapon->rcZoomedCrosshair, 255, 255, 255); - } - } - - m_fFade = 200.0f; //!!! - m_iFlags |= HUD_ACTIVE; - - return 1; -} - -// -// WeaponList -- Tells the hud about a new weapon type. -// -int CHudAmmo::MsgFunc_WeaponList(const char *pszName, int iSize, void *pbuf ) -{ - WeaponList weapon_data; - NetMsg_WeaponList( pbuf, iSize, weapon_data ); - - WEAPON Weapon; - memset( &Weapon, 0, sizeof(WEAPON) ); - - strcpy( Weapon.szName, weapon_data.weapon_name.c_str() ); - Weapon.iAmmoType = weapon_data.ammo1_type; - Weapon.iMax1 = weapon_data.ammo1_max_amnt == 255 ? -1 : weapon_data.ammo1_max_amnt; - Weapon.iAmmo2Type = weapon_data.ammo2_type; - Weapon.iMax2 = weapon_data.ammo2_max_amnt == 255 ? -1 : weapon_data.ammo2_max_amnt; - Weapon.iSlot = weapon_data.bucket; - Weapon.iSlotPos = weapon_data.bucket_pos; - Weapon.iId = weapon_data.bit_index; - Weapon.iFlags = weapon_data.flags; - Weapon.iClip = 0; - // puzl: 497 - default value for enable state - Weapon.iEnabled = 0; - - gWR.AddWeapon( &Weapon ); - return 1; -} - -//------------------------------------------------------------------------ -// Command Handlers -//------------------------------------------------------------------------ -// Slot button pressed -void CHudAmmo::SlotInput( int iSlot ) -{ - // Let the Viewport use it first, for menus - if ( gViewPort && gViewPort->SlotInput( iSlot ) ) - return; - - gWR.SelectSlot( iSlot, FALSE, 1 ); -} - -void CHudAmmo::UserCmd_Slot1(void) -{ - SlotInput( 0 ); -} - -void CHudAmmo::UserCmd_Slot2(void) -{ - SlotInput( 1 ); -} - -void CHudAmmo::UserCmd_Slot3(void) -{ - SlotInput( 2 ); -} - -void CHudAmmo::UserCmd_Slot4(void) -{ - SlotInput( 3 ); -} - -void CHudAmmo::UserCmd_Slot5(void) -{ - SlotInput( 4 ); -} - -void CHudAmmo::UserCmd_Slot6(void) -{ - SlotInput( 5 ); -} - -void CHudAmmo::UserCmd_Slot7(void) -{ - SlotInput( 6 ); -} - -void CHudAmmo::UserCmd_Slot8(void) -{ - SlotInput( 7 ); -} - -void CHudAmmo::UserCmd_Slot9(void) -{ - SlotInput( 8 ); -} - -void CHudAmmo::UserCmd_Slot10(void) -{ - SlotInput( 9 ); -} - -void CHudAmmo::UserCmd_Close(void) -{ - if (gpActiveSel) - { - gpLastSel = gpActiveSel; - gpActiveSel = NULL; - - const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_OFF); - gHUD.PlayHUDSound(theSound, kHUDSoundVolume); - } -} - - -// Selects the next item in the weapon menu -void CHudAmmo::UserCmd_NextWeapon(void) -{ - if(gHUD.GetInTopDownMode()) - { - AvHScrollHandler::ScrollHeightUp(); - } - - if ( gHUD.m_fPlayerDead || (gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS | HIDEHUD_ALL)) ) - return; - - if ( !gpActiveSel || gpActiveSel == (WEAPON*)1 ) - gpActiveSel = m_pWeapon; - - int pos = 0; - int slot = 0; - if ( gpActiveSel ) - { - pos = gpActiveSel->iSlotPos + 1; - slot = gpActiveSel->iSlot; - } - - for ( int loop = 0; loop <= 1; loop++ ) - { - for ( ; slot < MAX_WEAPON_SLOTS; slot++ ) - { - for ( ; pos < MAX_WEAPON_POSITIONS; pos++ ) - { - WEAPON *wsp = gWR.GetWeaponSlot( slot, pos ); - - if (gWR.IsSelectable(wsp)) - { - gpActiveSel = wsp; - return; - } - } - - pos = 0; - } - - slot = 0; // start looking from the first slot again - } - - gpActiveSel = NULL; -} - -// Selects the previous item in the menu -void CHudAmmo::UserCmd_PrevWeapon(void) -{ - if(gHUD.GetInTopDownMode()) - { - AvHScrollHandler::ScrollHeightDown(); - } - - if ( gHUD.m_fPlayerDead || (gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS | HIDEHUD_ALL)) ) - return; - - if ( !gpActiveSel || gpActiveSel == (WEAPON*)1 ) - gpActiveSel = m_pWeapon; - - int pos = MAX_WEAPON_POSITIONS-1; - int slot = MAX_WEAPON_SLOTS-1; - if ( gpActiveSel ) - { - pos = gpActiveSel->iSlotPos - 1; - slot = gpActiveSel->iSlot; - } - - for ( int loop = 0; loop <= 1; loop++ ) - { - for ( ; slot >= 0; slot-- ) - { - for ( ; pos >= 0; pos-- ) - { - WEAPON *wsp = gWR.GetWeaponSlot( slot, pos ); - - if (gWR.IsSelectable(wsp)) - { - gpActiveSel = wsp; - return; - } - } - - pos = MAX_WEAPON_POSITIONS-1; - } - - slot = MAX_WEAPON_SLOTS-1; - } - - gpActiveSel = NULL; -} - -void CHudAmmo::SetCurrentClip(int inClip) -{ - if(this->m_pWeapon) - { - this->m_pWeapon->iClip = inClip; - } -} - -//------------------------------------------------------------------------- -// Drawing code -//------------------------------------------------------------------------- - -int CHudAmmo::Draw(float flTime) -{ - int a, x, y, r, g, b; - int AmmoWidth; - - if (!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) )) - return 1; - - if (/*!gHUD.GetIsAlive() ||*/ (gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL )) ) - return 1; - - // Draw Weapon Menu - DrawWList(flTime); - - // Draw ammo pickup history - gHR.DrawAmmoHistory( flTime ); - - if (!(m_iFlags & HUD_ACTIVE)) - return 0; - - if (!m_pWeapon) - return 0; - - WEAPON *pw = m_pWeapon; // shorthand - - // SPR_Draw Ammo - if ((pw->iAmmoType < 0) && (pw->iAmmo2Type < 0)) - return 0; - - - int iFlags = DHN_DRAWZERO; // draw 0 values - - AmmoWidth = gHUD.GetSpriteRect(gHUD.m_HUD_number_0).right - gHUD.GetSpriteRect(gHUD.m_HUD_number_0).left; - - a = (int) max( MIN_ALPHA, m_fFade ); - - if (m_fFade > 0) - m_fFade -= (gHUD.m_flTimeDelta * 20); - - gHUD.GetPrimaryHudColor(r, g, b); - - ScaleColors(r, g, b, a ); - - int theViewport[4]; - gHUD.GetViewport(theViewport); - - // Does this weapon have a clip? - y = theViewport[1] + theViewport[3] - gHUD.m_iFontHeight - gHUD.m_iFontHeight/2; - - // Does weapon have any ammo at all? - if (m_pWeapon->iAmmoType > 0) - { - int iIconWidth = m_pWeapon->rcAmmo.right - m_pWeapon->rcAmmo.left; - - if (pw->iClip >= 0) - { - // room for the number and the '|' and the current ammo - - x = theViewport[0] + theViewport[2] - (8 * AmmoWidth) - iIconWidth; - x = gHUD.DrawHudNumber(x, y, iFlags | DHN_3DIGITS, pw->iClip, r, g, b); - - wrect_t rc; - rc.top = 0; - rc.left = 0; - rc.right = AmmoWidth; - rc.bottom = 100; - - int iBarWidth = AmmoWidth/10; - - x += AmmoWidth/2; - - gHUD.GetPrimaryHudColor(r, g, b); - - // draw the | bar - FillRGBA(x, y, iBarWidth, gHUD.m_iFontHeight, r, g, b, a); - - x += iBarWidth + AmmoWidth/2;; - - // GL Seems to need this - ScaleColors(r, g, b, a ); - x = gHUD.DrawHudNumber(x, y, iFlags | DHN_3DIGITS, gWR.CountAmmo(pw->iAmmoType), r, g, b); - - - } - else - { - // SPR_Draw a bullets only line - x = theViewport[0] + theViewport[2] - 4 * AmmoWidth - iIconWidth; - x = gHUD.DrawHudNumber(x, y, iFlags | DHN_3DIGITS, gWR.CountAmmo(pw->iAmmoType), r, g, b); - } - - // Draw the ammo Icon - int iOffset = (m_pWeapon->rcAmmo.bottom - m_pWeapon->rcAmmo.top)/8; - SPR_Set(m_pWeapon->hAmmo, r, g, b); - SPR_DrawAdditive(0, x, y - iOffset, &m_pWeapon->rcAmmo); - } - - // Does weapon have seconday ammo? - if (pw->iAmmo2Type > 0) - { - int iIconWidth = m_pWeapon->rcAmmo2.right - m_pWeapon->rcAmmo2.left; - - // Do we have secondary ammo? - if ((pw->iAmmo2Type != 0) && (gWR.CountAmmo(pw->iAmmo2Type) > 0)) - { - y -= gHUD.m_iFontHeight + gHUD.m_iFontHeight/4; - x = theViewport[0] + theViewport[2] - 4 * AmmoWidth - iIconWidth; - x = gHUD.DrawHudNumber(x, y, iFlags|DHN_3DIGITS, gWR.CountAmmo(pw->iAmmo2Type), r, g, b); - - // Draw the ammo Icon - SPR_Set(m_pWeapon->hAmmo2, r, g, b); - int iOffset = (m_pWeapon->rcAmmo2.bottom - m_pWeapon->rcAmmo2.top)/8; - SPR_DrawAdditive(0, x, y - iOffset, &m_pWeapon->rcAmmo2); - } - } - return 1; -} - - -// -// Draws the ammo bar on the hud -// -int DrawBar(int x, int y, int width, int height, float f) -{ - int r, g, b; - - if (f < 0) - f = 0; - if (f > 1) - f = 1; - - if (f) - { - int w = f * width; - - // Always show at least one pixel if we have ammo. - if (w <= 0) - w = 1; - UnpackRGB(r, g, b, RGB_GREENISH); - FillRGBA(x, y, w, height, r, g, b, 255); - x += w; - width -= w; - } - - gHUD.GetPrimaryHudColor(r, g, b); - FillRGBA(x, y, width, height, r, g, b, 128); - - return (x + width); -} - - - -void DrawAmmoBar(WEAPON *p, int x, int y, int width, int height) -{ - if ( !p ) - return; - - if (p->iAmmoType != -1) - { - if (!gWR.CountAmmo(p->iAmmoType)) - return; - - float f = (float)gWR.CountAmmo(p->iAmmoType)/(float)p->iMax1; - - x = DrawBar(x, y, width, height, f); - - - // Do we have secondary ammo too? - - if (p->iAmmo2Type != -1) - { - f = (float)gWR.CountAmmo(p->iAmmo2Type)/(float)p->iMax2; - - x += 5; //!!! - - DrawBar(x, y, width, height, f); - } - } -} - - - - -// -// Draw Weapon Menu -// -int CHudAmmo::DrawWList(float flTime) -{ - int r,g,b,x,y,a,i; - - if ( !gpActiveSel ) - { - gHUD.SetSelectingWeaponID(-1); - return 0; - } - - int iActiveSlot; - - if ( gpActiveSel == (WEAPON *)1 ) - iActiveSlot = -1; // current slot has no weapons - else - iActiveSlot = gpActiveSel->iSlot; - - x = 10; //!!! - y = 10; //!!! - - - // Ensure that there are available choices in the active slot - if ( iActiveSlot > 0 ) - { - if ( !gWR.GetFirstPos( iActiveSlot ) ) - { - gpActiveSel = (WEAPON *)1; - iActiveSlot = -1; - } - } - - // Draw top line - for ( i = 0; i < MAX_WEAPON_SLOTS; i++ ) - { - int iWidth; - - gHUD.GetPrimaryHudColor(r, g, b); - - if ( iActiveSlot == i ) - a = 255; - else - a = 192; - - ScaleColors(r, g, b, 255); - SPR_Set(gHUD.GetSprite(m_HUD_bucket0 + i), r, g, b ); - - // make active slot wide enough to accomodate gun pictures - if ( i == iActiveSlot ) - { - WEAPON *p = gWR.GetFirstPos(iActiveSlot); - if ( p ) - iWidth = p->rcActive.right - p->rcActive.left; - else - iWidth = giBucketWidth; - } - else - iWidth = giBucketWidth; - - SPR_DrawAdditive(0, x, y, &gHUD.GetSpriteRect(m_HUD_bucket0 + i)); - - x += iWidth + 5; - } - - - a = 128; //!!! - x = 10; - - // Draw all of the buckets - for (i = 0; i < MAX_WEAPON_SLOTS; i++) - { - y = giBucketHeight + 10; - - // If this is the active slot, draw the bigger pictures, - // otherwise just draw boxes - if ( i == iActiveSlot ) - { - WEAPON *p = gWR.GetFirstPos( i ); - int iWidth = giBucketWidth; - if ( p ) - iWidth = p->rcActive.right - p->rcActive.left; - - for ( int iPos = 0; iPos < MAX_WEAPON_POSITIONS; iPos++ ) - { - p = gWR.GetWeaponSlot( i, iPos ); - - if ( !p || !p->iId ) - continue; - - // Preserve red/yellow depending on whether it has ammo or not - if(!gWR.IsEnabled(p)) - { - UnpackRGB(r,g,b, RGB_REDISH); - ScaleColors(r, g, b, 128); - } - else - { - gHUD.GetPrimaryHudColor(r, g, b); - ScaleColors(r, g, b, 192); - } - - if ( gpActiveSel == p ) - { - SPR_Set(p->hActive, r, g, b ); - SPR_DrawAdditive(0, x, y, &p->rcActive); - - SPR_Set(gHUD.GetSprite(m_HUD_selection), r, g, b ); - SPR_DrawAdditive(0, x, y, &gHUD.GetSpriteRect(m_HUD_selection)); - - // Lookup iID for helptext - gHUD.SetSelectingWeaponID(p->iId, r, g, b); - } - else - { - // Draw Weapon if Red if no ammo - - //if (gWR.IsSelectable(p)) - // ScaleColors(r, g, b, 192); - //else - //{ - // UnpackRGB(r,g,b, RGB_REDISH); - // ScaleColors(r, g, b, 128); - //} - - SPR_Set( p->hInactive, r, g, b ); - SPR_DrawAdditive( 0, x, y, &p->rcInactive ); - } - - // Draw Ammo Bar - - DrawAmmoBar(p, x + giABWidth/2, y, giABWidth, giABHeight); - - y += p->rcActive.bottom - p->rcActive.top + 5; - } - - x += iWidth + 5; - - } - else - { - // Draw Row of weapons. - gHUD.GetPrimaryHudColor(r, g, b); - - for ( int iPos = 0; iPos < MAX_WEAPON_POSITIONS; iPos++ ) - { - WEAPON *p = gWR.GetWeaponSlot( i, iPos ); - - if ( !p || !p->iId ) - continue; - - if ( gWR.IsEnabled(p) ) - { - gHUD.GetPrimaryHudColor(r, g, b); - a = 128; - } - else - { - UnpackRGB(r,g,b, RGB_REDISH); - a = 96; - } - - FillRGBA( x, y, giBucketWidth, giBucketHeight, r, g, b, a ); - - y += giBucketHeight + 5; - } - - x += giBucketWidth + 5; - } - } - - return 1; - -} - - -/* ================================= - GetSpriteList - -Finds and returns the matching -sprite name 'psz' and resolution 'iRes' -in the given sprite list 'pList' -iCount is the number of items in the pList -================================= */ -client_sprite_t *GetSpriteList(client_sprite_t *pList, const char *psz, int iRes, int iCount) -{ - if (!pList) - return NULL; - - int i = iCount; - client_sprite_t *p = pList; - - while(i--) - { - if ((!strcmp(psz, p->szName)) && (p->iRes == iRes)) - return p; - p++; - } - - return NULL; -} +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ +// +// Ammo.cpp +// +// implementation of CHudAmmo class +// + +#include "hud.h" +#include "cl_util.h" + +#include +#include + +#include "ammohistory.h" +#include "vgui_TeamFortressViewport.h" +#include "mod/AvHClientVariables.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHScrollHandler.h" +#include "mod/AvHNetworkMessages.h" + +WEAPON *gpActiveSel; // NULL means off, 1 means just the menu bar, otherwise + // this points to the active weapon menu item +WEAPON *gpLastSel; // Last weapon menu selection + +bool HUD_GetWeaponEnabled(int inID); +client_sprite_t *GetSpriteList(client_sprite_t *pList, const char *psz, int iRes, int iCount); + +WeaponsResource gWR; + +int g_weaponselect = 0; + +extern bool gCanMove; + +void IN_AttackDownForced(void); +void IN_AttackUpForced(void); +void IN_Attack2Down(void); +void IN_Attack2Up(void); +void IN_ReloadDown(); +void IN_ReloadUp(); +bool CheckInAttack(void); + +//Equivalent to DECLARE_COMMAND(lastinv,LastInv) except we use gWR instead of gHud +void __CmdFunc_LastInv(void) +{ gWR.UserCmd_LastInv(); } + +// +movement +void __CmdFunc_MovementOn(void) +{ gWR.UserCmd_MovementOn(); } +void __CmdFunc_MovementOff(void) +{ gWR.UserCmd_MovementOff(); } + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +WeaponsResource::WeaponsResource(void) : lastWeapon(NULL), iOldWeaponBits(0) {} +WeaponsResource::~WeaponsResource(void) {} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::Init( void ) +{ + memset( rgWeapons, 0, sizeof(WEAPON)*MAX_WEAPONS ); + Reset(); + HOOK_COMMAND("lastinv", LastInv); + // +movement + HOOK_COMMAND("+movement", MovementOn); + HOOK_COMMAND("-movement", MovementOff); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::Reset( void ) +{ + lastWeapon = NULL; + iOldWeaponBits = 0; + memset( rgSlots, 0, sizeof(WEAPON*)*MAX_WEAPON_SLOTS*MAX_WEAPON_POSITIONS ); + memset( riAmmo, 0, sizeof(int)*MAX_AMMO_TYPES ); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource :: LoadAllWeaponSprites( void ) +{ + int customCrosshairs=CVAR_GET_FLOAT(kvCustomCrosshair); + for (int i = 0; i < MAX_WEAPONS; i++) + { + if ( rgWeapons[i].iId ) + LoadWeaponSprites( &rgWeapons[i], customCrosshairs ); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +inline void LoadWeaponSprite( client_sprite_t* ptr, HSPRITE& sprite, wrect_t& bounds ) +{ + if( ptr ) + { + string name( "sprites/" ); + name.append( ptr->szSprite ); + name.append( ".spr" ); + sprite = Safe_SPR_Load(name.c_str()); + bounds = ptr->rc; + } + else + { + sprite = NULL; + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource :: LoadWeaponSprites( WEAPON *pWeapon, int custom ) +{ + if ( custom < 0 || custom > 4 ) + custom=1; + + int resolutions[6] = { 320, 640, 800, 1024, 1280, 1600}; + const int numRes=6; + int i=0, j=0, iRes=320; + int screenWidth=ScreenWidth(); + + for ( j=0; j < numRes; j++ ) { + if ( screenWidth == resolutions[j] ) { + iRes=resolutions[j]; + break; + } + if ( j > 0 && screenWidth > resolutions[j-1] && screenWidth < resolutions[j] ) { + iRes=resolutions[j-1]; + break; + } + } + + char sz[128]; + + if ( !pWeapon ) + return; + + memset( &pWeapon->rcCrosshair, 0, sizeof(wrect_t) ); + memset( &pWeapon->rcActive, 0, sizeof(wrect_t) ); + memset( &pWeapon->rcInactive, 0, sizeof(wrect_t) ); + memset( &pWeapon->rcAmmo, 0, sizeof(wrect_t) ); + memset( &pWeapon->rcAmmo2, 0, sizeof(wrect_t) ); + pWeapon->hCrosshair =0; + pWeapon->hInactive = 0; + pWeapon->hActive = 0; + pWeapon->hAmmo = 0; + pWeapon->hAmmo2 = 0; + + sprintf(sz, "sprites/%s.txt", pWeapon->szName); + client_sprite_t *pList = SPR_GetList(sz, &i); + + if (!pList) + { + ASSERT(pList); + return; + } + + char crosshairName[32]; + sprintf(crosshairName, "crosshair_%d", custom); + for ( j=numRes-1; j>=0; j-- ) { + if ( resolutions[j] <= iRes ) { + if( pWeapon->hCrosshair == NULL ) + LoadWeaponSprite( GetSpriteList( pList, crosshairName, resolutions[j], i ), pWeapon->hCrosshair, pWeapon->rcCrosshair ); + if( pWeapon->hCrosshair == NULL && custom != 0 ) + LoadWeaponSprite( GetSpriteList( pList, "crosshair_0", resolutions[j], i ), pWeapon->hCrosshair, pWeapon->rcCrosshair ); + + + if( pWeapon->hInactive == NULL ) + LoadWeaponSprite( GetSpriteList( pList, "weapon", resolutions[j], i ), pWeapon->hInactive, pWeapon->rcInactive ); + + if( pWeapon->hActive == NULL ) + LoadWeaponSprite( GetSpriteList( pList, "weapon_s", resolutions[j], i ), pWeapon->hActive, pWeapon->rcActive ); + + if( pWeapon->hAmmo == NULL ) + LoadWeaponSprite( GetSpriteList( pList, "ammo", resolutions[j], i ), pWeapon->hAmmo, pWeapon->rcAmmo ); + + if( pWeapon->hAmmo2 == NULL ) + LoadWeaponSprite( GetSpriteList( pList, "ammo2", resolutions[j], i ), pWeapon->hAmmo2, pWeapon->rcAmmo2 ); + } + } + + if( pWeapon->hActive || pWeapon->hInactive || pWeapon->hAmmo || pWeapon->hAmmo2 ) + { gHR.iHistoryGap = max( gHR.iHistoryGap, pWeapon->rcActive.bottom - pWeapon->rcActive.top ); } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +WEAPON* WeaponsResource::GetWeapon( int iId ) +{ + if( iId < 0 || iId >= MAX_WEAPONS ) { return NULL; } + return rgWeapons[iId].iId ? &rgWeapons[iId] : NULL; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +WEAPON* WeaponsResource::GetWeaponSlot( int slot, int pos ) { return rgSlots[slot][pos]; } + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +WEAPON* WeaponsResource::GetFirstPos( int iSlot ) +{ + WEAPON *returnVal = NULL; + ASSERT( iSlot < MAX_WEAPON_SLOTS && iSlot >= 0 ); + + for( int counter = 0; counter < MAX_WEAPON_POSITIONS; ++counter ) + { + if( this->IsSelectable(rgSlots[iSlot][counter]) ) + { + returnVal = rgSlots[iSlot][counter]; + break; + } + } + + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +WEAPON* WeaponsResource::GetNextActivePos( int iSlot, int iSlotPos ) +{ + WEAPON* returnVal = NULL; + ASSERT( iSlot < MAX_WEAPON_SLOTS && iSlot >= 0 ); + ASSERT( iSlotPos >= 0 ); + + for( int counter = iSlotPos+1; counter < MAX_WEAPON_POSITIONS; ++counter ) + { + if( this->IsSelectable(rgSlots[iSlot][counter]) ) + { + returnVal = rgSlots[iSlot][counter]; + break; + } + } + + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool WeaponsResource::IsEnabled(WEAPON* p) +{ + if( p == NULL ) { return false; } + return HUD_GetWeaponEnabled(p->iId) && this->HasAmmo(p); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool WeaponsResource::IsSelectable(WEAPON* p) +{ + if( p == NULL ) { return false; } + return HUD_GetWeaponEnabled(p->iId); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool WeaponsResource::HasAmmo( WEAPON* p ) +{ + if( p == NULL) { return false; } + //note : if max ammo capacity is -1, this has always returned true in spite of not + // having actual ammo -- KGP + return (p->iAmmoType == -1) || (p->iMax1 == -1) || p->iClip > 0 || CountAmmo(p->iAmmoType) + || CountAmmo(p->iAmmo2Type) || (p->iFlags & WEAPON_FLAGS_SELECTIONEMPTY ); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +int WeaponsResource::CountAmmo( int iId ) +{ + ASSERT( iId < MAX_AMMO_TYPES ); + if( iId < 0 ) return 0; + return riAmmo[iId]; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +int WeaponsResource::GetAmmo( int iId ) +{ + ASSERT( iId < MAX_AMMO_TYPES && iId > -1 ); + return riAmmo[iId]; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::SetAmmo( int iId, int iCount ) +{ + ASSERT( iId < MAX_AMMO_TYPES && iId > -1 ); + riAmmo[iId] = iCount; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +HSPRITE* WeaponsResource::GetAmmoPicFromWeapon( int iAmmoId, wrect_t& rect ) +{ + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + if ( rgWeapons[i].iAmmoType == iAmmoId ) + { + rect = rgWeapons[i].rcAmmo; + return &rgWeapons[i].hAmmo; + } + else if ( rgWeapons[i].iAmmo2Type == iAmmoId ) + { + rect = rgWeapons[i].rcAmmo2; + return &rgWeapons[i].hAmmo2; + } + } + + return NULL; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::AddWeapon( WEAPON *wp ) +{ + int customCrosshairs=CVAR_GET_FLOAT(kvCustomCrosshair); + rgWeapons[ wp->iId ] = *wp; + LoadWeaponSprites( &rgWeapons[ wp->iId ], customCrosshairs); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::PickupWeapon( WEAPON *wp ) +{ + rgSlots[ wp->iSlot ][ wp->iSlotPos ] = wp; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::DropWeapon( WEAPON *wp ) +{ + rgSlots[ wp->iSlot ][ wp->iSlotPos ] = NULL; + if(lastWeapon == wp) //dropped last weapon, remove it from the list + { lastWeapon = NULL; } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::DropAllWeapons( void ) +{ + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + if ( rgWeapons[i].iId ) + DropWeapon( &rgWeapons[i] ); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::UserCmd_LastInv(void) +{ + if(this->IsSelectable(this->lastWeapon)) + { + this->SetCurrentWeapon(lastWeapon); + // : 764 + //const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_ON); + //gHUD.PlayHUDSound(theSound, kHUDSoundVolume); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::UserCmd_MovementOn() +{ + // Find out which weapon we want to trigger + AvHUser3 theUser3 = gHUD.GetHUDUser3(); + int wID = -1; + switch(theUser3) + { + case AVH_USER3_ALIEN_PLAYER1: + wID = AVH_ABILITY_LEAP; + break; + case AVH_USER3_ALIEN_PLAYER3: + // TODO: Add flap + break; + case AVH_USER3_ALIEN_PLAYER4: + wID = AVH_WEAPON_BLINK; + break; + case AVH_USER3_ALIEN_PLAYER5: + wID = AVH_ABILITY_CHARGE; + break; + default: + IN_ReloadDown(); + return; + } + + if (wID > -1) + { + // Fetch the needed movement weapon + WEAPON *p = this->GetWeapon(wID); + if (p != NULL && this->IsSelectable(p)) + { + // Send activation of ability asap + IN_Attack2Down(); + } + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::UserCmd_MovementOff() +{ + // Ensure that we're not activating any weapons when selected + IN_Attack2Up(); + IN_ReloadUp(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::SetValidWeapon(void) +{ + WEAPON* p = this->GetFirstPos(0); //alien attack 1 or primary marine weapon + if(gHUD.GetIsAlien()) + { + this->SetCurrentWeapon(p); + } + else + { + if(this->IsSelectable(p) && this->HasAmmo(p)) + { + this->SetCurrentWeapon(p); + } + else + { + p = this->GetFirstPos(1); //pistol slot + if(this->IsSelectable(p) && this->HasAmmo(p)) + { + this->SetCurrentWeapon(p); + } + else + { + p = this->GetFirstPos(2); //knife slot + this->SetCurrentWeapon(p); + } + } + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource::SetCurrentWeapon(WEAPON* newWeapon) +{ + WEAPON* currentWeapon = this->GetWeapon(gHUD.GetCurrentWeaponID()); + // : 497 - Because weapon state can get out of sync, we should allow this even if the weapons are the same + // && newWeapon != currentWeapon + if( newWeapon != NULL ) + { + if( newWeapon != currentWeapon ) + { lastWeapon = currentWeapon; } + ServerCmd(newWeapon->szName); + g_weaponselect = newWeapon->iId; + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void WeaponsResource :: SelectSlot( int iSlot, int fAdvance, int iDirection ) +{ + if ( gHUD.m_Menu.m_fMenuDisplayed && (fAdvance == FALSE) && (iDirection == 1) ) + { // menu is overriding slot use commands + gHUD.m_Menu.SelectMenuItem( iSlot + 1 ); // slots are one off the key numbers + return; + } + + if ( iSlot >= MAX_WEAPON_SLOTS ) + return; + + if ( gHUD.m_fPlayerDead || gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL ) ) + return; + + if (!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) )) //require suit + return; + + if ( ! ( gHUD.m_iWeaponBits & ~(1<<(WEAPON_SUIT)) )) //require something besides suit + return; + + WEAPON *p = NULL; + bool fastSwitch = CVAR_GET_FLOAT( "hud_fastswitch" ) != 0; + if ((gpActiveSel == NULL) || (gpActiveSel == (WEAPON *)1) || (iSlot != gpActiveSel->iSlot)) + { + p = GetFirstPos(iSlot); + const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_ON); + gHUD.PlayHUDSound(theSound, kHUDSoundVolume); + + if (this->IsSelectable(p) && fastSwitch) //check to see if we can use fastSwitch + { + WEAPON *p2 = GetNextActivePos( p->iSlot, p->iSlotPos ); + if (!this->IsSelectable(p2)) //only one target in the bucket + { + this->SetCurrentWeapon(p); + return; + } + } + } + else + { + const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_MOVE_SELECT); + gHUD.PlayHUDSound(theSound, kHUDSoundVolume); + + if ( gpActiveSel ) + p = GetNextActivePos( gpActiveSel->iSlot, gpActiveSel->iSlotPos ); + if ( !p ) + p = GetFirstPos( iSlot ); + } + + if (!this->IsSelectable(p)) // no valid selection found + { + // if fastSwitch is on, ignore, else turn on the menu + if ( !fastSwitch ) { + gpActiveSel = (WEAPON *)1; + } + else { + gpActiveSel = NULL; + } + } + else + { + gpActiveSel = p; + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +int giBucketHeight, giBucketWidth, giABHeight, giABWidth; // Ammo Bar width and height + +HSPRITE ghsprBuckets; // Sprite for top row of weapons menu + +DECLARE_MESSAGE(m_Ammo, CurWeapon ); // Current weapon and clip +DECLARE_MESSAGE(m_Ammo, WeaponList); // new weapon type +DECLARE_MESSAGE(m_Ammo, AmmoX); // update known ammo type's count +DECLARE_MESSAGE(m_Ammo, AmmoPickup); // flashes an ammo pickup record +DECLARE_MESSAGE(m_Ammo, WeapPickup); // flashes a weapon pickup record +DECLARE_MESSAGE(m_Ammo, HideWeapon); // hides the weapon, ammo, and crosshair displays temporarily +DECLARE_MESSAGE(m_Ammo, ItemPickup); + +DECLARE_COMMAND(m_Ammo, Slot1); +DECLARE_COMMAND(m_Ammo, Slot2); +DECLARE_COMMAND(m_Ammo, Slot3); +DECLARE_COMMAND(m_Ammo, Slot4); +DECLARE_COMMAND(m_Ammo, Slot5); +DECLARE_COMMAND(m_Ammo, Slot6); +DECLARE_COMMAND(m_Ammo, Slot7); +DECLARE_COMMAND(m_Ammo, Slot8); +DECLARE_COMMAND(m_Ammo, Slot9); +DECLARE_COMMAND(m_Ammo, Slot10); +DECLARE_COMMAND(m_Ammo, Close); +DECLARE_COMMAND(m_Ammo, NextWeapon); +DECLARE_COMMAND(m_Ammo, PrevWeapon); + +// width of ammo fonts +#define AMMO_SMALL_WIDTH 10 +#define AMMO_LARGE_WIDTH 20 + +#define HISTORY_DRAW_TIME "5" + +int CHudAmmo::Init(void) +{ + gHUD.AddHudElem(this); + + HOOK_MESSAGE(CurWeapon); + HOOK_MESSAGE(WeaponList); + HOOK_MESSAGE(AmmoPickup); + HOOK_MESSAGE(WeapPickup); + HOOK_MESSAGE(ItemPickup); + HOOK_MESSAGE(HideWeapon); + HOOK_MESSAGE(AmmoX); + + HOOK_COMMAND("slot1", Slot1); + HOOK_COMMAND("slot2", Slot2); + HOOK_COMMAND("slot3", Slot3); + HOOK_COMMAND("slot4", Slot4); + HOOK_COMMAND("slot5", Slot5); + HOOK_COMMAND("slot6", Slot6); + HOOK_COMMAND("slot7", Slot7); + HOOK_COMMAND("slot8", Slot8); + HOOK_COMMAND("slot9", Slot9); + HOOK_COMMAND("slot10", Slot10); + HOOK_COMMAND("cancelselect", Close); + HOOK_COMMAND("invnext", NextWeapon); + HOOK_COMMAND("invprev", PrevWeapon); + + Reset(); + + CVAR_CREATE( "hud_drawhistory_time", HISTORY_DRAW_TIME, 0 ); + CVAR_CREATE( "hud_fastswitch", "0", FCVAR_ARCHIVE ); // controls whether or not weapons can be selected in one keypress + + m_iFlags |= HUD_ACTIVE; //!!! + + gWR.Init(); + gHR.Init(); + + return 1; +}; + +void CHudAmmo::Reset(void) +{ + m_fFade = 0; + m_iFlags |= HUD_ACTIVE; //!!! + + gpActiveSel = NULL; + gHUD.m_iHideHUDDisplay = 0; + + gWR.Reset(); + gHR.Reset(); + + m_customCrosshair=0; + // VidInit(); + +} + +int CHudAmmo::VidInit(void) +{ + // Load sprites for buckets (top row of weapon menu) + m_HUD_bucket0 = gHUD.GetSpriteIndex( "bucket1" ); + m_HUD_selection = gHUD.GetSpriteIndex( "selection" ); + + ghsprBuckets = gHUD.GetSprite(m_HUD_bucket0); + giBucketWidth = gHUD.GetSpriteRect(m_HUD_bucket0).right - gHUD.GetSpriteRect(m_HUD_bucket0).left; + giBucketHeight = gHUD.GetSpriteRect(m_HUD_bucket0).bottom - gHUD.GetSpriteRect(m_HUD_bucket0).top; + + gHR.iHistoryGap = max( gHR.iHistoryGap, gHUD.GetSpriteRect(m_HUD_bucket0).bottom - gHUD.GetSpriteRect(m_HUD_bucket0).top); + + // If we've already loaded weapons, let's get new sprites + gWR.LoadAllWeaponSprites(); + + if (ScreenWidth() >= 640) + { + giABWidth = 20; + giABHeight = 4; + } + else + { + giABWidth = 10; + giABHeight = 2; + } + + return 1; +} + +// +// Think: +// Used for selection of weapon menu item. +// + +void CHudAmmo::Think(void) +{ + if ( gHUD.m_fPlayerDead ) + return; + + if ( gHUD.m_iWeaponBits != gWR.iOldWeaponBits ) + { + gWR.iOldWeaponBits = gHUD.m_iWeaponBits; + bool droppedCurrent = false; + + for (int i = MAX_WEAPONS-1; i > 0; i-- ) + { + WEAPON *p = gWR.GetWeapon(i); + + if ( p ) + { + if ( gHUD.m_iWeaponBits & ( 1 << p->iId ) ) + gWR.PickupWeapon( p ); + else + gWR.DropWeapon( p ); + } + } + } + if ( (int)CVAR_GET_FLOAT(kvCustomCrosshair) != m_customCrosshair ) { + m_customCrosshair=(int)CVAR_GET_FLOAT(kvCustomCrosshair); + for ( int i=0; i < MAX_WEAPONS; i++ ) { + WEAPON *weapon = gWR.GetWeapon(i); + if ( weapon ) { + gWR.LoadWeaponSprites(weapon, m_customCrosshair); + if ( gHUD.GetHUDPlayMode() != PLAYMODE_READYROOM && gHUD.GetCurrentWeaponID() == weapon->iId ) { + gHUD.SetCurrentCrosshair(weapon->hCrosshair, weapon->rcCrosshair, 255, 255, 255); + } + } + } + + } + + if(gHUD.GetIsAlien()) //check for hive death causing loss of current weapon + { + WEAPON* currentWeapon = gWR.GetWeapon(gHUD.GetCurrentWeaponID()); + if(!gWR.IsSelectable(currentWeapon)) //current weapon isn't valid + { + gWR.SetValidWeapon(); //get best option + } + } + + if (!gpActiveSel) + return; + + // has the player selected one? + if (gHUD.m_iKeyBits & IN_ATTACK) + { + if (gpActiveSel != (WEAPON *)1) + { + gWR.SetCurrentWeapon(gpActiveSel); + } + + gpLastSel = gpActiveSel; + gpActiveSel = NULL; + gHUD.m_iKeyBits &= ~IN_ATTACK; + + const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_SELECT); + gHUD.PlayHUDSound(theSound, kHUDSoundVolume); + } +} + + +//------------------------------------------------------------------------ +// Message Handlers +//------------------------------------------------------------------------ + +// +// AmmoX -- Update the count of a known type of ammo +// +int CHudAmmo::MsgFunc_AmmoX(const char *pszName, int iSize, void *pbuf) +{ + int iIndex, iCount; + NetMsg_AmmoX( pbuf, iSize, iIndex, iCount ); + gWR.SetAmmo( iIndex, abs(iCount) ); + + return 1; +} + +int CHudAmmo::MsgFunc_AmmoPickup( const char *pszName, int iSize, void *pbuf ) +{ + int iIndex, iCount; + NetMsg_AmmoPickup( pbuf, iSize, iIndex, iCount ); + + // Add ammo to the history + gHR.AddToHistory( HISTSLOT_AMMO, iIndex, abs(iCount) ); + + return 1; +} + +int CHudAmmo::MsgFunc_WeapPickup( const char *pszName, int iSize, void *pbuf ) +{ + int iIndex; + NetMsg_WeapPickup( pbuf, iSize, iIndex ); + + // Add the weapon to the history + gHR.AddToHistory( HISTSLOT_WEAP, iIndex ); + + return 1; +} + +int CHudAmmo::MsgFunc_ItemPickup( const char *pszName, int iSize, void *pbuf ) +{ + string szName; + NetMsg_ItemPickup( pbuf, iSize, szName ); + + // Add the weapon to the history + gHR.AddToHistory( HISTSLOT_ITEM, szName.c_str() ); + + return 1; +} + + +int CHudAmmo::MsgFunc_HideWeapon( const char *pszName, int iSize, void *pbuf ) +{ + NetMsg_HideWeapon( pbuf, iSize, gHUD.m_iHideHUDDisplay ); + + if (gEngfuncs.IsSpectateOnly()) + return 1; + if ( gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL ) ) + { + static wrect_t nullrc; + gpActiveSel = NULL; + gHUD.SetCurrentCrosshair( 0, nullrc, 0, 0, 0 ); + } + else + { + if ( m_pWeapon ) + gHUD.SetCurrentCrosshair( m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255 ); + } + + return 1; +} + +// +// CurWeapon: Update hud state with the current weapon and clip count. Ammo +// counts are updated with AmmoX. Server assures that the Weapon ammo type +// numbers match a real ammo type. +// +int CHudAmmo::MsgFunc_CurWeapon(const char *pszName, int iSize, void *pbuf ) +{ + static wrect_t nullrc; + + int iState, iId, iClip; + NetMsg_CurWeapon( pbuf, iSize, iState, iId, iClip ); + + if ( iId < 1 ) //signal kills crosshairs if this condition is met... + { + gHUD.SetCurrentCrosshair(0, nullrc, 0, 0, 0); + return 0; + } + + if ( g_iUser1 != OBS_IN_EYE ) + { + if ( iId == -1 && iClip == -1 ) //this conditional is never true due to iId < 1 check above! + { + gHUD.m_fPlayerDead = TRUE; + gpActiveSel = NULL; + return 1; + } + + gHUD.m_fPlayerDead = FALSE; + } + + WEAPON *pWeapon = gWR.GetWeapon( iId ); + if( pWeapon == NULL ) //don't have the weapon described in our resource list + { return 0; } + + bool bOnTarget = (iState & WEAPON_ON_TARGET) != 0; //used to track autoaim state + bool bIsCurrent = (iState & WEAPON_IS_CURRENT) != 0; + pWeapon->iEnabled = (iState & WEAPON_IS_ENABLED) != 0 ? TRUE : FALSE; + pWeapon->iClip = abs(iClip); + + // Ensure that movement is enabled/disabled according to weapons + if (iId == 22 || iId == 11 || iId == 21) + { + gCanMove = pWeapon->iEnabled; + } + + if( !bIsCurrent ) + { return 1; } + + m_pWeapon = pWeapon; + + if ( !(gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL )) ) + { + gHUD.SetCurrentCrosshair(m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255); + +/* if ( gHUD.m_iFOV >= 90 ) + { // normal crosshairs + if (bOnTarget && m_pWeapon->hAutoaim) + gHUD.SetCurrentCrosshair(m_pWeapon->hAutoaim, m_pWeapon->rcAutoaim, 255, 255, 255); + else + gHUD.SetCurrentCrosshair(m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255); + } + else + { // zoomed crosshairs + if (bOnTarget && m_pWeapon->hZoomedAutoaim) + gHUD.SetCurrentCrosshair(m_pWeapon->hZoomedAutoaim, m_pWeapon->rcZoomedAutoaim, 255, 255, 255); + else + gHUD.SetCurrentCrosshair(m_pWeapon->hZoomedCrosshair, m_pWeapon->rcZoomedCrosshair, 255, 255, 255); + }*/ + } + + m_fFade = 200.0f; //!!! + m_iFlags |= HUD_ACTIVE; + + return 1; +} + +// +// WeaponList -- Tells the hud about a new weapon type. +// +int CHudAmmo::MsgFunc_WeaponList(const char *pszName, int iSize, void *pbuf ) +{ + WeaponList weapon_data; + NetMsg_WeaponList( pbuf, iSize, weapon_data ); + + WEAPON Weapon; + memset( &Weapon, 0, sizeof(WEAPON) ); + + strcpy( Weapon.szName, weapon_data.weapon_name.c_str() ); + Weapon.iAmmoType = weapon_data.ammo1_type; + Weapon.iMax1 = weapon_data.ammo1_max_amnt == 255 ? -1 : weapon_data.ammo1_max_amnt; + Weapon.iAmmo2Type = weapon_data.ammo2_type; + Weapon.iMax2 = weapon_data.ammo2_max_amnt == 255 ? -1 : weapon_data.ammo2_max_amnt; + Weapon.iSlot = weapon_data.bucket; + Weapon.iSlotPos = weapon_data.bucket_pos; + Weapon.iId = weapon_data.bit_index; + Weapon.iFlags = weapon_data.flags; + Weapon.iClip = 0; + // : 497 - default value for enable state + Weapon.iEnabled = 0; + + gWR.AddWeapon( &Weapon ); + return 1; +} + +//------------------------------------------------------------------------ +// Command Handlers +//------------------------------------------------------------------------ +// Slot button pressed +void CHudAmmo::SlotInput( int iSlot ) +{ + // Let the Viewport use it first, for menus + if ( gViewPort && gViewPort->SlotInput( iSlot ) ) + return; + + gWR.SelectSlot( iSlot, FALSE, 1 ); +} + +void CHudAmmo::UserCmd_Slot1(void) +{ + SlotInput( 0 ); +} + +void CHudAmmo::UserCmd_Slot2(void) +{ + SlotInput( 1 ); +} + +void CHudAmmo::UserCmd_Slot3(void) +{ + SlotInput( 2 ); +} + +void CHudAmmo::UserCmd_Slot4(void) +{ + SlotInput( 3 ); +} + +void CHudAmmo::UserCmd_Slot5(void) +{ + SlotInput( 4 ); +} + +void CHudAmmo::UserCmd_Slot6(void) +{ + SlotInput( 5 ); +} + +void CHudAmmo::UserCmd_Slot7(void) +{ + SlotInput( 6 ); +} + +void CHudAmmo::UserCmd_Slot8(void) +{ + SlotInput( 7 ); +} + +void CHudAmmo::UserCmd_Slot9(void) +{ + SlotInput( 8 ); +} + +void CHudAmmo::UserCmd_Slot10(void) +{ + SlotInput( 9 ); +} + +void CHudAmmo::UserCmd_Close(void) +{ + if (gpActiveSel) + { + gpLastSel = gpActiveSel; + gpActiveSel = NULL; + + const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_OFF); + gHUD.PlayHUDSound(theSound, kHUDSoundVolume); + } +} + + +// Selects the next item in the weapon menu +void CHudAmmo::UserCmd_NextWeapon(void) +{ + if(gHUD.GetInTopDownMode()) + { + AvHScrollHandler::ScrollHeightUp(); + } + + if ( gHUD.m_fPlayerDead || (gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS | HIDEHUD_ALL)) ) + return; + + if ( !gpActiveSel || gpActiveSel == (WEAPON*)1 ) + gpActiveSel = m_pWeapon; + + int pos = 0; + int slot = 0; + if ( gpActiveSel ) + { + pos = gpActiveSel->iSlotPos + 1; + slot = gpActiveSel->iSlot; + } + + for ( int loop = 0; loop <= 1; loop++ ) + { + for ( ; slot < MAX_WEAPON_SLOTS; slot++ ) + { + for ( ; pos < MAX_WEAPON_POSITIONS; pos++ ) + { + WEAPON *wsp = gWR.GetWeaponSlot( slot, pos ); + + if (gWR.IsSelectable(wsp)) + { + gpActiveSel = wsp; + return; + } + } + + pos = 0; + } + + slot = 0; // start looking from the first slot again + } + + gpActiveSel = NULL; +} + +// Selects the previous item in the menu +void CHudAmmo::UserCmd_PrevWeapon(void) +{ + if(gHUD.GetInTopDownMode()) + { + AvHScrollHandler::ScrollHeightDown(); + } + + if ( gHUD.m_fPlayerDead || (gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS | HIDEHUD_ALL)) ) + return; + + if ( !gpActiveSel || gpActiveSel == (WEAPON*)1 ) + gpActiveSel = m_pWeapon; + + int pos = MAX_WEAPON_POSITIONS-1; + int slot = MAX_WEAPON_SLOTS-1; + if ( gpActiveSel ) + { + pos = gpActiveSel->iSlotPos - 1; + slot = gpActiveSel->iSlot; + } + + for ( int loop = 0; loop <= 1; loop++ ) + { + for ( ; slot >= 0; slot-- ) + { + for ( ; pos >= 0; pos-- ) + { + WEAPON *wsp = gWR.GetWeaponSlot( slot, pos ); + + if (gWR.IsSelectable(wsp)) + { + gpActiveSel = wsp; + return; + } + } + + pos = MAX_WEAPON_POSITIONS-1; + } + + slot = MAX_WEAPON_SLOTS-1; + } + + gpActiveSel = NULL; +} + +void CHudAmmo::SetCurrentClip(int inClip) +{ + if(this->m_pWeapon) + { + this->m_pWeapon->iClip = inClip; + } +} + +//------------------------------------------------------------------------- +// Drawing code +//------------------------------------------------------------------------- + +int CHudAmmo::Draw(float flTime) +{ + int a, x, y, r, g, b; + int AmmoWidth; + + if (!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) )) + return 1; + + if (/*!gHUD.GetIsAlive() ||*/ (gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL )) ) + return 1; + + // Draw Weapon Menu + DrawWList(flTime); + + // Draw ammo pickup history + gHR.DrawAmmoHistory( flTime ); + + if (!(m_iFlags & HUD_ACTIVE)) + return 0; + + if (!m_pWeapon) + return 0; + + WEAPON *pw = m_pWeapon; // shorthand + + // SPR_Draw Ammo + if ((pw->iAmmoType < 0) && (pw->iAmmo2Type < 0)) + return 0; + + + int iFlags = DHN_DRAWZERO; // draw 0 values + + AmmoWidth = gHUD.GetSpriteRect(gHUD.m_HUD_number_0).right - gHUD.GetSpriteRect(gHUD.m_HUD_number_0).left; + + a = (int) max( MIN_ALPHA, m_fFade ); + + if (m_fFade > 0) + m_fFade -= (gHUD.m_flTimeDelta * 20); + + gHUD.GetPrimaryHudColor(r, g, b); + + ScaleColors(r, g, b, a ); + + int theViewport[4]; + gHUD.GetViewport(theViewport); + + // Does this weapon have a clip? + y = theViewport[1] + theViewport[3] - gHUD.m_iFontHeight - gHUD.m_iFontHeight/2; + + // Does weapon have any ammo at all? + if (m_pWeapon->iAmmoType > 0) + { + int iIconWidth = m_pWeapon->rcAmmo.right - m_pWeapon->rcAmmo.left; + + if (pw->iClip >= 0) + { + // room for the number and the '|' and the current ammo + + x = theViewport[0] + theViewport[2] - (8 * AmmoWidth) - iIconWidth; + x = gHUD.DrawHudNumber(x, y, iFlags | DHN_3DIGITS, pw->iClip, r, g, b); + + wrect_t rc; + rc.top = 0; + rc.left = 0; + rc.right = AmmoWidth; + rc.bottom = 100; + + int iBarWidth = AmmoWidth/10; + + x += AmmoWidth/2; + + gHUD.GetPrimaryHudColor(r, g, b); + + // draw the | bar + FillRGBA(x, y, iBarWidth, gHUD.m_iFontHeight, r, g, b, a); + + x += iBarWidth + AmmoWidth/2;; + + // GL Seems to need this + ScaleColors(r, g, b, a ); + x = gHUD.DrawHudNumber(x, y, iFlags | DHN_3DIGITS, gWR.CountAmmo(pw->iAmmoType), r, g, b); + + + } + else + { + // SPR_Draw a bullets only line + x = theViewport[0] + theViewport[2] - 4 * AmmoWidth - iIconWidth; + x = gHUD.DrawHudNumber(x, y, iFlags | DHN_3DIGITS, gWR.CountAmmo(pw->iAmmoType), r, g, b); + } + + // Draw the ammo Icon + int iOffset = (m_pWeapon->rcAmmo.bottom - m_pWeapon->rcAmmo.top)/8; + SPR_Set(m_pWeapon->hAmmo, r, g, b); + SPR_DrawAdditive(0, x, y - iOffset, &m_pWeapon->rcAmmo); + } + + // Does weapon have seconday ammo? + if (pw->iAmmo2Type > 0) + { + int iIconWidth = m_pWeapon->rcAmmo2.right - m_pWeapon->rcAmmo2.left; + + // Do we have secondary ammo? + if ((pw->iAmmo2Type != 0) && (gWR.CountAmmo(pw->iAmmo2Type) > 0)) + { + y -= gHUD.m_iFontHeight + gHUD.m_iFontHeight/4; + x = theViewport[0] + theViewport[2] - 4 * AmmoWidth - iIconWidth; + x = gHUD.DrawHudNumber(x, y, iFlags|DHN_3DIGITS, gWR.CountAmmo(pw->iAmmo2Type), r, g, b); + + // Draw the ammo Icon + SPR_Set(m_pWeapon->hAmmo2, r, g, b); + int iOffset = (m_pWeapon->rcAmmo2.bottom - m_pWeapon->rcAmmo2.top)/8; + SPR_DrawAdditive(0, x, y - iOffset, &m_pWeapon->rcAmmo2); + } + } + return 1; +} + + +// +// Draws the ammo bar on the hud +// +int DrawBar(int x, int y, int width, int height, float f) +{ + int r, g, b; + + if (f < 0) + f = 0; + if (f > 1) + f = 1; + + if (f) + { + int w = f * width; + + // Always show at least one pixel if we have ammo. + if (w <= 0) + w = 1; + UnpackRGB(r, g, b, RGB_GREENISH); + FillRGBA(x, y, w, height, r, g, b, 255); + x += w; + width -= w; + } + + gHUD.GetPrimaryHudColor(r, g, b); + FillRGBA(x, y, width, height, r, g, b, 128); + + return (x + width); +} + + + +void DrawAmmoBar(WEAPON *p, int x, int y, int width, int height) +{ + if ( !p ) + return; + + if (p->iAmmoType != -1) + { + if (!gWR.CountAmmo(p->iAmmoType)) + return; + + float f = (float)gWR.CountAmmo(p->iAmmoType)/(float)p->iMax1; + + x = DrawBar(x, y, width, height, f); + + + // Do we have secondary ammo too? + + if (p->iAmmo2Type != -1) + { + f = (float)gWR.CountAmmo(p->iAmmo2Type)/(float)p->iMax2; + + x += 5; //!!! + + DrawBar(x, y, width, height, f); + } + } +} + + + + +// +// Draw Weapon Menu +// +int CHudAmmo::DrawWList(float flTime) +{ + int r,g,b,x,y,a,i; + + if ( !gpActiveSel ) + { + gHUD.SetSelectingWeaponID(-1); + return 0; + } + + int iActiveSlot; + + if ( gpActiveSel == (WEAPON *)1 ) + iActiveSlot = -1; // current slot has no weapons + else + iActiveSlot = gpActiveSel->iSlot; + + x = 10; //!!! + y = 10; //!!! + + + // Ensure that there are available choices in the active slot + if ( iActiveSlot > 0 ) + { + if ( !gWR.GetFirstPos( iActiveSlot ) ) + { + gpActiveSel = (WEAPON *)1; + iActiveSlot = -1; + } + } + + // Draw top line + for ( i = 0; i < MAX_WEAPON_SLOTS; i++ ) + { + int iWidth; + + gHUD.GetPrimaryHudColor(r, g, b); + + if ( iActiveSlot == i ) + a = 255; + else + a = 192; + + ScaleColors(r, g, b, 255); + SPR_Set(gHUD.GetSprite(m_HUD_bucket0 + i), r, g, b ); + + // make active slot wide enough to accomodate gun pictures + if ( i == iActiveSlot ) + { + WEAPON *p = gWR.GetFirstPos(iActiveSlot); + if ( p ) + iWidth = p->rcActive.right - p->rcActive.left; + else + iWidth = giBucketWidth; + } + else + iWidth = giBucketWidth; + + SPR_DrawAdditive(0, x, y, &gHUD.GetSpriteRect(m_HUD_bucket0 + i)); + + x += iWidth + 5; + } + + + a = 128; //!!! + x = 10; + + // Draw all of the buckets + for (i = 0; i < MAX_WEAPON_SLOTS; i++) + { + y = giBucketHeight + 10; + + // If this is the active slot, draw the bigger pictures, + // otherwise just draw boxes + if ( i == iActiveSlot ) + { + WEAPON *p = gWR.GetFirstPos( i ); + int iWidth = giBucketWidth; + if ( p ) + iWidth = p->rcActive.right - p->rcActive.left; + + for ( int iPos = 0; iPos < MAX_WEAPON_POSITIONS; iPos++ ) + { + p = gWR.GetWeaponSlot( i, iPos ); + + if ( !p || !p->iId ) + continue; + + // Preserve red/yellow depending on whether it has ammo or not + if(!gWR.IsEnabled(p)) + { + UnpackRGB(r,g,b, RGB_REDISH); + ScaleColors(r, g, b, 128); + } + else + { + gHUD.GetPrimaryHudColor(r, g, b); + ScaleColors(r, g, b, 192); + } + + if ( gpActiveSel == p ) + { + SPR_Set(p->hActive, r, g, b ); + SPR_DrawAdditive(0, x, y, &p->rcActive); + + SPR_Set(gHUD.GetSprite(m_HUD_selection), r, g, b ); + SPR_DrawAdditive(0, x, y, &gHUD.GetSpriteRect(m_HUD_selection)); + + // Lookup iID for helptext + gHUD.SetSelectingWeaponID(p->iId, r, g, b); + } + else + { + // Draw Weapon if Red if no ammo + + //if (gWR.IsSelectable(p)) + // ScaleColors(r, g, b, 192); + //else + //{ + // UnpackRGB(r,g,b, RGB_REDISH); + // ScaleColors(r, g, b, 128); + //} + + SPR_Set( p->hInactive, r, g, b ); + SPR_DrawAdditive( 0, x, y, &p->rcInactive ); + } + + // Draw Ammo Bar + + DrawAmmoBar(p, x + giABWidth/2, y, giABWidth, giABHeight); + + y += p->rcActive.bottom - p->rcActive.top + 5; + } + + x += iWidth + 5; + + } + else + { + // Draw Row of weapons. + gHUD.GetPrimaryHudColor(r, g, b); + + for ( int iPos = 0; iPos < MAX_WEAPON_POSITIONS; iPos++ ) + { + WEAPON *p = gWR.GetWeaponSlot( i, iPos ); + + if ( !p || !p->iId ) + continue; + + if ( gWR.IsEnabled(p) ) + { + gHUD.GetPrimaryHudColor(r, g, b); + a = 128; + } + else + { + UnpackRGB(r,g,b, RGB_REDISH); + a = 96; + } + + FillRGBA( x, y, giBucketWidth, giBucketHeight, r, g, b, a ); + + y += giBucketHeight + 5; + } + + x += giBucketWidth + 5; + } + } + + return 1; + +} + + +/* ================================= + GetSpriteList + +Finds and returns the matching +sprite name 'psz' and resolution 'iRes' +in the given sprite list 'pList' +iCount is the number of items in the pList +================================= */ +client_sprite_t *GetSpriteList(client_sprite_t *pList, const char *psz, int iRes, int iCount) +{ + if (!pList) + return NULL; + + int i = iCount; + client_sprite_t *p = pList; + + while(i--) + { + if ((!strcmp(psz, p->szName)) && (p->iRes == iRes)) + return p; + p++; + } + + return NULL; +} diff --git a/main/source/cl_dll/ammo.h b/main/source/cl_dll/ammo.h index 577497f..b5c3f22 100644 --- a/main/source/cl_dll/ammo.h +++ b/main/source/cl_dll/ammo.h @@ -1,62 +1,64 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ - -#ifndef __AMMO_H__ -#define __AMMO_H__ - -#define MAX_WEAPON_NAME 128 - - -#define WEAPON_FLAGS_SELECTIONEMPTY 1 - -struct WEAPON -{ - char szName[MAX_WEAPON_NAME]; - int iAmmoType; - int iAmmo2Type; - int iMax1; - int iMax2; - int iSlot; - int iSlotPos; - int iFlags; - int iId; - int iClip; - // puzl: 497 - weapon enable state - int iEnabled; - - int iCount; // # of itesm in plist - - HSPRITE hActive; - wrect_t rcActive; - HSPRITE hInactive; - wrect_t rcInactive; - HSPRITE hAmmo; - wrect_t rcAmmo; - HSPRITE hAmmo2; - wrect_t rcAmmo2; - HSPRITE hCrosshair; - wrect_t rcCrosshair; - HSPRITE hAutoaim; - wrect_t rcAutoaim; - HSPRITE hZoomedCrosshair; - wrect_t rcZoomedCrosshair; - HSPRITE hZoomedAutoaim; - wrect_t rcZoomedAutoaim; -}; - -typedef int AMMO; - - +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +#ifndef __AMMO_H__ +#define __AMMO_H__ + +#define MAX_WEAPON_NAME 128 + + +#define WEAPON_FLAGS_SELECTIONEMPTY 1 + +struct WEAPON +{ + char szName[MAX_WEAPON_NAME]; + int iAmmoType; + int iAmmo2Type; + int iMax1; + int iMax2; + int iSlot; + int iSlotPos; + int iFlags; + int iId; + int iClip; + // : 497 - weapon enable state + int iEnabled; + + int iCount; // # of itesm in plist + + HSPRITE hActive; + wrect_t rcActive; + HSPRITE hInactive; + wrect_t rcInactive; + HSPRITE hAmmo; + wrect_t rcAmmo; + HSPRITE hAmmo2; + wrect_t rcAmmo2; + HSPRITE hCrosshair; + wrect_t rcCrosshair; + +/* HSPRITE hAutoaim; + wrect_t rcAutoaim; + HSPRITE hZoomedCrosshair; + wrect_t rcZoomedCrosshair; + HSPRITE hZoomedAutoaim; + wrect_t rcZoomedAutoaim; + */ +}; + +typedef int AMMO; + + #endif \ No newline at end of file diff --git a/main/source/cl_dll/ammohistory.h b/main/source/cl_dll/ammohistory.h index 4c2b560..fb42951 100644 --- a/main/source/cl_dll/ammohistory.h +++ b/main/source/cl_dll/ammohistory.h @@ -38,7 +38,7 @@ public: void Init( void ); void Reset( void ); - void LoadWeaponSprites( WEAPON* wp ); + void LoadWeaponSprites( WEAPON* wp, int custom ); void LoadAllWeaponSprites( void ); WEAPON* GetWeapon( int iId ); @@ -62,6 +62,8 @@ public: //CONSIDER: Should the selection functions be in the menu with the selection variables? void UserCmd_LastInv( void ); + void UserCmd_MovementOn( void ); + void UserCmd_MovementOff( void ); void SetValidWeapon( void ); void SetCurrentWeapon( WEAPON* wp ); void SelectSlot( int iSlot, int fAdvance, int iDirection ); diff --git a/main/source/cl_dll/cdll_int.cpp b/main/source/cl_dll/cdll_int.cpp index f895b8f..6c2833c 100644 --- a/main/source/cl_dll/cdll_int.cpp +++ b/main/source/cl_dll/cdll_int.cpp @@ -155,6 +155,8 @@ int CL_DLLEXPORT Initialize( cl_enginefunc_t *pEnginefuncs, int iVersion ) memcpy(&gEngfuncs, pEnginefuncs, sizeof(cl_enginefunc_t)); EV_HookEvents(); + gHUD.InitExploitPrevention(); + // get tracker interface, if any char szDir[512]; if (!gEngfuncs.COM_ExpandFilename("Bin/TrackerUI.dll", szDir, sizeof(szDir))) @@ -190,6 +192,7 @@ so the HUD can reinitialize itself. int CL_DLLEXPORT HUD_VidInit( void ) { + gHUD.InitExploitPrevention(); RecClHudVidInit(); gHUD.VidInit(); diff --git a/main/source/cl_dll/chudmisc.h b/main/source/cl_dll/chudmisc.h index 5948e1e..a2dea51 100644 --- a/main/source/cl_dll/chudmisc.h +++ b/main/source/cl_dll/chudmisc.h @@ -1,497 +1,499 @@ -#ifndef CHUDMISC_H -#define CHUDMISC_H - -#define RGB_YELLOWISH 0x00FFA000 //255,160,0 -#define RGB_REDISH 0x00FF1010 //255,160,0 -#define RGB_GREENISH 0x0000A000 //0,160,0 -#define RGB_MARINE_BLUE 0x000099FF //0 153 255 -#define RGB_MARINE_SELECTED 0x006EE6FF //110, 230, 255 -#define RGB_MARINE_PARASITED 0x00E3F03D //227, 240, 61 - -#include "wrect.h" -#include "cl_dll.h" -#include "ammo.h" -#include "game_shared/teamconst.h" - -#define DHN_DRAWZERO 1 -#define DHN_2DIGITS 2 -#define DHN_3DIGITS 4 -#define MIN_ALPHA 100 - -#define HUDELEM_ACTIVE 1 - -typedef struct { - int x, y; -} POSITION; - -typedef struct { - unsigned char r,g,b,a; -} RGBA; - -typedef struct cvar_s cvar_t; - - -#define HUD_ACTIVE 1 -#define HUD_INTERMISSION 2 - -#define MAX_PLAYER_NAME_LENGTH 32 - -#define MAX_MOTD_LENGTH 1536 - -// -//----------------------------------------------------- -// -class CHudBase -{ -public: - POSITION m_pos; - int m_type; - int m_iFlags; // active, moving, - virtual ~CHudBase() {} - virtual int Init( void ) {return 0;} - virtual int VidInit( void ) {return 0;} - virtual int Draw(float flTime) {return 0;} - virtual void Think(void) {return;} - virtual void Reset(void) {return;} - virtual void InitHUDData( void ) {} // called every time a server is connected to - -}; - -struct HUDLIST { - CHudBase *p; - HUDLIST *pNext; -}; - - - -// -//----------------------------------------------------- -// -#include "..\game_shared\voice_status.h" -#include "hud_spectator.h" - - -// -//----------------------------------------------------- -// -class CHudAmmo: public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - int Draw(float flTime); - void Think(void); - void Reset(void); - int DrawWList(float flTime); - int MsgFunc_CurWeapon(const char *pszName, int iSize, void *pbuf); - int MsgFunc_WeaponList(const char *pszName, int iSize, void *pbuf); - int MsgFunc_AmmoX(const char *pszName, int iSize, void *pbuf); - int MsgFunc_AmmoPickup( const char *pszName, int iSize, void *pbuf ); - int MsgFunc_WeapPickup( const char *pszName, int iSize, void *pbuf ); - int MsgFunc_ItemPickup( const char *pszName, int iSize, void *pbuf ); - int MsgFunc_HideWeapon( const char *pszName, int iSize, void *pbuf ); - - void SlotInput( int iSlot ); - void _cdecl UserCmd_Slot1( void ); - void _cdecl UserCmd_Slot2( void ); - void _cdecl UserCmd_Slot3( void ); - void _cdecl UserCmd_Slot4( void ); - void _cdecl UserCmd_Slot5( void ); - void _cdecl UserCmd_Slot6( void ); - void _cdecl UserCmd_Slot7( void ); - void _cdecl UserCmd_Slot8( void ); - void _cdecl UserCmd_Slot9( void ); - void _cdecl UserCmd_Slot10( void ); - void _cdecl UserCmd_Close( void ); - void _cdecl UserCmd_NextWeapon( void ); - void _cdecl UserCmd_PrevWeapon( void ); - - void SetCurrentClip(int inClip); -private: - float m_fFade; - RGBA m_rgba; - WEAPON *m_pWeapon; - int m_HUD_bucket0; - int m_HUD_selection; - -}; - - - -// -//----------------------------------------------------- -// -class CHudAmmoSecondary: public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - void Reset( void ); - int Draw(float flTime); - - int MsgFunc_SecAmmoVal( const char *pszName, int iSize, void *pbuf ); - int MsgFunc_SecAmmoIcon( const char *pszName, int iSize, void *pbuf ); - -private: - enum { - MAX_SEC_AMMO_VALUES = 4 - }; - - int m_HUD_ammoicon; // sprite indices - int m_iAmmoAmounts[MAX_SEC_AMMO_VALUES]; - float m_fFade; -}; - - -#include "health.h" - - -#define FADE_TIME 100 - - - - -// -//----------------------------------------------------- -// -class CHudGeiger: public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - int Draw(float flTime); - int MsgFunc_Geiger(const char *pszName, int iSize, void *pbuf); - -private: - int m_iGeigerRange; - -}; - -// -//----------------------------------------------------- -// -class CHudTrain: public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - int Draw(float flTime); - int MsgFunc_Train(const char *pszName, int iSize, void *pbuf); - -private: - HSPRITE m_hSprite; - int m_iPos; - -}; - -// -//----------------------------------------------------- -// -class CHudStatusBar : public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - int Draw( float flTime ); - const char* GetStatusString() const; - void Reset( void ); - void ParseStatusString( int line_num ); - - int MsgFunc_StatusText( const char *pszName, int iSize, void *pbuf ); - int MsgFunc_StatusValue( const char *pszName, int iSize, void *pbuf ); - -protected: - void ReparseStringIfNeeded(); - - enum { - MAX_STATUSTEXT_LENGTH = 128, - MAX_STATUSBAR_VALUES = 8, - MAX_STATUSBAR_LINES = 2, - }; - - char m_szStatusText[MAX_STATUSBAR_LINES][MAX_STATUSTEXT_LENGTH]; // a text string describing how the status bar is to be drawn - char m_szStatusBar[MAX_STATUSBAR_LINES][MAX_STATUSTEXT_LENGTH]; // the constructed bar that is drawn - int m_iStatusValues[MAX_STATUSBAR_VALUES]; // an array of values for use in the status bar - - int m_bReparseString; // set to TRUE whenever the m_szStatusBar needs to be recalculated - - // an array of colors...one color for each line - float *m_pflNameColors[MAX_STATUSBAR_LINES]; -}; - -class ScoreboardIcon; -// puzl: 0001073 -#define CUSTOM_ICON_LENGTH 32 - -struct extra_player_info_t -{ - short score; - short lastScore; - float timeOfLastScoreChange; - - short frags; - short deaths; - short playerclass; - short auth; - short teamnumber; - char teamname[MAX_TEAM_NAME]; - char customicon[CUSTOM_ICON_LENGTH + 3]; //last 3 characters is the color. - ScoreboardIcon* icon; -}; - -struct team_info_t -{ - char name[MAX_TEAM_NAME]; - short score; - short frags; - short deaths; - short ping; - short packetloss; - short ownteam; - short players; - int already_drawn; - int scores_overriden; - int teamnumber; -}; - -extern hud_player_info_t g_PlayerInfoList[MAX_PLAYERS+1]; // player info from the engine -extern extra_player_info_t g_PlayerExtraInfo[MAX_PLAYERS+1]; // additional player info sent directly to the client dll -extern team_info_t g_TeamInfo[MAX_TEAMS+1]; -extern int g_IsSpectator[MAX_PLAYERS+1]; - - -// -//----------------------------------------------------- -// -class CHudDeathNotice : public CHudBase -{ -public: - int Init( void ); - void InitHUDData( void ); - int VidInit( void ); - int Draw( float flTime ); - int MsgFunc_DeathMsg( const char *pszName, int iSize, void *pbuf ); - -private: - int m_HUD_d_skull; // sprite index of skull icon -}; - - -// -//----------------------------------------------------- -// -class CHudMenu : public CHudBase -{ -public: - int Init( void ); - void InitHUDData( void ); - int VidInit( void ); - void Reset( void ); - int Draw( float flTime ); - int MsgFunc_ShowMenu( const char *pszName, int iSize, void *pbuf ); - - void SelectMenuItem( int menu_item ); - - int m_fMenuDisplayed; - int m_bitsValidSlots; - float m_flShutoffTime; - int m_fWaitingForMore; -}; - - -// -//----------------------------------------------------- -// -class CHudSayText : public CHudBase -{ -public: - int Init( void ); - void InitHUDData( void ); - int VidInit( void ); - int Draw( float flTime ); - int MsgFunc_SayText( const char *pszName, int iSize, void *pbuf ); - void SayTextPrint( const char *pszBuf, int iBufSize, int clientIndex = -1 ); - void EnsureTextFitsInOneLineAndWrapIfHaveTo( int line ); -friend class CHud; -friend class CHudSpectator; - -private: - - struct cvar_s * m_HUD_saytext; - struct cvar_s * m_HUD_saytext_time; -}; - - -// -//----------------------------------------------------- -// -class CHudBattery: public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - int Draw(float flTime); - int MsgFunc_Battery(const char *pszName, int iSize, void *pbuf ); - -private: - HSPRITE m_hSprite1; - HSPRITE m_hSprite2; - wrect_t *m_prc1; - wrect_t *m_prc2; - int m_iBat; - float m_fFade; - int m_iHeight; // width of the battery innards -}; - - - -// -//----------------------------------------------------- -// -class CHudFlashlight: public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - int Draw(float flTime); - void Reset( void ); - int MsgFunc_Flashlight(const char *pszName, int iSize, void *pbuf ); - int MsgFunc_FlashBat(const char *pszName, int iSize, void *pbuf ); - -private: - HSPRITE m_hSprite1; - HSPRITE m_hSprite2; - HSPRITE m_hBeam; - wrect_t *m_prc1; - wrect_t *m_prc2; - wrect_t *m_prcBeam; - float m_flBat; - int m_iBat; - int m_fOn; - float m_fFade; - int m_iWidth; // width of the battery innards -}; - - -// -//----------------------------------------------------- -// -const int maxHUDMessages = 16; -struct message_parms_t -{ - client_textmessage_t *pMessage; - float time; - int x, y; - int totalWidth, totalHeight; - int width; - int lines; - int lineLength; - int length; - int r, g, b; - int text; - int fadeBlend; - float charTime; - float fadeTime; -}; - - -// -//----------------------------------------------------- -// -class CHudTextMessage: public CHudBase -{ -public: - int Init( void ); - static char *LocaliseTextString( const char *msg, char *dst_buffer, int buffer_size ); - static char *BufferedLocaliseTextString( const char *msg ); - char *LookupString( const char *msg_name, int *msg_dest = NULL ); - int MsgFunc_TextMsg(const char *pszName, int iSize, void *pbuf); -}; - - -// -//----------------------------------------------------- -// - -class CHudMessage: public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - int Draw(float flTime); - int MsgFunc_HudText(const char *pszName, int iSize, void *pbuf); - int MsgFunc_HudText2(const char *pszName, int iSize, void *pbuf); - int MsgFunc_GameTitle(const char *pszName, int iSize, void *pbuf); - - float FadeBlend( float fadein, float fadeout, float hold, float localTime ); - int XPosition( float x, int width, int lineWidth ); - int YPosition( float y, int height ); - - void MessageAddPlayerID(const char* pName, bool inEnemy); - - void MessageAdd( const char *pName, float time ); - bool MessageRemove( const char *pName ); - void MessageDrawScan( client_textmessage_t *pMessage, float time ); - void MessageScanStart( void ); - void MessageScanNextChar( void ); - void Reset( void ); - -private: - bool DrawMessage(client_textmessage_t* inMessage, float inStartTime, float inCurrentTime); - void SetPlayerIDPosition(); - - client_textmessage_t *m_pMessages[maxHUDMessages]; - float m_startTime[maxHUDMessages]; - message_parms_t m_parms; - float m_gameTitleTime; - client_textmessage_t *m_pGameTitle; - - client_textmessage_t mPlayerIDMessage; - float mPlayerIDTime; - char* mPlayerID; - - int m_HUD_title_life; - int m_HUD_title_half; -}; - -// -//----------------------------------------------------- -// -#define MAX_SPRITE_NAME_LENGTH 24 - -class CHudStatusIcons: public CHudBase -{ -public: - int Init( void ); - int VidInit( void ); - void Reset( void ); - int Draw(float flTime); - - enum { - MAX_ICONSPRITENAME_LENGTH = MAX_SPRITE_NAME_LENGTH, - MAX_ICONSPRITES = 4, - }; - - - //had to make these public so CHud could access them (to enable concussion icon) - //could use a friend declaration instead... - void EnableIcon( char *pszIconName, unsigned char red, unsigned char green, unsigned char blue ); - void DisableIcon( char *pszIconName ); - -private: - - typedef struct - { - char szSpriteName[MAX_ICONSPRITENAME_LENGTH]; - HSPRITE spr; - wrect_t rc; - unsigned char r, g, b; - } icon_sprite_t; - - icon_sprite_t m_IconList[MAX_ICONSPRITES]; - -}; - -typedef struct cvar_s cvar_t; - +#ifndef CHUDMISC_H +#define CHUDMISC_H + +#define RGB_YELLOWISH 0x00FFA000 //255,160,0 +#define RGB_REDISH 0x00FF1010 //255,160,0 +#define RGB_GREENISH 0x0000A000 //0,160,0 +#define RGB_MARINE_BLUE 0x000099FF //0 153 255 +#define RGB_MARINE_SELECTED 0x006EE6FF //110, 230, 255 +#define RGB_MARINE_PARASITED 0x00E3F03D //227, 240, 61 + +#include "wrect.h" +#include "cl_dll.h" +#include "ammo.h" +#include "game_shared/teamconst.h" + +#define DHN_DRAWZERO 1 +#define DHN_2DIGITS 2 +#define DHN_3DIGITS 4 +#define MIN_ALPHA 100 + +#define HUDELEM_ACTIVE 1 + +typedef struct { + int x, y; +} POSITION; + +typedef struct { + unsigned char r,g,b,a; +} RGBA; + +typedef struct cvar_s cvar_t; + + +#define HUD_ACTIVE 1 +#define HUD_INTERMISSION 2 + +#define MAX_PLAYER_NAME_LENGTH 32 + +#define MAX_MOTD_LENGTH 1536 + +// +//----------------------------------------------------- +// +class CHudBase +{ +public: + POSITION m_pos; + int m_type; + int m_iFlags; // active, moving, + virtual ~CHudBase() {} + virtual int Init( void ) {return 0;} + virtual int VidInit( void ) {return 0;} + virtual int Draw(float flTime) {return 0;} + virtual void Think(void) {return;} + virtual void Reset(void) {return;} + virtual void InitHUDData( void ) {} // called every time a server is connected to + +}; + +struct HUDLIST { + CHudBase *p; + HUDLIST *pNext; +}; + + + +// +//----------------------------------------------------- +// +#include "..\game_shared\voice_status.h" +#include "hud_spectator.h" + + +// +//----------------------------------------------------- +// +class CHudAmmo: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw(float flTime); + void Think(void); + void Reset(void); + int DrawWList(float flTime); + int MsgFunc_CurWeapon(const char *pszName, int iSize, void *pbuf); + int MsgFunc_WeaponList(const char *pszName, int iSize, void *pbuf); + int MsgFunc_AmmoX(const char *pszName, int iSize, void *pbuf); + int MsgFunc_AmmoPickup( const char *pszName, int iSize, void *pbuf ); + int MsgFunc_WeapPickup( const char *pszName, int iSize, void *pbuf ); + int MsgFunc_ItemPickup( const char *pszName, int iSize, void *pbuf ); + int MsgFunc_HideWeapon( const char *pszName, int iSize, void *pbuf ); + + void SlotInput( int iSlot ); + void _cdecl UserCmd_Slot1( void ); + void _cdecl UserCmd_Slot2( void ); + void _cdecl UserCmd_Slot3( void ); + void _cdecl UserCmd_Slot4( void ); + void _cdecl UserCmd_Slot5( void ); + void _cdecl UserCmd_Slot6( void ); + void _cdecl UserCmd_Slot7( void ); + void _cdecl UserCmd_Slot8( void ); + void _cdecl UserCmd_Slot9( void ); + void _cdecl UserCmd_Slot10( void ); + void _cdecl UserCmd_Close( void ); + void _cdecl UserCmd_NextWeapon( void ); + void _cdecl UserCmd_PrevWeapon( void ); + + void SetCurrentClip(int inClip); +private: + float m_fFade; + RGBA m_rgba; + WEAPON *m_pWeapon; + int m_HUD_bucket0; + int m_HUD_selection; + int m_customCrosshair; + +}; + + + +// +//----------------------------------------------------- +// +class CHudAmmoSecondary: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + void Reset( void ); + int Draw(float flTime); + + int MsgFunc_SecAmmoVal( const char *pszName, int iSize, void *pbuf ); + int MsgFunc_SecAmmoIcon( const char *pszName, int iSize, void *pbuf ); + +private: + enum { + MAX_SEC_AMMO_VALUES = 4 + }; + + int m_HUD_ammoicon; // sprite indices + int m_iAmmoAmounts[MAX_SEC_AMMO_VALUES]; + float m_fFade; +}; + + +#include "health.h" + + +#define FADE_TIME 100 + + + + +// +//----------------------------------------------------- +// +class CHudGeiger: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw(float flTime); + int MsgFunc_Geiger(const char *pszName, int iSize, void *pbuf); + +private: + int m_iGeigerRange; + +}; + +// +//----------------------------------------------------- +// +class CHudTrain: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw(float flTime); + int MsgFunc_Train(const char *pszName, int iSize, void *pbuf); + +private: + HSPRITE m_hSprite; + int m_iPos; + +}; + +// +//----------------------------------------------------- +// +class CHudStatusBar : public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw( float flTime ); + const char* GetStatusString() const; + void Reset( void ); + void ParseStatusString( int line_num ); + + int MsgFunc_StatusText( const char *pszName, int iSize, void *pbuf ); + int MsgFunc_StatusValue( const char *pszName, int iSize, void *pbuf ); + +protected: + void ReparseStringIfNeeded(); + + enum { + MAX_STATUSTEXT_LENGTH = 128, + MAX_STATUSBAR_VALUES = 8, + MAX_STATUSBAR_LINES = 2, + }; + + char m_szStatusText[MAX_STATUSBAR_LINES][MAX_STATUSTEXT_LENGTH]; // a text string describing how the status bar is to be drawn + char m_szStatusBar[MAX_STATUSBAR_LINES][MAX_STATUSTEXT_LENGTH]; // the constructed bar that is drawn + int m_iStatusValues[MAX_STATUSBAR_VALUES]; // an array of values for use in the status bar + + int m_bReparseString; // set to TRUE whenever the m_szStatusBar needs to be recalculated + + // an array of colors...one color for each line + float *m_pflNameColors[MAX_STATUSBAR_LINES]; +}; + +class ScoreboardIcon; +// : 0001073 +#define CUSTOM_ICON_LENGTH 32 + +struct extra_player_info_t +{ + short score; + short lastScore; + float timeOfLastScoreChange; + short frags; + short deaths; + short playerclass; + short extra; + short auth; + short teamnumber; + char teamname[MAX_TEAM_NAME]; + char customicon[CUSTOM_ICON_LENGTH + 3]; //last 3 characters is the color. + short health; + ScoreboardIcon* icon; +}; + +struct team_info_t +{ + char name[MAX_TEAM_NAME]; + short score; + short frags; + short deaths; + short ping; + short packetloss; + short ownteam; + short players; + int already_drawn; + int scores_overriden; + int teamnumber; +}; + +extern hud_player_info_t g_PlayerInfoList[MAX_PLAYERS+1]; // player info from the engine +extern extra_player_info_t g_PlayerExtraInfo[MAX_PLAYERS+1]; // additional player info sent directly to the client dll +extern team_info_t g_TeamInfo[MAX_TEAMS+1]; +extern int g_IsSpectator[MAX_PLAYERS+1]; + + +// +//----------------------------------------------------- +// +class CHudDeathNotice : public CHudBase +{ +public: + int Init( void ); + void InitHUDData( void ); + int VidInit( void ); + int Draw( float flTime ); + int MsgFunc_DeathMsg( const char *pszName, int iSize, void *pbuf ); + +private: + int m_HUD_d_skull; // sprite index of skull icon +}; + + +// +//----------------------------------------------------- +// +class CHudMenu : public CHudBase +{ +public: + int Init( void ); + void InitHUDData( void ); + int VidInit( void ); + void Reset( void ); + int Draw( float flTime ); + int MsgFunc_ShowMenu( const char *pszName, int iSize, void *pbuf ); + + void SelectMenuItem( int menu_item ); + + int m_fMenuDisplayed; + int m_bitsValidSlots; + float m_flShutoffTime; + int m_fWaitingForMore; +}; + + +// +//----------------------------------------------------- +// +class CHudSayText : public CHudBase +{ +public: + int Init( void ); + void InitHUDData( void ); + int VidInit( void ); + int Draw( float flTime ); + int MsgFunc_SayText( const char *pszName, int iSize, void *pbuf ); + void SayTextPrint( const char *pszBuf, int iBufSize, int clientIndex = -1 ); + void EnsureTextFitsInOneLineAndWrapIfHaveTo( int line ); +friend class CHud; +friend class CHudSpectator; + +private: + + struct cvar_s * m_HUD_saytext; + struct cvar_s * m_HUD_saytext_time; +}; + + +// +//----------------------------------------------------- +// +class CHudBattery: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw(float flTime); + int MsgFunc_Battery(const char *pszName, int iSize, void *pbuf ); + +private: + HSPRITE m_hSprite1; + HSPRITE m_hSprite2; + wrect_t *m_prc1; + wrect_t *m_prc2; + int m_iBat; + float m_fFade; + int m_iHeight; // width of the battery innards +}; + + + +// +//----------------------------------------------------- +// +class CHudFlashlight: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw(float flTime); + void Reset( void ); + int MsgFunc_Flashlight(const char *pszName, int iSize, void *pbuf ); + int MsgFunc_FlashBat(const char *pszName, int iSize, void *pbuf ); + +private: + HSPRITE m_hSprite1; + HSPRITE m_hSprite2; + HSPRITE m_hBeam; + wrect_t *m_prc1; + wrect_t *m_prc2; + wrect_t *m_prcBeam; + float m_flBat; + int m_iBat; + int m_fOn; + float m_fFade; + int m_iWidth; // width of the battery innards +}; + + +// +//----------------------------------------------------- +// +const int maxHUDMessages = 16; +struct message_parms_t +{ + client_textmessage_t *pMessage; + float time; + int x, y; + int totalWidth, totalHeight; + int width; + int lines; + int lineLength; + int length; + int r, g, b; + int text; + int fadeBlend; + float charTime; + float fadeTime; +}; + + +// +//----------------------------------------------------- +// +class CHudTextMessage: public CHudBase +{ +public: + int Init( void ); + static char *LocaliseTextString( const char *msg, char *dst_buffer, int buffer_size ); + static char *BufferedLocaliseTextString( const char *msg ); + char *LookupString( const char *msg_name, int *msg_dest = NULL ); + int MsgFunc_TextMsg(const char *pszName, int iSize, void *pbuf); +}; + + +// +//----------------------------------------------------- +// + +class CHudMessage: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw(float flTime); + int MsgFunc_HudText(const char *pszName, int iSize, void *pbuf); + int MsgFunc_HudText2(const char *pszName, int iSize, void *pbuf); + int MsgFunc_GameTitle(const char *pszName, int iSize, void *pbuf); + + float FadeBlend( float fadein, float fadeout, float hold, float localTime ); + int XPosition( float x, int width, int lineWidth ); + int YPosition( float y, int height ); + + void MessageAddPlayerID(const char* pName, bool inEnemy); + + void MessageAdd( const char *pName, float time ); + bool MessageRemove( const char *pName ); + void MessageDrawScan( client_textmessage_t *pMessage, float time ); + void MessageScanStart( void ); + void MessageScanNextChar( void ); + void Reset( void ); + +private: + bool DrawMessage(client_textmessage_t* inMessage, float inStartTime, float inCurrentTime); + void SetPlayerIDPosition(); + + client_textmessage_t *m_pMessages[maxHUDMessages]; + float m_startTime[maxHUDMessages]; + message_parms_t m_parms; + float m_gameTitleTime; + client_textmessage_t *m_pGameTitle; + + client_textmessage_t mPlayerIDMessage; + float mPlayerIDTime; + char* mPlayerID; + + int m_HUD_title_life; + int m_HUD_title_half; +}; + +// +//----------------------------------------------------- +// +#define MAX_SPRITE_NAME_LENGTH 24 + +class CHudStatusIcons: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + void Reset( void ); + int Draw(float flTime); + + enum { + MAX_ICONSPRITENAME_LENGTH = MAX_SPRITE_NAME_LENGTH, + MAX_ICONSPRITES = 4, + }; + + + //had to make these public so CHud could access them (to enable concussion icon) + //could use a friend declaration instead... + void EnableIcon( char *pszIconName, unsigned char red, unsigned char green, unsigned char blue ); + void DisableIcon( char *pszIconName ); + +private: + + typedef struct + { + char szSpriteName[MAX_ICONSPRITENAME_LENGTH]; + HSPRITE spr; + wrect_t rc; + unsigned char r, g, b; + } icon_sprite_t; + + icon_sprite_t m_IconList[MAX_ICONSPRITES]; + +}; + +typedef struct cvar_s cvar_t; + #endif \ No newline at end of file diff --git a/main/source/cl_dll/cl_dll.vcproj b/main/source/cl_dll/cl_dll.vcproj index 707e8db..8edd149 100644 --- a/main/source/cl_dll/cl_dll.vcproj +++ b/main/source/cl_dll/cl_dll.vcproj @@ -27,7 +27,7 @@ Optimization="0" OptimizeForProcessor="0" AdditionalIncludeDirectories=""$(SolutionDir)";U:\inlcude\stlport;U:\include\vgui;U:\include\nexus;U:\include\libpng;U:\include\fmod;U:\include\lua;U:\include\particle;U:\include\zlib" - PreprocessorDefinitions="_DEBUG;_MBCS;DEBUG;WIN32;_WINDOWS;AVH_CLIENT;$(NoInherit)" + PreprocessorDefinitions="_DEBUG;_MBCS;DEBUG;WIN32;_WINDOWS;AVH_CLIENT;$(NoInherit);USE_OLDAUTH" MinimalRebuild="TRUE" BasicRuntimeChecks="3" SmallerTypeCheck="TRUE" @@ -113,7 +113,7 @@ EnableIntrinsicFunctions="TRUE" OptimizeForProcessor="0" AdditionalIncludeDirectories=""$(SolutionDir)";U:\inlcude\stlport;U:\include\vgui;U:\include\nexus;U:\include\libpng;U:\include\fmod;U:\include\lua;U:\include\particle;U:\include\zlib" - PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;AVH_CLIENT;AVH_PLAYTEST_BUILD;$(NOINHERIT)" + PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;AVH_CLIENT;USE_OLDAUTH;$(NOINHERIT)" StringPooling="TRUE" RuntimeLibrary="0" RuntimeTypeInfo="TRUE" @@ -5949,31 +5949,6 @@ RelativePath="..\textrep\TRTagValuePair.h"> - - - - - - - - - - - - - - - - diff --git a/main/source/cl_dll/demo.cpp b/main/source/cl_dll/demo.cpp index 50cc3ef..1b3361a 100644 --- a/main/source/cl_dll/demo.cpp +++ b/main/source/cl_dll/demo.cpp @@ -100,6 +100,7 @@ void CL_DLLEXPORT Demo_ReadBuffer( int size, unsigned char *buffer ) int type; int i = 0; bool theMouseVisibility = false; + int particleIndex=0; type = *( int * )buffer; i += sizeof( int ); @@ -176,7 +177,7 @@ void CL_DLLEXPORT Demo_ReadBuffer( int size, unsigned char *buffer ) break; case TYPE_PARTICLES: - i += gParticleTemplateList.InitializeDemoPlayback(size, (unsigned char*)&buffer[i]); + i += gParticleTemplateList.InitializeDemoPlayback(size, (unsigned char*)&buffer[i], particleIndex++); break; case TYPE_BASESTATE2: diff --git a/main/source/cl_dll/entity.cpp b/main/source/cl_dll/entity.cpp index 996feb3..ce31e21 100644 --- a/main/source/cl_dll/entity.cpp +++ b/main/source/cl_dll/entity.cpp @@ -297,7 +297,7 @@ void MoveModel( void ) } #endif - +*/ //#define TRACE_TEST #if defined( TRACE_TEST ) @@ -328,7 +328,7 @@ void TraceModel( void ) } #endif -*/ + /* void ParticleCallback( struct particle_s *particle, float frametime ) @@ -545,11 +545,11 @@ void CL_DLLEXPORT HUD_CreateEntities( void ) #if defined( TEST_IT ) MoveModel(); #endif - +*/ #if defined( TRACE_TEST ) TraceModel(); #endif -*/ + /* Particles(); */ diff --git a/main/source/cl_dll/ev_common.cpp b/main/source/cl_dll/ev_common.cpp index 1622473..5c44136 100644 --- a/main/source/cl_dll/ev_common.cpp +++ b/main/source/cl_dll/ev_common.cpp @@ -104,6 +104,15 @@ qboolean EV_IsLocal( int idx ) return gEngfuncs.pEventAPI->EV_IsLocal( idx - 1 ) ? true : false; } +qboolean EV_IsSpec( int idx ) +{ + // check if we are in some way in first person spec mode + if ( IS_FIRSTPERSON_SPEC ) + return (g_iUser2 == idx); + else + return false; +} + /* ================= EV_GetGunPosition diff --git a/main/source/cl_dll/eventscripts.h b/main/source/cl_dll/eventscripts.h index 0e169ed..b99929d 100644 --- a/main/source/cl_dll/eventscripts.h +++ b/main/source/cl_dll/eventscripts.h @@ -21,6 +21,7 @@ void EV_EjectBrass( float *origin, float *velocity, float rotation, int model, i void EV_GetGunPosition( struct event_args_s *args, float *pos, float *origin ); void EV_GetDefaultShellInfo( struct event_args_s *args, float *origin, float *velocity, float *ShellVelocity, float *ShellOrigin, float *forward, float *right, float *up, float forwardScale, float upScale, float rightScale ); qboolean EV_IsLocal( int idx ); +qboolean EV_IsSpec( int idx ); qboolean EV_IsPlayer( int idx ); void EV_CreateTracer( float *start, float *end ); diff --git a/main/source/cl_dll/hl/hl_weapons.cpp b/main/source/cl_dll/hl/hl_weapons.cpp index a486592..f00c72a 100644 --- a/main/source/cl_dll/hl/hl_weapons.cpp +++ b/main/source/cl_dll/hl/hl_weapons.cpp @@ -1,1404 +1,1537 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -* Modified by Charlie Cleveland: -* -* $Workfile: hl_weapons.cpp $ -* $Date: 2002/10/24 21:11:52 $ -* -* ------------------------------------------------------------------------------- -* $Log: hl_weapons.cpp,v $ -* Revision 1.31 2002/10/24 21:11:52 Flayra -* - Updated jetpack effect because it was really buggy this old way -* -* Revision 1.30 2002/10/16 02:12:10 Flayra -* - Valve anti-cheat integrated! -* -* Revision 1.29 2002/10/16 00:36:40 Flayra -* - Removed blink fail -* -* Revision 1.28 2002/08/31 18:01:59 Flayra -* - Work at VALVe -* -* Revision 1.27 2002/07/08 16:09:03 Flayra -* - Removed unneeded code here, so random numbers were generated properly on both client and server -* -* Revision 1.26 2002/07/01 20:56:31 Flayra -* - Added babbler gun -* -* Revision 1.25 2002/06/25 17:03:01 Flayra -* - Removed old weapons, added new weapons, fixed mines, iuser3 enables and disables weapons -* -* =============================================================================== -****/ -#include "../hud.h" -#include "../cl_util.h" -#include "common/const.h" -#include "common/entity_state.h" -#include "common/cl_entity.h" -#include "../demo.h" -#include "common/usercmd.h" -#include "common/event_flags.h" -#include "common/event_api.h" -#include "cl_dll/com_weapons.h" -#include "cl_dll/ammo.h" -#include "cl_dll/ammohistory.h" -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "dlls/cbase.h" -#include "dlls/monsters.h" -#include "dlls/weapons.h" -#include "dlls/nodes.h" -#include "dlls/player.h" -#include "mod/AvHEvents.h" - -#include "common/usercmd.h" -#include "common/entity_state.h" -#include "common/demo_api.h" -#include "pm_shared/pm_defs.h" -#include "common/event_api.h" -#include "common/r_efx.h" - -#include "../hud_iface.h" -#include "../com_weapons.h" - -#include "mod/AvHMarineWeapons.h" -#include "mod/AvHSpecials.h" -#include "mod/AvHAlienWeapons.h" -#include "mod/AvHAlienAbilities.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHMovementUtil.h" - -#include "engine/APIProxy.h" -#include "cl_dll/Exports.h" - -extern globalvars_t *gpGlobals; -extern bool gIsJetpacking; - -// Pool of client side entities/entvars_t -static entvars_t ev[ 32 ]; -static int num_ents = 0; - -// The entity we'll use to represent the local client -static CBasePlayer player; - -// Local version of game .dll global variables ( time, etc. ) -static globalvars_t Globals; - -static CBasePlayerWeapon *g_pWpns[ 32 ]; - -vec3_t previousorigin; - -// HLDM Weapon placeholder entities. -//CGlock g_Glock; - -// NS weapons -AvHKnife gKnife; -AvHMachineGun gMachineGun; -AvHPistol gPistol; -AvHSonicGun gSonicGun; -AvHHeavyMachineGun gHeavyMachineGun; -AvHGrenadeGun gGrenadeGun; -AvHGrenade gGrenade; -AvHWelder gWelder; -AvHMine gMine; -AvHSpitGun gSpitGun; -AvHClaws gClaws; -AvHSpore gSpores; -AvHBite gBite; -AvHBite2 gBite2; -AvHSpikeGun gSpikeGun; -AvHSwipe gSwipe; -AvHWebSpinner gWebSpinner; -AvHPrimalScream gPrimalScream; -AvHParasiteGun gParasite; -AvHUmbraGun gUmbra; -AvHBlinkGun gBlink; -AvHDivineWind gDivineWind; -//AvHParalysisGun gParalysisGun; -AvHBileBombGun gBileBomb; -AvHAcidRocketGun gAcidRocket; -AvHHealingSpray gHealingSpray; -AvHMetabolize gMetabolize; -AvHDevour gDevour; -AvHStomp gStomp; - -// Alien abilities -AvHLeap gLeap; -AvHCharge gCharge; - -// Jetpack events -int gJetpackEventID; -//int gWallJumpEventID; -//int gFlightEventID; -int gTeleportEventID; -int gPhaseInEventID; -int gSiegeHitEventID; -int gSiegeViewHitEventID; -int gCommanderPointsAwardedEventID; -int gBlinkEffectSuccessEventID; - -//bool gPlayingJetpack = false; -//CGlock g_Glock; -//CCrowbar g_Crowbar; -//CPython g_Python; -//CMP5 g_Mp5; -//CCrossbow g_Crossbow; -//CShotgun g_Shotgun; -//CRpg g_Rpg; -//CGauss g_Gauss; -//CEgon g_Egon; -//CHgun g_HGun; -//CHandGrenade g_HandGren; -//CSatchel g_Satchel; -//CTripmine g_Tripmine; -//CSqueak g_Snark; - -/* -====================== -AlertMessage - -Print debug messages to console -====================== -*/ -void AlertMessage( ALERT_TYPE atype, char *szFmt, ... ) -{ - va_list argptr; - static char string[1024]; - - va_start (argptr, szFmt); -#ifdef WIN32 - //overflow protection in MS version of function... - _vsnprintf( string, 1023, szFmt, argptr ); -#else - vsprintf (string, szFmt,argptr); -#endif - va_end (argptr); - - gEngfuncs.Con_Printf( "cl: " ); - gEngfuncs.Con_Printf( string ); -} - -// Client-side effects for jetpack -void CheckJetpack() -{ - // if local player is jetpacking, play effects immediately -// if(gIsJetpacking && !gPlayingJetpack) -// { -// cl_entity_t* thePlayer; -// thePlayer = gEngfuncs.GetLocalPlayer(); -// ASSERT(thePlayer); -// -// // Play event locally, server will tell everyone else to play event -// gEngfuncs.pEventAPI->EV_PlaybackEvent(0, NULL, gJetpackEventID, 0, thePlayer->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); -// -// //gPlayingJetpack = true; -// } - // Check to turn it off too (in case there's a network anomaly or the game resets or something, just trying to be safe) - //else if(!gIsJetpacking && (gPlayingJetpack)) - //{ - // gEngfuncs.pEventAPI->EV_PlaybackEvent(0, NULL, gEndJetpackEventID, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - // - // gPlayingJetpack = false; - //} -} - -//Returns if it's multiplayer. -//Mostly used by the client side weapons. -bool bIsMultiplayer ( void ) -{ - return gEngfuncs.GetMaxClients() == 1 ? 0 : 1; -} -//Just loads a v_ model. -void LoadVModel ( char *szViewModel, CBasePlayer *m_pPlayer ) -{ - gEngfuncs.CL_LoadModel( szViewModel, &m_pPlayer->pev->viewmodel ); -} - -/* -===================== -HUD_PrepEntity - -Links the raw entity to an entvars_s holder. If a player is passed in as the owner, then -we set up the m_pPlayer field. -===================== -*/ -void HUD_PrepEntity( CBaseEntity *pEntity, CBasePlayer *pWeaponOwner ) -{ - typedef vector IDListType; - static IDListType sIDList; - - memset( &ev[ num_ents ], 0, sizeof( entvars_t ) ); - pEntity->pev = &ev[ num_ents++ ]; - - pEntity->Precache(); - pEntity->Spawn(); - - if ( pWeaponOwner ) - { - ItemInfo info; - - ((CBasePlayerWeapon *)pEntity)->m_pPlayer = pWeaponOwner; - - ((CBasePlayerWeapon *)pEntity)->GetItemInfo( &info ); - - // ASSERT that a weapon with this id isn't in the list - int theNewID = info.iId; - IDListType::iterator theIter = std::find(sIDList.begin(), sIDList.end(), theNewID); - ASSERT(theIter == sIDList.end()); - - // Insert id into our list - sIDList.push_back(theNewID); - - g_pWpns[ theNewID ] = (CBasePlayerWeapon *)pEntity; - } -} - -/* -===================== -CBaseEntity :: Killed - -If weapons code "kills" an entity, just set its effects to EF_NODRAW -===================== -*/ -void CBaseEntity :: Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->effects |= EF_NODRAW; -} - -/* -===================== -CBasePlayerWeapon :: DefaultReload -===================== -*/ -BOOL CBasePlayerWeapon :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body ) -{ - - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) - return FALSE; - - int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); - - if (j == 0) - return FALSE; - - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay; - - //!!UNDONE -- reload sound goes here !!! - //SendWeaponAnim( iAnim, UseDecrement(), body ); - - m_fInReload = TRUE; - - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + kDeployIdleInterval; - return TRUE; -} - -/* -===================== -CBasePlayerWeapon :: CanDeploy -===================== -*/ -BOOL CBasePlayerWeapon :: CanDeploy( void ) -{ - BOOL bHasAmmo = 0; - - if ( !pszAmmo1() ) - { - // this weapon doesn't use ammo, can always deploy. - return TRUE; - } - - if ( pszAmmo1() ) - { - bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] != 0); - } - if ( pszAmmo2() ) - { - bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] != 0); - } - if (m_iClip > 0) - { - bHasAmmo |= 1; - } - if (!bHasAmmo) - { - return FALSE; - } - - return TRUE; -} - -/* -===================== -CBasePlayerWeapon :: DefaultDeploy - -===================== -*/ -BOOL CBasePlayerWeapon :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal, int body ) -{ - if ( !CanDeploy() ) - return FALSE; - - gEngfuncs.CL_LoadModel( szViewModel, &m_pPlayer->pev->viewmodel ); - - SendWeaponAnim( iAnim, skiplocal, body ); - - m_pPlayer->m_flNextAttack = this->GetDeployTime(); - m_flTimeWeaponIdle = this->GetDeployTime() + kDeployIdleInterval; - return TRUE; -} - -/* -===================== -CBasePlayerWeapon :: PlayEmptySound - -===================== -*/ -BOOL CBasePlayerWeapon :: PlayEmptySound( void ) -{ - if (m_iPlayEmptySound) - { - HUD_PlaySound( "weapons/357_cock1.wav", 0.8 ); - m_iPlayEmptySound = 0; - return 0; - } - return 0; -} - -/* -===================== -CBasePlayerWeapon :: ResetEmptySound - -===================== -*/ -void CBasePlayerWeapon :: ResetEmptySound( void ) -{ - m_iPlayEmptySound = 1; -} - -/* -===================== -CBasePlayerWeapon::Holster - -Put away weapon -===================== -*/ -void CBasePlayerWeapon::Holster( int skiplocal /* = 0 */ ) -{ - m_fInReload = FALSE; // cancel any reload in progress. - m_pPlayer->pev->viewmodel = 0; -} - -/* -===================== -CBasePlayerWeapon::SendWeaponAnim - -Animate weapon model -===================== -*/ -void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body ) -{ - m_pPlayer->pev->weaponanim = iAnim; - - HUD_SendWeaponAnim( iAnim, body, 0 ); -} - -/* -===================== -CBaseEntity::FireBulletsPlayer - -Only produces random numbers to match the server ones. -===================== -*/ -Vector CBaseEntity::FireBulletsPlayer ( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int shared_rand ) -{ - //float x, y, z; - Vector theShotDirection; - - theShotDirection.x = theShotDirection.y = theShotDirection.z = 0; - -// for ( ULONG iShot = 1; iShot <= cShots; iShot++ ) -// { -// if ( pevAttacker == NULL ) -// { -// // get circular gaussian spread -// do { -// x = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5); -// y = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5); -// z = x*x+y*y; -// } while (z > 1); -// } -// else -// { -// //Use player's random seed. -// // get circular gaussian spread -// x = UTIL_SharedRandomFloat( shared_rand + iShot, -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 1 + iShot ) , -0.5, 0.5 ); -// y = UTIL_SharedRandomFloat( shared_rand + ( 2 + iShot ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 3 + iShot ), -0.5, 0.5 ); -// z = x * x + y * y; -// } -// -// UTIL_GetRandomSpreadDir(shared_rand, iShot, vecDirShooting) -// } - -// return Vector ( (float)(x * vecSpread.x), (float)(y * vecSpread.y), 0.0f ); - return theShotDirection; -} - - -bool GetCanUseWeapon() -{ - // This mirrors the functionality of AvHPlayer::GetCanUseWeapon. - return !gHUD.GetIsInTopDownMode() && !gHUD.GetIsBeingDigested() && !gHUD.GetIsEnsnared() && !gHUD.GetIsStunned() && gEngfuncs.GetViewModel() != NULL; -} - -/* -===================== -CBasePlayerWeapon::ItemPostFrame - -Handles weapon firing, reloading, etc. -===================== -*/ -void CBasePlayerWeapon::ItemPostFrame( void ) -{ - - if ((m_fInReload) && (m_pPlayer->m_flNextAttack <= 0.0)) - { -/////////////////////////////////////////////////////////////////////////////////////////////////////// -// Put code in here to predict reloads (ie, have the ammo on screen update before we get a response) // -/////////////////////////////////////////////////////////////////////////////////////////////////////// -//#if 0 // FIXME, need ammo on client to make this work right -// // complete the reload. -// int j = min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); -// -// // Add them to the clip -// m_iClip += j; -// m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; -//#else -// m_iClip += 10; -//#endif - m_fInReload = FALSE; - } - - if ((m_pPlayer->pev->button & IN_ATTACK2) && (m_flNextSecondaryAttack <= 0.0)) - { - if (GetCanUseWeapon()) - { - if ( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] ) - { - m_fFireOnEmpty = TRUE; - } - - SecondaryAttack(); - m_pPlayer->pev->button &= ~IN_ATTACK2; - } - } - else if ( (m_pPlayer->pev->button & IN_ATTACK) && (m_flNextPrimaryAttack <= 0.0) ) - { - if (GetCanUseWeapon()) - { - if ( (m_iClip == 0 && pszAmmo1()) || - (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) ) - { - m_fFireOnEmpty = TRUE; - } - - //#ifdef AVH_CLIENT - //if((m_iClip == 0) && ? - //#endif - - PrimaryAttack(); - } - } - else if ( m_pPlayer->pev->button & IN_RELOAD && iMaxClip() != WEAPON_NOCLIP && !m_fInReload ) - { - if (GetCanUseWeapon()) - { - // reload when reload is pressed, or if no buttons are down and weapon is empty. - Reload(); - } - } - else if ( !(m_pPlayer->pev->button & (IN_ATTACK|IN_ATTACK2) ) ) - { - if (GetCanUseWeapon()) - { - - // no fire buttons down - - m_fFireOnEmpty = FALSE; - - // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing - if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < 0.0 ) - { - // << CGC >> Only reload if we have more ammo to reload with - if(m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0) - { - Reload(); - return; - } - } - - WeaponIdle( ); - } - return; - } - - // catch all - if ( ShouldWeaponIdle() ) - { - WeaponIdle(); - } -} - -/* -===================== -CBasePlayer::SelectItem - - Switch weapons -===================== -*/ -void CBasePlayer::SelectItem(const char *pstr) -{ - if (!pstr) - return; - - CBasePlayerItem *pItem = NULL; - - if (!pItem) - return; - - - if (pItem == m_pActiveItem) - return; - - if (m_pActiveItem) - m_pActiveItem->Holster( ); - - m_pLastItem = m_pActiveItem; - m_pActiveItem = pItem; - - if (m_pActiveItem) - { - m_pActiveItem->Deploy( ); - } -} - -/* -===================== -CBasePlayer::SelectLastItem - -===================== -*/ -void CBasePlayer::SelectLastItem(void) -{ - if (!m_pLastItem) - { - return; - } - - if ( m_pActiveItem && !m_pActiveItem->CanHolster() ) - { - return; - } - - if (m_pActiveItem) - m_pActiveItem->Holster( ); - - CBasePlayerItem *pTemp = m_pActiveItem; - m_pActiveItem = m_pLastItem; - m_pLastItem = pTemp; - m_pActiveItem->Deploy( ); -} - -/* -===================== -CBasePlayer::Killed - -===================== -*/ -void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) -{ - // Holster weapon immediately, to allow it to cleanup - if ( m_pActiveItem ) - m_pActiveItem->Holster( ); -} - -/* -===================== -CBasePlayer::Spawn - -===================== -*/ -void CBasePlayer::Spawn( void ) -{ - if (m_pActiveItem) - m_pActiveItem->Deploy( ); -} - -/* -===================== -UTIL_TraceLine - -Don't actually trace, but act like the trace didn't hit anything. -===================== -*/ -void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr ) -{ - memset( ptr, 0, sizeof( *ptr ) ); - ptr->flFraction = 1.0; -} - -/* -===================== -UTIL_ParticleBox - -For debugging, draw a box around a player made out of particles -===================== -*/ -void UTIL_ParticleBox( CBasePlayer *player, float *mins, float *maxs, float life, unsigned char r, unsigned char g, unsigned char b ) -{ - int i; - vec3_t mmin, mmax; - - for ( i = 0; i < 3; i++ ) - { - mmin[ i ] = player->pev->origin[ i ] + mins[ i ]; - mmax[ i ] = player->pev->origin[ i ] + maxs[ i ]; - } - - gEngfuncs.pEfxAPI->R_ParticleBox( (float *)&mmin, (float *)&mmax, 5.0, 0, 255, 0 ); -} - -/* -===================== -UTIL_ParticleBoxes - -For debugging, draw boxes for other collidable players -===================== -*/ -void UTIL_ParticleBoxes( void ) -{ - int idx; - physent_t *pe; - cl_entity_t *player; - vec3_t mins, maxs; - - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - - // Store off the old count - gEngfuncs.pEventAPI->EV_PushPMStates(); - - player = gEngfuncs.GetLocalPlayer(); - // Now add in all of the players. - gEngfuncs.pEventAPI->EV_SetSolidPlayers ( player->index - 1 ); - - for ( idx = 1; idx < 100; idx++ ) - { - pe = gEngfuncs.pEventAPI->EV_GetPhysent( idx ); - if ( !pe ) - break; - - if ( pe->info >= 1 && pe->info <= gEngfuncs.GetMaxClients() ) - { - mins = pe->origin + pe->mins; - maxs = pe->origin + pe->maxs; - - gEngfuncs.pEfxAPI->R_ParticleBox( (float *)&mins, (float *)&maxs, 0, 0, 255, 2.0 ); - } - } - - gEngfuncs.pEventAPI->EV_PopPMStates(); -} - -/* -===================== -UTIL_ParticleLine - -For debugging, draw a line made out of particles -===================== -*/ -void UTIL_ParticleLine( CBasePlayer *player, float *start, float *end, float life, unsigned char r, unsigned char g, unsigned char b ) -{ - gEngfuncs.pEfxAPI->R_ParticleLine( start, end, r, g, b, life ); -} - -/* -===================== -CBasePlayerWeapon::PrintState - -For debugging, print out state variables to log file -===================== -*/ -void CBasePlayerWeapon::PrintState( void ) -{ - COM_Log( "c:\\hl.log", "%.4f ", gpGlobals->time ); - COM_Log( "c:\\hl.log", "%.4f ", m_pPlayer->m_flNextAttack ); - COM_Log( "c:\\hl.log", "%.4f ", m_flNextPrimaryAttack ); - COM_Log( "c:\\hl.log", "%.4f ", m_flTimeWeaponIdle - gpGlobals->time); - COM_Log( "c:\\hl.log", "%i ", m_iClip ); - COM_Log( "c:\\hl.log", "\r\n"); -} - -/* -===================== -HUD_InitClientWeapons - -Set up weapons, player and functions needed to run weapons code client-side. -===================== -*/ -void HUD_InitClientWeapons( void ) -{ - static int initialized = 0; - if ( initialized ) - return; - - initialized = 1; - - // Set up pointer ( dummy object ) - gpGlobals = &Globals; - - // Fill in current time ( probably not needed ) - gpGlobals->time = gEngfuncs.GetClientTime(); - - // Fake functions - g_engfuncs.pfnPrecacheModel = stub_PrecacheModel; - g_engfuncs.pfnPrecacheSound = stub_PrecacheSound; - g_engfuncs.pfnPrecacheEvent = stub_PrecacheEvent; - g_engfuncs.pfnNameForFunction = stub_NameForFunction; - g_engfuncs.pfnSetModel = stub_SetModel; - g_engfuncs.pfnSetClientMaxspeed = HUD_SetMaxSpeed; - - // Handled locally - g_engfuncs.pfnPlaybackEvent = HUD_PlaybackEvent; - g_engfuncs.pfnAlertMessage = AlertMessage; - - // Pass through to engine - g_engfuncs.pfnPrecacheEvent = gEngfuncs.pfnPrecacheEvent; - g_engfuncs.pfnRandomFloat = gEngfuncs.pfnRandomFloat; - g_engfuncs.pfnRandomLong = gEngfuncs.pfnRandomLong; - - // Allocate a slot for the local player - HUD_PrepEntity( &player , NULL ); - - - // Allocate slot(s) for each weapon that we are going to be predicting - //HUD_PrepEntity( &g_Glock, &player ); - HUD_PrepEntity( &gKnife, &player); - HUD_PrepEntity( &gMachineGun, &player); - HUD_PrepEntity( &gPistol, &player); - HUD_PrepEntity( &gSonicGun, &player); - HUD_PrepEntity( &gHeavyMachineGun, &player); - HUD_PrepEntity( &gGrenadeGun, &player); - HUD_PrepEntity( &gGrenade, &player); - HUD_PrepEntity( &gWelder, &player); - HUD_PrepEntity( &gMine, &player); - HUD_PrepEntity( &gSpitGun, &player); - HUD_PrepEntity( &gClaws, &player); - HUD_PrepEntity( &gSpores, &player); - HUD_PrepEntity( &gSpikeGun, &player); - HUD_PrepEntity( &gBite, &player); - HUD_PrepEntity( &gBite2, &player); - HUD_PrepEntity( &gSwipe, &player); - HUD_PrepEntity( &gWebSpinner, &player); - HUD_PrepEntity( &gPrimalScream, &player); - //HUD_PrepEntity( &gParalysisGun, &player); - - HUD_PrepEntity( &gBlink, &player); - HUD_PrepEntity( &gParasite, &player); - HUD_PrepEntity( &gUmbra, &player); - HUD_PrepEntity( &gDivineWind, &player); - HUD_PrepEntity( &gBileBomb, &player); - HUD_PrepEntity( &gAcidRocket, &player); - HUD_PrepEntity( &gHealingSpray, &player); - HUD_PrepEntity( &gMetabolize, &player); - HUD_PrepEntity( &gStomp, &player); - HUD_PrepEntity( &gDevour, &player); - - HUD_PrepEntity( &gLeap, &player); - HUD_PrepEntity( &gCharge, &player); - - gJetpackEventID = PRECACHE_EVENT(1, kJetpackEvent); - //gWallJumpEventID = PRECACHE_EVENT(1, kWallJumpEvent); - //gFlightEventID = PRECACHE_EVENT(1, kFlightEvent); - gTeleportEventID = PRECACHE_EVENT(1, kTeleportEvent); - gPhaseInEventID = PRECACHE_EVENT(1, kPhaseInEvent); - gSiegeHitEventID = PRECACHE_EVENT(1, kSiegeHitEvent); - gSiegeViewHitEventID = PRECACHE_EVENT(1, kSiegeViewHitEvent); - gCommanderPointsAwardedEventID = PRECACHE_EVENT(1, kCommanderPointsAwardedEvent); - gBlinkEffectSuccessEventID = PRECACHE_EVENT(1, kBlinkEffectSuccessEventName); -} - -/* -===================== -HUD_GetLastOrg - -Retruns the last position that we stored for egon beam endpoint. -===================== -*/ -void HUD_GetLastOrg( float *org ) -{ - int i; - - // Return last origin - for ( i = 0; i < 3; i++ ) - { - org[i] = previousorigin[i]; - } -} - -/* -===================== -HUD_SetLastOrg - -Remember our exact predicted origin so we can draw the egon to the right position. -===================== -*/ -void HUD_SetLastOrg( void ) -{ - int i; - - // Offset final origin by view_offset - for ( i = 0; i < 3; i++ ) - { - previousorigin[i] = g_finalstate->playerstate.origin[i] + g_finalstate->client.view_ofs[ i ]; - } -} - - -CBasePlayerWeapon* HUD_GetWeaponForID(int inID) -{ - CBasePlayerWeapon* pWeapon = NULL; - - switch(inID) - { -// case WEAPON_GLOCK: -// pWeapon = &g_Glock; -// break; - case AVH_WEAPON_KNIFE: - pWeapon = &gKnife; - break; - case AVH_WEAPON_MG: - pWeapon = &gMachineGun; - break; - case AVH_WEAPON_PISTOL: - pWeapon = &gPistol; - break; - case AVH_WEAPON_SONIC: - pWeapon = &gSonicGun; - break; - case AVH_WEAPON_HMG: - pWeapon = &gHeavyMachineGun; - break; - case AVH_WEAPON_GRENADE_GUN: - pWeapon = &gGrenadeGun; - break; - case AVH_WEAPON_GRENADE: - pWeapon = &gGrenade; - break; - case AVH_WEAPON_WELDER: - pWeapon = &gWelder; - break; - case AVH_WEAPON_MINE: - pWeapon = &gMine; - break; - case AVH_WEAPON_SPIT: - pWeapon = &gSpitGun; - break; - case AVH_WEAPON_CLAWS: - pWeapon = &gClaws; - break; - case AVH_WEAPON_SPORES: - pWeapon = &gSpores; - break; - case AVH_WEAPON_SPIKE: - pWeapon = &gSpikeGun; - break; - case AVH_WEAPON_BITE: - pWeapon = &gBite; - break; - case AVH_WEAPON_BITE2: - pWeapon = &gBite2; - break; - case AVH_WEAPON_SWIPE: - pWeapon = &gSwipe; - break; - case AVH_WEAPON_WEBSPINNER: - pWeapon = &gWebSpinner; - break; - case AVH_WEAPON_PRIMALSCREAM: - pWeapon = &gPrimalScream; - break; - case AVH_WEAPON_PARASITE: - pWeapon = &gParasite; - break; - case AVH_WEAPON_UMBRA: - pWeapon = &gUmbra; - break; - case AVH_WEAPON_BLINK: - pWeapon = &gBlink; - break; - case AVH_WEAPON_DIVINEWIND: - pWeapon = &gDivineWind; - break; -// case AVH_WEAPON_PARALYSIS: -// pWeapon = &gParalysisGun; -// break; - case AVH_WEAPON_BILEBOMB: - pWeapon = &gBileBomb; - break; - case AVH_WEAPON_ACIDROCKET: - pWeapon = &gAcidRocket; - break; - case AVH_WEAPON_HEALINGSPRAY: - pWeapon = &gHealingSpray; - break; - case AVH_WEAPON_METABOLIZE: - pWeapon = &gMetabolize; - break; - case AVH_WEAPON_STOMP: - pWeapon = &gStomp; - break; - case AVH_WEAPON_DEVOUR: - pWeapon = &gDevour; - break; - - // Abilities - case AVH_ABILITY_LEAP: - pWeapon = &gLeap; - break; - case AVH_ABILITY_CHARGE: - pWeapon = &gCharge; - break; - } - - return pWeapon; -} - - -bool HUD_GetWeaponEnabled(int inID) -{ - ASSERT(inID >= 0); - ASSERT(inID < 32); - - // puzl: 497 - use the enabled state in the associated WEAPON instead of the CBasePlayerWeapon's iuser3 - bool theWeaponEnabled = false; - CBasePlayerWeapon* theWeapon = g_pWpns[inID]; - if(theWeapon) - { - ItemInfo theItemInfo; - theWeapon->GetItemInfo(&theItemInfo); - WEAPON *pWeapon = gWR.GetWeapon( theItemInfo.iId ); - if ( pWeapon != 0 ) { - theWeaponEnabled = (pWeapon->iEnabled == 1); - } - } - - return theWeaponEnabled; -} - -/* -===================== -HUD_WeaponsPostThink - -Run Weapon firing code on client -===================== -*/ -void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cmd, double time, unsigned int random_seed ) -{ - int i; - int buttonsChanged; - CBasePlayerWeapon* pCurrent = NULL; - weapon_data_t nulldata, *pfrom, *pto; - static int lasthealth; - - memset( &nulldata, 0, sizeof( nulldata ) ); - - HUD_InitClientWeapons(); - - // Get current clock - gpGlobals->time = time; - - // Fill in data based on selected weapon - // FIXME, make this a method in each weapon? where you pass in an entity_state_t *? - // Store pointer to our destination entity_state_t so we can get our origin, etc. from it - CBasePlayerWeapon* pWeapon = HUD_GetWeaponForID(from->client.m_iId); - - // for setting up events on the client - g_finalstate = to; - - // If we are running events/etc. go ahead and see if we - // managed to die between last frame and this one - // If so, run the appropriate player killed or spawn function - if ( g_runfuncs ) - { - if ( to->client.health <= 0 && lasthealth > 0 ) - { - player.Killed( NULL, 0 ); - - } - else if ( to->client.health > 0 && lasthealth <= 0 ) - { - player.Spawn(); - } - - lasthealth = to->client.health; - } - - // We are not predicting the current weapon, just bow out here. - if ( !pWeapon ) - return; - - for ( i = 0; i < 32; i++ ) - { - pCurrent = g_pWpns[ i ]; - if ( !pCurrent ) - { - continue; - } - - pfrom = &from->weapondata[ i ]; - - pCurrent->m_fInReload = pfrom->m_fInReload; - pCurrent->m_fInSpecialReload = pfrom->m_fInSpecialReload; -// pCurrent->m_flPumpTime = pfrom->m_flPumpTime; - pCurrent->m_iClip = pfrom->m_iClip; - pCurrent->m_flNextPrimaryAttack = pfrom->m_flNextPrimaryAttack; - pCurrent->m_flNextSecondaryAttack = pfrom->m_flNextSecondaryAttack; - pCurrent->m_flTimeWeaponIdle = pfrom->m_flTimeWeaponIdle; - if(pWeapon && (pWeapon->m_iId == pfrom->m_iId)) - { - // Predict clip - gHUD.m_Ammo.SetCurrentClip(pfrom->m_iClip); - - AvHBasePlayerWeapon* theWeapon = dynamic_cast(pWeapon); - if(theWeapon) - { - gHUD.SetCurrentWeaponData(pWeapon->m_iId, theWeapon->GetEnabledState()); - } - - //gHUD.SetClientDebugCSP(pfrom, from->client.m_flNextAttack); - } - - // Tell HUD what energy level is needed to use weapon, so alien HUD can indicate this - float theEnergyLevel = 0.0f; - AvHMUGetEnergyCost((AvHWeaponID)(pWeapon->m_iId), theEnergyLevel); - gHUD.SetCurrentUseableEnergyLevel(theEnergyLevel); - -// New SDK stuff...needed? -// pCurrent->pev->fuser1 = pfrom->fuser1; - pCurrent->m_flStartThrow = pfrom->fuser2; - pCurrent->m_flReleaseThrow = pfrom->fuser3; -// pCurrent->m_chargeReady = pfrom->iuser1; -// pCurrent->m_fInAttack = pfrom->iuser2; - pCurrent->pev->iuser3 = pfrom->iuser3; - -// pCurrent->m_iSecondaryAmmoType = (int)from->client.vuser3[2]; - pCurrent->m_iPrimaryAmmoType = (int)from->client.vuser4[0]; -// player.m_rgAmmo[ pCurrent->m_iPrimaryAmmoType ] = (int)from->client.vuser4[1]; -// player.m_rgAmmo[ pCurrent->m_iSecondaryAmmoType ] = (int)from->client.vuser4[2]; - } - - // For random weapon events, use this seed to seed random # generator - player.random_seed = random_seed; - - // Get old buttons from previous state. - player.m_afButtonLast = from->playerstate.oldbuttons; - - // Which buttsons chave changed - buttonsChanged = (player.m_afButtonLast ^ cmd->buttons); // These buttons have changed this frame - - // Debounced button codes for pressed/released - // The changed ones still down are "pressed" - player.m_afButtonPressed = buttonsChanged & cmd->buttons; - // The ones not down are "released" - player.m_afButtonReleased = buttonsChanged & (~cmd->buttons); - - // Set player variables that weapons code might check/alter - player.pev->button = cmd->buttons; - - player.pev->velocity = from->client.velocity; - player.pev->flags = from->client.flags; - - player.pev->deadflag = from->client.deadflag; - player.pev->waterlevel = from->client.waterlevel; - player.pev->maxspeed = from->client.maxspeed; - player.pev->fov = from->client.fov; - player.pev->weaponanim = from->client.weaponanim; - player.pev->viewmodel = from->client.viewmodel; - player.m_flNextAttack = from->client.m_flNextAttack; - //player.m_flNextAmmoBurn = from->client.fuser2; - //player.m_flAmmoStartCharge = from->client.fuser3; - - // Removed this because NS uses vuser1 and vuser2 (and the HL weapons aren't used) - ////Stores all our ammo info, so the client side weapons can use them. - //player.ammo_9mm = (int)from->client.vuser1[0]; - //player.ammo_357 = (int)from->client.vuser1[1]; - //player.ammo_argrens = (int)from->client.vuser1[2]; - //player.ammo_bolts = (int)from->client.ammo_nails; //is an int anyways... - //player.ammo_buckshot = (int)from->client.ammo_shells; - //player.ammo_uranium = (int)from->client.ammo_cells; - //player.ammo_hornets = (int)from->client.vuser2[0]; - //player.ammo_rockets = (int)from->client.ammo_rockets; - - - // Point to current weapon object - if ( from->client.m_iId ) - { - player.m_pActiveItem = g_pWpns[ from->client.m_iId ]; - } - - if ( player.m_pActiveItem->m_iId == WEAPON_RPG ) - { - ( ( CRpg * )player.m_pActiveItem)->m_fSpotActive = (int)from->client.vuser2[ 1 ]; - ( ( CRpg * )player.m_pActiveItem)->m_cActiveRockets = (int)from->client.vuser2[ 2 ]; - } - - // Don't go firing anything if we have died. - // Or if we don't have a weapon model deployed - if ( ( player.pev->deadflag != ( DEAD_DISCARDBODY + 1 ) ) && !CL_IsDead() && player.pev->viewmodel && !g_iUser1 ) - { - - if ( player.m_flNextAttack <= 0 ) - { - pWeapon->ItemPostFrame(); - } -// if ( g_runfuncs ) -// { -// pWeapon->PrintState(); -// } - } - - // Assume that we are not going to switch weapons - to->client.m_iId = from->client.m_iId; - - // Now see if we issued a changeweapon command ( and we're not dead ) - if ( cmd->weaponselect && ( player.pev->deadflag != ( DEAD_DISCARDBODY + 1 ) ) ) - { - // Switched to a different weapon? - if ( from->weapondata[ cmd->weaponselect ].m_iId == cmd->weaponselect ) - { - ASSERT(cmd->weaponselect >= 0); - ASSERT(cmd->weaponselect < 32); - - CBasePlayerWeapon *pNew = g_pWpns[ cmd->weaponselect ]; - if ( pNew && ( pNew != pWeapon ) && player.m_pActiveItem && player.m_pActiveItem->CanHolster()) - { - // Put away old weapon - if (player.m_pActiveItem) - player.m_pActiveItem->Holster( ); - - player.m_pLastItem = player.m_pActiveItem; - player.m_pActiveItem = pNew; - - // Deploy new weapon - if (player.m_pActiveItem) - { - player.m_pActiveItem->Deploy( ); - } - - // Update weapon id so we can predict things correctly. - to->client.m_iId = cmd->weaponselect; - } - } - } - - // Copy in results of prediction code - to->client.viewmodel = player.pev->viewmodel; - to->client.fov = player.pev->fov; - to->client.weaponanim = player.pev->weaponanim; - to->client.m_flNextAttack = player.m_flNextAttack; - //to->client.fuser2 = player.m_flNextAmmoBurn; - //to->client.fuser3 = player.m_flAmmoStartCharge; - to->client.maxspeed = player.pev->maxspeed; - -// Removed this because NS uses vuser1 and vuser2 (and the HL weapons aren't used) -// //HL Weapons -// to->client.vuser1[0] = player.ammo_9mm; -// to->client.vuser1[1] = player.ammo_357; -// to->client.vuser1[2] = player.ammo_argrens; -// -// to->client.ammo_nails = player.ammo_bolts; -// to->client.ammo_shells = player.ammo_buckshot; -// to->client.ammo_cells = player.ammo_uranium; -// to->client.vuser2[0] = player.ammo_hornets; -// to->client.ammo_rockets = player.ammo_rockets; - - if ( player.m_pActiveItem->m_iId == WEAPON_RPG ) - { - from->client.vuser2[ 1 ] = ( ( CRpg * )player.m_pActiveItem)->m_fSpotActive; - from->client.vuser2[ 2 ] = ( ( CRpg * )player.m_pActiveItem)->m_cActiveRockets; - } - - // Make sure that weapon animation matches what the game .dll is telling us - // over the wire ( fixes some animation glitches ) - if ( g_runfuncs && ( HUD_GetWeaponAnim() != to->client.weaponanim ) ) - { - int body = 2; - - //Pop the model to body 0. - //if ( pWeapon == &g_Tripmine ) - // body = 0; - - // Force a fixed anim down to viewmodel - HUD_SendWeaponAnim( to->client.weaponanim, body, 1 ); - } - - for ( i = 0; i < 32; i++ ) - { - pCurrent = g_pWpns[ i ]; - - pto = &to->weapondata[ i ]; - - if ( !pCurrent ) - { - memset( pto, 0, sizeof( weapon_data_t ) ); - continue; - } - - pto->m_fInReload = pCurrent->m_fInReload; - pto->m_fInSpecialReload = pCurrent->m_fInSpecialReload; -// pto->m_flPumpTime = pCurrent->m_flPumpTime; - pto->m_iClip = pCurrent->m_iClip; - pto->m_flNextPrimaryAttack = pCurrent->m_flNextPrimaryAttack; - pto->m_flNextSecondaryAttack = pCurrent->m_flNextSecondaryAttack; - pto->m_flTimeWeaponIdle = pCurrent->m_flTimeWeaponIdle; -// pto->fuser1 = pCurrent->pev->fuser1; -// pto->fuser2 = pCurrent->m_flStartThrow; -// pto->fuser3 = pCurrent->m_flReleaseThrow; -// pto->iuser1 = pCurrent->m_chargeReady; -// pto->iuser2 = pCurrent->m_fInAttack; - pto->iuser3 = pCurrent->pev->iuser3; - - // Decrement weapon counters, server does this at same time ( during post think, after doing everything else ) - pto->m_flNextReload -= cmd->msec / 1000.0; - pto->m_fNextAimBonus -= cmd->msec / 1000.0; - pto->m_flNextPrimaryAttack -= cmd->msec / 1000.0; - pto->m_flNextSecondaryAttack -= cmd->msec / 1000.0; - pto->m_flTimeWeaponIdle -= cmd->msec / 1000.0; - pto->fuser1 -= cmd->msec / 1000.0; - - to->client.vuser3[2] = pCurrent->m_iSecondaryAmmoType; - to->client.vuser4 = pCurrent->pev->vuser4; -// to->client.vuser4[0] = pCurrent->m_iPrimaryAmmoType; -// to->client.vuser4[1] = player.m_rgAmmo[ pCurrent->m_iPrimaryAmmoType ]; -// to->client.vuser4[2] = player.m_rgAmmo[ pCurrent->m_iSecondaryAmmoType ]; - -/* if ( pto->m_flPumpTime != -9999 ) - { - pto->m_flPumpTime -= cmd->msec / 1000.0; - if ( pto->m_flPumpTime < -0.001 ) - pto->m_flPumpTime = -0.001; - }*/ - - if ( pto->m_fNextAimBonus < -1.0 ) - { - pto->m_fNextAimBonus = -1.0; - } - - if ( pto->m_flNextPrimaryAttack < -1.0 ) - { - pto->m_flNextPrimaryAttack = -1.0; - } - - if ( pto->m_flNextSecondaryAttack < -0.001 ) - { - pto->m_flNextSecondaryAttack = -0.001; - } - - if ( pto->m_flTimeWeaponIdle < -0.001 ) - { - pto->m_flTimeWeaponIdle = -0.001; - } - - if ( pto->m_flNextReload < -0.001 ) - { - pto->m_flNextReload = -0.001; - } - - if ( pto->fuser1 < -0.001 ) - { - pto->fuser1 = -0.001; - } - } - - // m_flNextAttack is now part of the weapons, but is part of the player instead - to->client.m_flNextAttack -= cmd->msec / 1000.0; - if ( to->client.m_flNextAttack < -0.001 ) - { - to->client.m_flNextAttack = -0.001; - } - - to->client.fuser2 -= cmd->msec / 1000.0; - if ( to->client.fuser2 < -0.001 ) - { - to->client.fuser2 = -0.001; - } - - to->client.fuser3 -= cmd->msec / 1000.0; - if ( to->client.fuser3 < -0.001 ) - { - to->client.fuser3 = -0.001; - } - - // Store off the last position from the predicted state. - HUD_SetLastOrg(); - - // Wipe it so we can't use it after this frame - g_finalstate = NULL; -} - - -/* -===================== -HUD_PostRunCmd - -Client calls this during prediction, after it has moved the player and updated any info changed into to-> -time is the current client clock based on prediction -cmd is the command that caused the movement, etc -runfuncs is 1 if this is the first time we've predicted this command. If so, sounds and effects should play, otherwise, they should -be ignored -===================== -*/ -void CL_DLLEXPORT HUD_PostRunCmd( struct local_state_s *from, struct local_state_s *to, struct usercmd_s *cmd, int runfuncs, double time, unsigned int random_seed ) -{ - RecClPostRunCmd(from, to, cmd, runfuncs, time, random_seed); - - g_runfuncs = runfuncs; - - if ( cl_lw && cl_lw->value ) - { - HUD_WeaponsPostThink( from, to, cmd, time, random_seed ); - } - else - { - to->client.fov = g_lastFOV; - } - - // Check to see whether too play local jetpack effects - if(runfuncs) - { - static sLastTime = 0; - float theTimePassed = time - sLastTime; - - //CheckJetpack(); - - //UpdateJetpackLights(); - - sLastTime = time; - } - - // All games can use FOV state - g_lastFOV = to->client.fov; -} - - - +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +* Modified by Charlie Cleveland: +* +* $Workfile: hl_weapons.cpp $ +* $Date: 2002/10/24 21:11:52 $ +* +* ------------------------------------------------------------------------------- +* $Log: hl_weapons.cpp,v $ +* Revision 1.31 2002/10/24 21:11:52 Flayra +* - Updated jetpack effect because it was really buggy this old way +* +* Revision 1.30 2002/10/16 02:12:10 Flayra +* - Valve anti-cheat integrated! +* +* Revision 1.29 2002/10/16 00:36:40 Flayra +* - Removed blink fail +* +* Revision 1.28 2002/08/31 18:01:59 Flayra +* - Work at VALVe +* +* Revision 1.27 2002/07/08 16:09:03 Flayra +* - Removed unneeded code here, so random numbers were generated properly on both client and server +* +* Revision 1.26 2002/07/01 20:56:31 Flayra +* - Added babbler gun +* +* Revision 1.25 2002/06/25 17:03:01 Flayra +* - Removed old weapons, added new weapons, fixed mines, iuser3 enables and disables weapons +* +* =============================================================================== +****/ +#include "../hud.h" +#include "../cl_util.h" +#include "common/const.h" +#include "common/entity_state.h" +#include "common/cl_entity.h" +#include "../demo.h" +#include "common/usercmd.h" +#include "common/event_flags.h" +#include "common/event_api.h" +#include "cl_dll/com_weapons.h" +#include "cl_dll/ammo.h" +#include "cl_dll/ammohistory.h" +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "dlls/cbase.h" +#include "dlls/monsters.h" +#include "dlls/weapons.h" +#include "dlls/nodes.h" +#include "dlls/player.h" +#include "mod/AvHEvents.h" + +#include "common/usercmd.h" +#include "common/entity_state.h" +#include "common/demo_api.h" +#include "pm_shared/pm_defs.h" +#include "common/event_api.h" +#include "common/r_efx.h" + +#include "../hud_iface.h" +#include "../com_weapons.h" + +#include "mod/AvHMarineWeapons.h" +#include "mod/AvHSpecials.h" +#include "mod/AvHAlienWeapons.h" +#include "mod/AvHAlienAbilities.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHAlienAbilityConstants.h" +#include "mod/AvHMovementUtil.h" + +#include "engine/APIProxy.h" +#include "cl_dll/Exports.h" + +extern globalvars_t *gpGlobals; +extern bool gIsJetpacking; + +// Pool of client side entities/entvars_t +static entvars_t ev[ 32 ]; +static int num_ents = 0; + +// The entity we'll use to represent the local client +static CBasePlayer player; + +// Local version of game .dll global variables ( time, etc. ) +static globalvars_t Globals; + +static CBasePlayerWeapon *g_pWpns[ 32 ]; + +bool CheckInAttack2(void); + +vec3_t previousorigin; + +// HLDM Weapon placeholder entities. +//CGlock g_Glock; + +// NS weapons +AvHKnife gKnife; +AvHMachineGun gMachineGun; +AvHPistol gPistol; +AvHSonicGun gSonicGun; +AvHHeavyMachineGun gHeavyMachineGun; +AvHGrenadeGun gGrenadeGun; +AvHGrenade gGrenade; +AvHWelder gWelder; +AvHMine gMine; +AvHSpitGun gSpitGun; +AvHClaws gClaws; +AvHSpore gSpores; +AvHBite gBite; +AvHBite2 gBite2; +AvHSpikeGun gSpikeGun; +AvHSwipe gSwipe; +AvHWebSpinner gWebSpinner; +AvHPrimalScream gPrimalScream; +AvHParasiteGun gParasite; +AvHUmbraGun gUmbra; +AvHBlinkGun gBlink; +AvHDivineWind gDivineWind; +//AvHParalysisGun gParalysisGun; +AvHBileBombGun gBileBomb; +AvHAcidRocketGun gAcidRocket; +AvHHealingSpray gHealingSpray; +AvHMetabolize gMetabolize; +AvHDevour gDevour; +AvHStomp gStomp; + +// Alien abilities +AvHLeap gLeap; +AvHCharge gCharge; + +// Jetpack events +int gJetpackEventID; +//int gWallJumpEventID; +//int gFlightEventID; +int gTeleportEventID; +int gPhaseInEventID; +int gSiegeHitEventID; +int gSiegeViewHitEventID; +int gCommanderPointsAwardedEventID; +int gBlinkEffectSuccessEventID; + +//bool gPlayingJetpack = false; +//CGlock g_Glock; +//CCrowbar g_Crowbar; +//CPython g_Python; +//CMP5 g_Mp5; +//CCrossbow g_Crossbow; +//CShotgun g_Shotgun; +//CRpg g_Rpg; +//CGauss g_Gauss; +//CEgon g_Egon; +//CHgun g_HGun; +//CHandGrenade g_HandGren; +//CSatchel g_Satchel; +//CTripmine g_Tripmine; +//CSqueak g_Snark; + +/* +====================== +AlertMessage + +Print debug messages to console +====================== +*/ +void AlertMessage( ALERT_TYPE atype, char *szFmt, ... ) +{ + va_list argptr; + static char string[1024]; + + va_start (argptr, szFmt); +#ifdef WIN32 + //overflow protection in MS version of function... + _vsnprintf( string, 1023, szFmt, argptr ); +#else + vsprintf (string, szFmt,argptr); +#endif + va_end (argptr); + + gEngfuncs.Con_Printf( "cl: " ); + gEngfuncs.Con_Printf( string ); +} + +// Client-side effects for jetpack +void CheckJetpack() +{ + // if local player is jetpacking, play effects immediately +// if(gIsJetpacking && !gPlayingJetpack) +// { +// cl_entity_t* thePlayer; +// thePlayer = gEngfuncs.GetLocalPlayer(); +// ASSERT(thePlayer); +// +// // Play event locally, server will tell everyone else to play event +// gEngfuncs.pEventAPI->EV_PlaybackEvent(0, NULL, gJetpackEventID, 0, thePlayer->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); +// +// //gPlayingJetpack = true; +// } + // Check to turn it off too (in case there's a network anomaly or the game resets or something, just trying to be safe) + //else if(!gIsJetpacking && (gPlayingJetpack)) + //{ + // gEngfuncs.pEventAPI->EV_PlaybackEvent(0, NULL, gEndJetpackEventID, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + // + // gPlayingJetpack = false; + //} +} + +//Returns if it's multiplayer. +//Mostly used by the client side weapons. +bool bIsMultiplayer ( void ) +{ + return gEngfuncs.GetMaxClients() == 1 ? 0 : 1; +} +//Just loads a v_ model. +void LoadVModel ( char *szViewModel, CBasePlayer *m_pPlayer ) +{ + gEngfuncs.CL_LoadModel( szViewModel, &m_pPlayer->pev->viewmodel ); +} + +/* +===================== +HUD_PrepEntity + +Links the raw entity to an entvars_s holder. If a player is passed in as the owner, then +we set up the m_pPlayer field. +===================== +*/ +void HUD_PrepEntity( CBaseEntity *pEntity, CBasePlayer *pWeaponOwner ) +{ + typedef vector IDListType; + static IDListType sIDList; + + memset( &ev[ num_ents ], 0, sizeof( entvars_t ) ); + pEntity->pev = &ev[ num_ents++ ]; + + pEntity->Precache(); + pEntity->Spawn(); + + if ( pWeaponOwner ) + { + ItemInfo info; + + ((CBasePlayerWeapon *)pEntity)->m_pPlayer = pWeaponOwner; + + ((CBasePlayerWeapon *)pEntity)->GetItemInfo( &info ); + + // ASSERT that a weapon with this id isn't in the list + int theNewID = info.iId; + IDListType::iterator theIter = std::find(sIDList.begin(), sIDList.end(), theNewID); + ASSERT(theIter == sIDList.end()); + + // Insert id into our list + sIDList.push_back(theNewID); + + g_pWpns[ theNewID ] = (CBasePlayerWeapon *)pEntity; + } +} + +/* +===================== +CBaseEntity :: Killed + +If weapons code "kills" an entity, just set its effects to EF_NODRAW +===================== +*/ +void CBaseEntity :: Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->effects |= EF_NODRAW; +} + +/* +===================== +CBasePlayerWeapon :: DefaultReload +===================== +*/ +BOOL CBasePlayerWeapon :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body ) +{ + + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + return FALSE; + + int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); + + if (j == 0) + return FALSE; + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay; + + //!!UNDONE -- reload sound goes here !!! + //SendWeaponAnim( iAnim, UseDecrement(), body ); + + m_fInReload = TRUE; + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + kDeployIdleInterval; + return TRUE; +} + +/* +===================== +CBasePlayerWeapon :: CanDeploy +===================== +*/ +BOOL CBasePlayerWeapon :: CanDeploy( void ) +{ + BOOL bHasAmmo = 0; + + if ( !pszAmmo1() ) + { + // this weapon doesn't use ammo, can always deploy. + return TRUE; + } + + if ( pszAmmo1() ) + { + bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] != 0); + } + if ( pszAmmo2() ) + { + bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] != 0); + } + if (m_iClip > 0) + { + bHasAmmo |= 1; + } + if (!bHasAmmo) + { + return FALSE; + } + + return TRUE; +} + +/* +===================== +CBasePlayerWeapon :: DefaultDeploy + +===================== +*/ +BOOL CBasePlayerWeapon :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal, int body ) +{ + if ( !CanDeploy() ) + return FALSE; + + gEngfuncs.CL_LoadModel( szViewModel, &m_pPlayer->pev->viewmodel ); + + SendWeaponAnim( iAnim, skiplocal, body ); + + m_pPlayer->m_flNextAttack = this->GetDeployTime(); + m_flTimeWeaponIdle = this->GetDeployTime() + kDeployIdleInterval; + return TRUE; +} + +/* +===================== +CBasePlayerWeapon :: PlayEmptySound + +===================== +*/ +BOOL CBasePlayerWeapon :: PlayEmptySound( void ) +{ + if (m_iPlayEmptySound) + { + HUD_PlaySound( "weapons/357_cock1.wav", 0.8 ); + m_iPlayEmptySound = 0; + return 0; + } + return 0; +} + +/* +===================== +CBasePlayerWeapon :: ResetEmptySound + +===================== +*/ +void CBasePlayerWeapon :: ResetEmptySound( void ) +{ + m_iPlayEmptySound = 1; +} + +/* +===================== +CBasePlayerWeapon::Holster + +Put away weapon +===================== +*/ +void CBasePlayerWeapon::Holster( int skiplocal /* = 0 */ ) +{ + m_fInReload = FALSE; // cancel any reload in progress. + m_pPlayer->pev->viewmodel = 0; +} + +/* +===================== +CBasePlayerWeapon::SendWeaponAnim + +Animate weapon model +===================== +*/ +void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body ) +{ + m_pPlayer->pev->weaponanim = iAnim; + + HUD_SendWeaponAnim( iAnim, body, 0 ); +} + +/* +===================== +CBaseEntity::FireBulletsPlayer + +Only produces random numbers to match the server ones. +===================== +*/ +Vector CBaseEntity::FireBulletsPlayer ( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int shared_rand ) +{ + //float x, y, z; + Vector theShotDirection; + + theShotDirection.x = theShotDirection.y = theShotDirection.z = 0; + +// for ( ULONG iShot = 1; iShot <= cShots; iShot++ ) +// { +// if ( pevAttacker == NULL ) +// { +// // get circular gaussian spread +// do { +// x = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5); +// y = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5); +// z = x*x+y*y; +// } while (z > 1); +// } +// else +// { +// //Use player's random seed. +// // get circular gaussian spread +// x = UTIL_SharedRandomFloat( shared_rand + iShot, -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 1 + iShot ) , -0.5, 0.5 ); +// y = UTIL_SharedRandomFloat( shared_rand + ( 2 + iShot ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 3 + iShot ), -0.5, 0.5 ); +// z = x * x + y * y; +// } +// +// UTIL_GetRandomSpreadDir(shared_rand, iShot, vecDirShooting) +// } + +// return Vector ( (float)(x * vecSpread.x), (float)(y * vecSpread.y), 0.0f ); + return theShotDirection; +} + + +bool GetCanUseWeapon() +{ + // This mirrors the functionality of AvHPlayer::GetCanUseWeapon. + return !gHUD.GetIsInTopDownMode() && !gHUD.GetIsBeingDigested() && !gHUD.GetIsEnsnared() && !gHUD.GetIsStunned() && gEngfuncs.GetViewModel() != NULL; +} + +/* +===================== +CBasePlayerWeapon::ItemPostFrame + +Handles weapon firing, reloading, etc. +===================== +*/ +void CBasePlayerWeapon::ItemPostFrame( void ) +{ + // Hack initialization + if (this->m_flLastAnimationPlayed >= 3.0f * BALANCE_VAR(kLeapROF) + gpGlobals->time) + this->m_flLastAnimationPlayed = 0.0f; + + if ((m_fInReload) && (m_pPlayer->m_flNextAttack <= 0.0)) + { +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// Put code in here to predict reloads (ie, have the ammo on screen update before we get a response) // +/////////////////////////////////////////////////////////////////////////////////////////////////////// +//#if 0 // FIXME, need ammo on client to make this work right +// // complete the reload. +// int j = min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); +// +// // Add them to the clip +// m_iClip += j; +// m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; +//#else +// m_iClip += 10; +//#endif + m_fInReload = FALSE; + } + + // Properly propagate the end animation + if (this->PrevAttack2Status == true && !(m_pPlayer->pev->button & IN_ATTACK2)) + { + switch (gHUD.GetCurrentWeaponID()) + { + case AVH_WEAPON_SWIPE: + this->SendWeaponAnim(12); + break; + case AVH_WEAPON_ACIDROCKET: + this->SendWeaponAnim(8); + break; + case AVH_WEAPON_CLAWS: + this->SendWeaponAnim(9); + break; + case AVH_WEAPON_STOMP: + this->SendWeaponAnim(8); + break; + case AVH_WEAPON_DEVOUR: + this->SendWeaponAnim(11); + break; + } + } + + if ( (m_pPlayer->pev->button & IN_ATTACK) && !(m_pPlayer->pev->button & IN_ATTACK2) && (m_flNextPrimaryAttack <= 0.0) ) + { + if (GetCanUseWeapon()) + { + if ( (m_iClip == 0 && pszAmmo1()) || + (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) ) + { + m_fFireOnEmpty = TRUE; + } + + + if ((gHUD.GetHUDUser3() == AVH_USER3_ALIEN_PLAYER1) + && (gHUD.GetCurrentWeaponID() == AVH_ABILITY_LEAP) + && (this->m_flLastAnimationPlayed + (float)BALANCE_VAR(kLeapROF) <= gpGlobals->time)) + { + // : 0001151 predict energy too + AvHAlienWeapon* theWeapon = dynamic_cast(g_pWpns[AVH_ABILITY_LEAP]); + if ( theWeapon && theWeapon->IsUseable() ) { + float theVolumeScalar = 1.0f; + cl_entity_t *player = gEngfuncs.GetLocalPlayer(); + int theSilenceLevel = AvHGetAlienUpgradeLevel(player->curstate.iuser4, MASK_UPGRADE_6); + switch(theSilenceLevel) + { + case 1: + theVolumeScalar = (float)BALANCE_VAR(kSilenceLevel1Volume); + break; + case 2: + theVolumeScalar = (float)BALANCE_VAR(kSilenceLevel2Volume); + break; + case 3: + theVolumeScalar = (float)BALANCE_VAR(kSilenceLevel3Volume); + break; + } + HUD_PlaySound( kLeapSound, theVolumeScalar); + AvHMUDeductAlienEnergy(m_pPlayer->pev->fuser3, theWeapon->GetEnergyForAttack() ); + gEngfuncs.pEventAPI->EV_WeaponAnimation(3, 2); + this->m_flLastAnimationPlayed = gpGlobals->time; + } + } + //#ifdef AVH_CLIENT + //if((m_iClip == 0) && ? + //#endif + PrimaryAttack(); + //return; + } + } + // +movement: Rewritten to allow us to use +attack2 for movement abilities + else if ((m_pPlayer->pev->button & IN_ATTACK2) && (gHUD.GetIsAlien())) + { + //m_flNextSecondaryAttack + // Find out what kind of special movement we are using, and execute the animation for it + if (this->PrevAttack2Status == false) + { + bool enabled=false; + // : 0001151 predict energy too + AvHAlienWeapon *theWeapon = dynamic_cast(g_pWpns[AVH_ABILITY_LEAP]); + if ( theWeapon ) + enabled=theWeapon->IsUseable(); + + switch (gHUD.GetHUDUser3()) + { + case AVH_USER3_ALIEN_PLAYER1: + + if (enabled && (this->m_flLastAnimationPlayed + (float)BALANCE_VAR(kLeapROF) <= gpGlobals->time)) + { + float theVolumeScalar = 1.0f; + cl_entity_t *player = gEngfuncs.GetLocalPlayer(); + int theSilenceLevel = AvHGetAlienUpgradeLevel(player->curstate.iuser4, MASK_UPGRADE_6); + switch(theSilenceLevel) + { + case 1: + theVolumeScalar = (float)BALANCE_VAR(kSilenceLevel1Volume); + break; + case 2: + theVolumeScalar = (float)BALANCE_VAR(kSilenceLevel2Volume); + break; + case 3: + theVolumeScalar = (float)BALANCE_VAR(kSilenceLevel3Volume); + break; + } + HUD_PlaySound( kLeapSound, theVolumeScalar); + AvHMUDeductAlienEnergy(m_pPlayer->pev->fuser3, theWeapon->GetEnergyForAttack() ); + this->SendWeaponAnim(3); + this->m_flLastAnimationPlayed = gpGlobals->time; + } + break; + case AVH_USER3_ALIEN_PLAYER4: + switch (gHUD.GetCurrentWeaponID()) + { + case AVH_WEAPON_SWIPE: + this->SendWeaponAnim(9); + break; + case AVH_WEAPON_ACIDROCKET: + this->SendWeaponAnim(11); + break; + } + break; + case AVH_USER3_ALIEN_PLAYER5: + switch (gHUD.GetCurrentWeaponID()) + { + case AVH_WEAPON_CLAWS: + this->SendWeaponAnim(5); + break; + case AVH_WEAPON_DEVOUR: + this->SendWeaponAnim(18); + break; + case AVH_WEAPON_STOMP: + this->SendWeaponAnim(15); + break; + } + break; + } + } + + if ((gHUD.GetHUDUser3() == AVH_USER3_ALIEN_PLAYER1) + && (this->m_flLastAnimationPlayed + BALANCE_VAR(kLeapROF) < gpGlobals->time)) + this->PrevAttack2Status = false; + else + this->PrevAttack2Status = true; + + return; +// if (GetCanUseWeapon()) +// { +// PrimaryAttack(); +// } + } + else if ( m_pPlayer->pev->button & IN_RELOAD && iMaxClip() != WEAPON_NOCLIP && !m_fInReload ) + { + if (GetCanUseWeapon()) + { + // reload when reload is pressed, or if no buttons are down and weapon is empty. + Reload(); + } + } + // +movement: Removed case for +attack2 + else if ( !(m_pPlayer->pev->button & (IN_ATTACK /*|IN_ATTACK2 */) ) ) + { + if (GetCanUseWeapon()) + { + + // no fire buttons down + + m_fFireOnEmpty = FALSE; + + // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < 0.0 ) + { + // << CGC >> Only reload if we have more ammo to reload with + if(m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0) + { + Reload(); + return; + } + } + + WeaponIdle( ); + } + this->PrevAttack2Status = false; + return; + } + + this->PrevAttack2Status = false; + + // catch all + if ( ShouldWeaponIdle() ) + { + WeaponIdle(); + } +} + +/* +===================== +CBasePlayer::SelectItem + + Switch weapons +===================== +*/ +void CBasePlayer::SelectItem(const char *pstr) +{ + if (!pstr) + return; + + CBasePlayerItem *pItem = NULL; + + if (!pItem) + return; + + + if (pItem == m_pActiveItem) + return; + + if (m_pActiveItem) + m_pActiveItem->Holster( ); + + m_pLastItem = m_pActiveItem; + m_pActiveItem = pItem; + + if (m_pActiveItem) + { + m_pActiveItem->Deploy( ); + } +} + +/* +===================== +CBasePlayer::SelectLastItem + +===================== +*/ +void CBasePlayer::SelectLastItem(void) +{ + if (!m_pLastItem) + { + return; + } + + if ( m_pActiveItem && !m_pActiveItem->CanHolster() ) + { + return; + } + + if (m_pActiveItem) + m_pActiveItem->Holster( ); + + CBasePlayerItem *pTemp = m_pActiveItem; + m_pActiveItem = m_pLastItem; + m_pLastItem = pTemp; + m_pActiveItem->Deploy( ); +} + +/* +===================== +CBasePlayer::Killed + +===================== +*/ +void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) +{ + // Holster weapon immediately, to allow it to cleanup + if ( m_pActiveItem ) + m_pActiveItem->Holster( ); +} + +/* +===================== +CBasePlayer::Spawn + +===================== +*/ +void CBasePlayer::Spawn( void ) +{ + if (m_pActiveItem) + m_pActiveItem->Deploy( ); + +// this->m_flLastAnimationPlayed = gpGlobals->time; +} + +/* +===================== +UTIL_TraceLine + +Don't actually trace, but act like the trace didn't hit anything. +===================== +*/ +void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr ) +{ + memset( ptr, 0, sizeof( *ptr ) ); + ptr->flFraction = 1.0; +} + +/* +===================== +UTIL_ParticleBox + +For debugging, draw a box around a player made out of particles +===================== +*/ +void UTIL_ParticleBox( CBasePlayer *player, float *mins, float *maxs, float life, unsigned char r, unsigned char g, unsigned char b ) +{ + int i; + vec3_t mmin, mmax; + + for ( i = 0; i < 3; i++ ) + { + mmin[ i ] = player->pev->origin[ i ] + mins[ i ]; + mmax[ i ] = player->pev->origin[ i ] + maxs[ i ]; + } + + gEngfuncs.pEfxAPI->R_ParticleBox( (float *)&mmin, (float *)&mmax, 5.0, 0, 255, 0 ); +} + +/* +===================== +UTIL_ParticleBoxes + +For debugging, draw boxes for other collidable players +===================== +*/ +void UTIL_ParticleBoxes( void ) +{ + int idx; + physent_t *pe; + cl_entity_t *player; + vec3_t mins, maxs; + + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + + // Store off the old count + gEngfuncs.pEventAPI->EV_PushPMStates(); + + player = gEngfuncs.GetLocalPlayer(); + // Now add in all of the players. + gEngfuncs.pEventAPI->EV_SetSolidPlayers ( player->index - 1 ); + + for ( idx = 1; idx < 100; idx++ ) + { + pe = gEngfuncs.pEventAPI->EV_GetPhysent( idx ); + if ( !pe ) + break; + + if ( pe->info >= 1 && pe->info <= gEngfuncs.GetMaxClients() ) + { + mins = pe->origin + pe->mins; + maxs = pe->origin + pe->maxs; + + gEngfuncs.pEfxAPI->R_ParticleBox( (float *)&mins, (float *)&maxs, 0, 0, 255, 2.0 ); + } + } + + gEngfuncs.pEventAPI->EV_PopPMStates(); +} + +/* +===================== +UTIL_ParticleLine + +For debugging, draw a line made out of particles +===================== +*/ +void UTIL_ParticleLine( CBasePlayer *player, float *start, float *end, float life, unsigned char r, unsigned char g, unsigned char b ) +{ + gEngfuncs.pEfxAPI->R_ParticleLine( start, end, r, g, b, life ); +} + +/* +===================== +CBasePlayerWeapon::PrintState + +For debugging, print out state variables to log file +===================== +*/ +void CBasePlayerWeapon::PrintState( void ) +{ + COM_Log( "c:\\hl.log", "%.4f ", gpGlobals->time ); + COM_Log( "c:\\hl.log", "%.4f ", m_pPlayer->m_flNextAttack ); + COM_Log( "c:\\hl.log", "%.4f ", m_flNextPrimaryAttack ); + COM_Log( "c:\\hl.log", "%.4f ", m_flTimeWeaponIdle - gpGlobals->time); + COM_Log( "c:\\hl.log", "%i ", m_iClip ); + COM_Log( "c:\\hl.log", "\r\n"); +} + +/* +===================== +HUD_InitClientWeapons + +Set up weapons, player and functions needed to run weapons code client-side. +===================== +*/ +void HUD_InitClientWeapons( void ) +{ + static int initialized = 0; + if ( initialized ) + return; + + initialized = 1; + + // Set up pointer ( dummy object ) + gpGlobals = &Globals; + + // Fill in current time ( probably not needed ) + gpGlobals->time = gEngfuncs.GetClientTime(); + + // Fake functions + g_engfuncs.pfnPrecacheModel = stub_PrecacheModel; + g_engfuncs.pfnPrecacheSound = stub_PrecacheSound; + g_engfuncs.pfnPrecacheEvent = stub_PrecacheEvent; + g_engfuncs.pfnNameForFunction = stub_NameForFunction; + g_engfuncs.pfnSetModel = stub_SetModel; + g_engfuncs.pfnSetClientMaxspeed = HUD_SetMaxSpeed; + + // Handled locally + g_engfuncs.pfnPlaybackEvent = HUD_PlaybackEvent; + g_engfuncs.pfnAlertMessage = AlertMessage; + + // Pass through to engine + g_engfuncs.pfnPrecacheEvent = gEngfuncs.pfnPrecacheEvent; + g_engfuncs.pfnRandomFloat = gEngfuncs.pfnRandomFloat; + g_engfuncs.pfnRandomLong = gEngfuncs.pfnRandomLong; + + // Allocate a slot for the local player + HUD_PrepEntity( &player , NULL ); + + + // Allocate slot(s) for each weapon that we are going to be predicting + //HUD_PrepEntity( &g_Glock, &player ); + HUD_PrepEntity( &gKnife, &player); + HUD_PrepEntity( &gMachineGun, &player); + HUD_PrepEntity( &gPistol, &player); + HUD_PrepEntity( &gSonicGun, &player); + HUD_PrepEntity( &gHeavyMachineGun, &player); + HUD_PrepEntity( &gGrenadeGun, &player); + HUD_PrepEntity( &gGrenade, &player); + HUD_PrepEntity( &gWelder, &player); + HUD_PrepEntity( &gMine, &player); + HUD_PrepEntity( &gSpitGun, &player); + HUD_PrepEntity( &gClaws, &player); + HUD_PrepEntity( &gSpores, &player); + HUD_PrepEntity( &gSpikeGun, &player); + HUD_PrepEntity( &gBite, &player); + HUD_PrepEntity( &gBite2, &player); + HUD_PrepEntity( &gSwipe, &player); + HUD_PrepEntity( &gWebSpinner, &player); + HUD_PrepEntity( &gPrimalScream, &player); + //HUD_PrepEntity( &gParalysisGun, &player); + + HUD_PrepEntity( &gBlink, &player); + HUD_PrepEntity( &gParasite, &player); + HUD_PrepEntity( &gUmbra, &player); + HUD_PrepEntity( &gDivineWind, &player); + HUD_PrepEntity( &gBileBomb, &player); + HUD_PrepEntity( &gAcidRocket, &player); + HUD_PrepEntity( &gHealingSpray, &player); + HUD_PrepEntity( &gMetabolize, &player); + HUD_PrepEntity( &gStomp, &player); + HUD_PrepEntity( &gDevour, &player); + + HUD_PrepEntity( &gLeap, &player); + HUD_PrepEntity( &gCharge, &player); + + gJetpackEventID = PRECACHE_EVENT(1, kJetpackEvent); + //gWallJumpEventID = PRECACHE_EVENT(1, kWallJumpEvent); + //gFlightEventID = PRECACHE_EVENT(1, kFlightEvent); + gTeleportEventID = PRECACHE_EVENT(1, kTeleportEvent); + gPhaseInEventID = PRECACHE_EVENT(1, kPhaseInEvent); + gSiegeHitEventID = PRECACHE_EVENT(1, kSiegeHitEvent); + gSiegeViewHitEventID = PRECACHE_EVENT(1, kSiegeViewHitEvent); + gCommanderPointsAwardedEventID = PRECACHE_EVENT(1, kCommanderPointsAwardedEvent); + gBlinkEffectSuccessEventID = PRECACHE_EVENT(1, kBlinkEffectSuccessEventName); +} + +/* +===================== +HUD_GetLastOrg + +Retruns the last position that we stored for egon beam endpoint. +===================== +*/ +void HUD_GetLastOrg( float *org ) +{ + int i; + + // Return last origin + for ( i = 0; i < 3; i++ ) + { + org[i] = previousorigin[i]; + } +} + +/* +===================== +HUD_SetLastOrg + +Remember our exact predicted origin so we can draw the egon to the right position. +===================== +*/ +void HUD_SetLastOrg( void ) +{ + int i; + + // Offset final origin by view_offset + for ( i = 0; i < 3; i++ ) + { + previousorigin[i] = g_finalstate->playerstate.origin[i] + g_finalstate->client.view_ofs[ i ]; + } +} + + +CBasePlayerWeapon* HUD_GetWeaponForID(int inID) +{ + CBasePlayerWeapon* pWeapon = NULL; + + switch(inID) + { +// case WEAPON_GLOCK: +// pWeapon = &g_Glock; +// break; + case AVH_WEAPON_KNIFE: + pWeapon = &gKnife; + break; + case AVH_WEAPON_MG: + pWeapon = &gMachineGun; + break; + case AVH_WEAPON_PISTOL: + pWeapon = &gPistol; + break; + case AVH_WEAPON_SONIC: + pWeapon = &gSonicGun; + break; + case AVH_WEAPON_HMG: + pWeapon = &gHeavyMachineGun; + break; + case AVH_WEAPON_GRENADE_GUN: + pWeapon = &gGrenadeGun; + break; + case AVH_WEAPON_GRENADE: + pWeapon = &gGrenade; + break; + case AVH_WEAPON_WELDER: + pWeapon = &gWelder; + break; + case AVH_WEAPON_MINE: + pWeapon = &gMine; + break; + case AVH_WEAPON_SPIT: + pWeapon = &gSpitGun; + break; + case AVH_WEAPON_CLAWS: + pWeapon = &gClaws; + break; + case AVH_WEAPON_SPORES: + pWeapon = &gSpores; + break; + case AVH_WEAPON_SPIKE: + pWeapon = &gSpikeGun; + break; + case AVH_WEAPON_BITE: + pWeapon = &gBite; + break; + case AVH_WEAPON_BITE2: + pWeapon = &gBite2; + break; + case AVH_WEAPON_SWIPE: + pWeapon = &gSwipe; + break; + case AVH_WEAPON_WEBSPINNER: + pWeapon = &gWebSpinner; + break; + case AVH_WEAPON_PRIMALSCREAM: + pWeapon = &gPrimalScream; + break; + case AVH_WEAPON_PARASITE: + pWeapon = &gParasite; + break; + case AVH_WEAPON_UMBRA: + pWeapon = &gUmbra; + break; + case AVH_WEAPON_BLINK: + pWeapon = &gBlink; + break; + case AVH_WEAPON_DIVINEWIND: + pWeapon = &gDivineWind; + break; +// case AVH_WEAPON_PARALYSIS: +// pWeapon = &gParalysisGun; +// break; + case AVH_WEAPON_BILEBOMB: + pWeapon = &gBileBomb; + break; + case AVH_WEAPON_ACIDROCKET: + pWeapon = &gAcidRocket; + break; + case AVH_WEAPON_HEALINGSPRAY: + pWeapon = &gHealingSpray; + break; + case AVH_WEAPON_METABOLIZE: + pWeapon = &gMetabolize; + break; + case AVH_WEAPON_STOMP: + pWeapon = &gStomp; + break; + case AVH_WEAPON_DEVOUR: + pWeapon = &gDevour; + break; + + // Abilities + case AVH_ABILITY_LEAP: + pWeapon = &gLeap; + break; + case AVH_ABILITY_CHARGE: + pWeapon = &gCharge; + break; + } + + return pWeapon; +} + + +bool HUD_GetWeaponEnabled(int inID) +{ + ASSERT(inID >= 0); + ASSERT(inID < 32); + + // : 497 - use the enabled state in the associated WEAPON instead of the CBasePlayerWeapon's iuser3 + bool theWeaponEnabled = false; + CBasePlayerWeapon* theWeapon = g_pWpns[inID]; + if(theWeapon) + { + ItemInfo theItemInfo; + theWeapon->GetItemInfo(&theItemInfo); + WEAPON *pWeapon = gWR.GetWeapon( theItemInfo.iId ); + if ( pWeapon != 0 ) { + theWeaponEnabled = (pWeapon->iEnabled == 1); + } + } + + return theWeaponEnabled; +} + +/* +===================== +HUD_WeaponsPostThink + +Run Weapon firing code on client +===================== +*/ +void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cmd, double time, unsigned int random_seed ) +{ + int i; + int buttonsChanged; + CBasePlayerWeapon* pCurrent = NULL; + weapon_data_t nulldata, *pfrom, *pto; + static int lasthealth; + + memset( &nulldata, 0, sizeof( nulldata ) ); + + HUD_InitClientWeapons(); + + // Get current clock + gpGlobals->time = time; + + // Fill in data based on selected weapon + // FIXME, make this a method in each weapon? where you pass in an entity_state_t *? + // Store pointer to our destination entity_state_t so we can get our origin, etc. from it + CBasePlayerWeapon* pWeapon = HUD_GetWeaponForID(from->client.m_iId); + + // for setting up events on the client + g_finalstate = to; + + // If we are running events/etc. go ahead and see if we + // managed to die between last frame and this one + // If so, run the appropriate player killed or spawn function + if ( g_runfuncs ) + { + if ( to->client.health <= 0 && lasthealth > 0 ) + { + player.Killed( NULL, 0 ); + + } + else if ( to->client.health > 0 && lasthealth <= 0 ) + { + player.Spawn(); + } + + lasthealth = to->client.health; + } + + // We are not predicting the current weapon, just bow out here. + if ( !pWeapon ) + return; + + for ( i = 0; i < 32; i++ ) + { + pCurrent = g_pWpns[ i ]; + if ( !pCurrent ) + { + continue; + } + + pfrom = &from->weapondata[ i ]; + + pCurrent->m_fInReload = pfrom->m_fInReload; + pCurrent->m_fInSpecialReload = pfrom->m_fInSpecialReload; +// pCurrent->m_flPumpTime = pfrom->m_flPumpTime; + pCurrent->m_iClip = pfrom->m_iClip; + pCurrent->m_flNextPrimaryAttack = pfrom->m_flNextPrimaryAttack; + pCurrent->m_flNextSecondaryAttack = pfrom->m_flNextSecondaryAttack; + pCurrent->m_flTimeWeaponIdle = pfrom->m_flTimeWeaponIdle; + if(pWeapon && (pWeapon->m_iId == pfrom->m_iId)) + { + // Predict clip + gHUD.m_Ammo.SetCurrentClip(pfrom->m_iClip); + + AvHBasePlayerWeapon* theWeapon = dynamic_cast(pWeapon); + if(theWeapon) + { + gHUD.SetCurrentWeaponData(pWeapon->m_iId, theWeapon->GetEnabledState()); + } + + //gHUD.SetClientDebugCSP(pfrom, from->client.m_flNextAttack); + } + + // Tell HUD what energy level is needed to use weapon, so alien HUD can indicate this + float theEnergyLevel = 0.0f; + AvHMUGetEnergyCost((AvHWeaponID)(pWeapon->m_iId), theEnergyLevel); + gHUD.SetCurrentUseableEnergyLevel(theEnergyLevel); + +// New SDK stuff...needed? +// pCurrent->pev->fuser1 = pfrom->fuser1; + pCurrent->m_flStartThrow = pfrom->fuser2; + pCurrent->m_flReleaseThrow = pfrom->fuser3; +// pCurrent->m_chargeReady = pfrom->iuser1; +// pCurrent->m_fInAttack = pfrom->iuser2; + pCurrent->pev->iuser3 = pfrom->iuser3; + +// pCurrent->m_iSecondaryAmmoType = (int)from->client.vuser3[2]; + pCurrent->m_iPrimaryAmmoType = (int)from->client.vuser4[0]; +// player.m_rgAmmo[ pCurrent->m_iPrimaryAmmoType ] = (int)from->client.vuser4[1]; +// player.m_rgAmmo[ pCurrent->m_iSecondaryAmmoType ] = (int)from->client.vuser4[2]; + } + + // For random weapon events, use this seed to seed random # generator + player.random_seed = random_seed; + + // Get old buttons from previous state. + player.m_afButtonLast = from->playerstate.oldbuttons; + + // Which buttsons chave changed + buttonsChanged = (player.m_afButtonLast ^ cmd->buttons); // These buttons have changed this frame + + // Debounced button codes for pressed/released + // The changed ones still down are "pressed" + player.m_afButtonPressed = buttonsChanged & cmd->buttons; + // The ones not down are "released" + player.m_afButtonReleased = buttonsChanged & (~cmd->buttons); + + // Set player variables that weapons code might check/alter + player.pev->button = cmd->buttons; + + player.pev->velocity = from->client.velocity; + player.pev->flags = from->client.flags; + + player.pev->deadflag = from->client.deadflag; + player.pev->waterlevel = from->client.waterlevel; + player.pev->maxspeed = from->client.maxspeed; + player.pev->fov = from->client.fov; + player.pev->weaponanim = from->client.weaponanim; + player.pev->viewmodel = from->client.viewmodel; + player.m_flNextAttack = from->client.m_flNextAttack; + //player.m_flNextAmmoBurn = from->client.fuser2; + //player.m_flAmmoStartCharge = from->client.fuser3; + + // Removed this because NS uses vuser1 and vuser2 (and the HL weapons aren't used) + ////Stores all our ammo info, so the client side weapons can use them. + //player.ammo_9mm = (int)from->client.vuser1[0]; + //player.ammo_357 = (int)from->client.vuser1[1]; + //player.ammo_argrens = (int)from->client.vuser1[2]; + //player.ammo_bolts = (int)from->client.ammo_nails; //is an int anyways... + //player.ammo_buckshot = (int)from->client.ammo_shells; + //player.ammo_uranium = (int)from->client.ammo_cells; + //player.ammo_hornets = (int)from->client.vuser2[0]; + //player.ammo_rockets = (int)from->client.ammo_rockets; + + + // Point to current weapon object + if ( from->client.m_iId ) + { + player.m_pActiveItem = g_pWpns[ from->client.m_iId ]; + } + + if ( player.m_pActiveItem->m_iId == WEAPON_RPG ) + { + ( ( CRpg * )player.m_pActiveItem)->m_fSpotActive = (int)from->client.vuser2[ 1 ]; + ( ( CRpg * )player.m_pActiveItem)->m_cActiveRockets = (int)from->client.vuser2[ 2 ]; + } + + // Don't go firing anything if we have died. + // Or if we don't have a weapon model deployed + if ( ( player.pev->deadflag != ( DEAD_DISCARDBODY + 1 ) ) && !CL_IsDead() && player.pev->viewmodel && !g_iUser1 ) + { + + if ( player.m_flNextAttack <= 0 ) + { + pWeapon->ItemPostFrame(); + } +// if ( g_runfuncs ) +// { +// pWeapon->PrintState(); +// } + } + + // Assume that we are not going to switch weapons + to->client.m_iId = from->client.m_iId; + + // Now see if we issued a changeweapon command ( and we're not dead ) + if ( cmd->weaponselect && ( player.pev->deadflag != ( DEAD_DISCARDBODY + 1 ) ) ) + { + // Switched to a different weapon? + if ( from->weapondata[ cmd->weaponselect ].m_iId == cmd->weaponselect ) + { + ASSERT(cmd->weaponselect >= 0); + ASSERT(cmd->weaponselect < 32); + + CBasePlayerWeapon *pNew = g_pWpns[ cmd->weaponselect ]; + if ( pNew && ( pNew != pWeapon ) && player.m_pActiveItem && player.m_pActiveItem->CanHolster()) + { + // Put away old weapon + if (player.m_pActiveItem) + player.m_pActiveItem->Holster( ); + + player.m_pLastItem = player.m_pActiveItem; + player.m_pActiveItem = pNew; + + // Deploy new weapon + if (player.m_pActiveItem) + { + player.m_pActiveItem->Deploy( ); + } + + // Update weapon id so we can predict things correctly. + to->client.m_iId = cmd->weaponselect; + } + } + } + + // Copy in results of prediction code + to->client.viewmodel = player.pev->viewmodel; + to->client.fov = player.pev->fov; + to->client.weaponanim = player.pev->weaponanim; + to->client.m_flNextAttack = player.m_flNextAttack; + //to->client.fuser2 = player.m_flNextAmmoBurn; + //to->client.fuser3 = player.m_flAmmoStartCharge; + to->client.maxspeed = player.pev->maxspeed; + +// Removed this because NS uses vuser1 and vuser2 (and the HL weapons aren't used) +// //HL Weapons +// to->client.vuser1[0] = player.ammo_9mm; +// to->client.vuser1[1] = player.ammo_357; +// to->client.vuser1[2] = player.ammo_argrens; +// +// to->client.ammo_nails = player.ammo_bolts; +// to->client.ammo_shells = player.ammo_buckshot; +// to->client.ammo_cells = player.ammo_uranium; +// to->client.vuser2[0] = player.ammo_hornets; +// to->client.ammo_rockets = player.ammo_rockets; + + if ( player.m_pActiveItem->m_iId == WEAPON_RPG ) + { + from->client.vuser2[ 1 ] = ( ( CRpg * )player.m_pActiveItem)->m_fSpotActive; + from->client.vuser2[ 2 ] = ( ( CRpg * )player.m_pActiveItem)->m_cActiveRockets; + } + + // Make sure that weapon animation matches what the game .dll is telling us + // over the wire ( fixes some animation glitches ) + // Ensure that the fade and onos won't get these, to play the blink and charge animations correctly + bool noRun = (to->client.iuser3 == AVH_USER3_ALIEN_PLAYER4) || (to->client.iuser3 == AVH_USER3_ALIEN_PLAYER5); + if (g_runfuncs && ( HUD_GetWeaponAnim() != to->client.weaponanim ) && !(CheckInAttack2()) && !noRun) + { + int body = 2; + + //Pop the model to body 0. + //if ( pWeapon == &g_Tripmine ) + // body = 0; + + // Force a fixed anim down to viewmodel + HUD_SendWeaponAnim( to->client.weaponanim, body, 1 ); + } + + for ( i = 0; i < 32; i++ ) + { + pCurrent = g_pWpns[ i ]; + + pto = &to->weapondata[ i ]; + + if ( !pCurrent ) + { + memset( pto, 0, sizeof( weapon_data_t ) ); + continue; + } + + pto->m_fInReload = pCurrent->m_fInReload; + pto->m_fInSpecialReload = pCurrent->m_fInSpecialReload; +// pto->m_flPumpTime = pCurrent->m_flPumpTime; + pto->m_iClip = pCurrent->m_iClip; + pto->m_flNextPrimaryAttack = pCurrent->m_flNextPrimaryAttack; + pto->m_flNextSecondaryAttack = pCurrent->m_flNextSecondaryAttack; + pto->m_flTimeWeaponIdle = pCurrent->m_flTimeWeaponIdle; +// pto->fuser1 = pCurrent->pev->fuser1; +// pto->fuser2 = pCurrent->m_flStartThrow; +// pto->fuser3 = pCurrent->m_flReleaseThrow; +// pto->iuser1 = pCurrent->m_chargeReady; +// pto->iuser2 = pCurrent->m_fInAttack; + pto->iuser3 = pCurrent->pev->iuser3; + + // Decrement weapon counters, server does this at same time ( during post think, after doing everything else ) + pto->m_flNextReload -= cmd->msec / 1000.0; + pto->m_fNextAimBonus -= cmd->msec / 1000.0; + pto->m_flNextPrimaryAttack -= cmd->msec / 1000.0; + pto->m_flNextSecondaryAttack -= cmd->msec / 1000.0; + pto->m_flTimeWeaponIdle -= cmd->msec / 1000.0; + pto->fuser1 -= cmd->msec / 1000.0; + + to->client.vuser3[2] = pCurrent->m_iSecondaryAmmoType; + to->client.vuser4 = pCurrent->pev->vuser4; +// to->client.vuser4[0] = pCurrent->m_iPrimaryAmmoType; +// to->client.vuser4[1] = player.m_rgAmmo[ pCurrent->m_iPrimaryAmmoType ]; +// to->client.vuser4[2] = player.m_rgAmmo[ pCurrent->m_iSecondaryAmmoType ]; + +/* if ( pto->m_flPumpTime != -9999 ) + { + pto->m_flPumpTime -= cmd->msec / 1000.0; + if ( pto->m_flPumpTime < -0.001 ) + pto->m_flPumpTime = -0.001; + }*/ + + if ( pto->m_fNextAimBonus < -1.0 ) + { + pto->m_fNextAimBonus = -1.0; + } + + if ( pto->m_flNextPrimaryAttack < -1.0 ) + { + pto->m_flNextPrimaryAttack = -1.0; + } + + if ( pto->m_flNextSecondaryAttack < -0.001 ) + { + pto->m_flNextSecondaryAttack = -0.001; + } + + if ( pto->m_flTimeWeaponIdle < -0.001 ) + { + pto->m_flTimeWeaponIdle = -0.001; + } + + if ( pto->m_flNextReload < -0.001 ) + { + pto->m_flNextReload = -0.001; + } + + if ( pto->fuser1 < -0.001 ) + { + pto->fuser1 = -0.001; + } + } + + // m_flNextAttack is now part of the weapons, but is part of the player instead + to->client.m_flNextAttack -= cmd->msec / 1000.0; + if ( to->client.m_flNextAttack < -0.001 ) + { + to->client.m_flNextAttack = -0.001; + } + + to->client.fuser2 -= cmd->msec / 1000.0; + if ( to->client.fuser2 < -0.001 ) + { + to->client.fuser2 = -0.001; + } + + to->client.fuser3 -= cmd->msec / 1000.0; + if ( to->client.fuser3 < -0.001 ) + { + to->client.fuser3 = -0.001; + } + + // Store off the last position from the predicted state. + HUD_SetLastOrg(); + + // Wipe it so we can't use it after this frame + g_finalstate = NULL; +} + + +/* +===================== +HUD_PostRunCmd + +Client calls this during prediction, after it has moved the player and updated any info changed into to-> +time is the current client clock based on prediction +cmd is the command that caused the movement, etc +runfuncs is 1 if this is the first time we've predicted this command. If so, sounds and effects should play, otherwise, they should +be ignored +===================== +*/ +void CL_DLLEXPORT HUD_PostRunCmd( struct local_state_s *from, struct local_state_s *to, struct usercmd_s *cmd, int runfuncs, double time, unsigned int random_seed ) +{ + RecClPostRunCmd(from, to, cmd, runfuncs, time, random_seed); + + g_runfuncs = runfuncs; + + if ( cl_lw && cl_lw->value ) + { + HUD_WeaponsPostThink( from, to, cmd, time, random_seed ); + } + else + { + to->client.fov = g_lastFOV; + } + + // Check to see whether too play local jetpack effects + if(runfuncs) + { + static sLastTime = 0; + float theTimePassed = time - sLastTime; + + //CheckJetpack(); + + //UpdateJetpackLights(); + + sLastTime = time; + } + + // All games can use FOV state + g_lastFOV = to->client.fov; +} + + + diff --git a/main/source/cl_dll/hud.cpp b/main/source/cl_dll/hud.cpp index aedba00..c68ced6 100644 --- a/main/source/cl_dll/hud.cpp +++ b/main/source/cl_dll/hud.cpp @@ -1,578 +1,587 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -// -// hud.cpp -// -// implementation of CHud class -// - -#include "hud.h" -#include "cl_util.h" -#include -#include -#include "hud_servers.h" -#include "vgui_TeamFortressViewport.h" -#include "vgui_int.h" - -#include "demo.h" -#include "common/demo_api.h" -#include "ui/UIComponent.h" -#include "vgui_scorepanel.h" - -#include "mod/AvHNetworkMessages.h" -#include "ui/ChatPanel.h" -#include "mod/AvHClientVariables.h" -// tankefugl: duck toggle -bool g_bDuckToggled; -// :tankefugl - -class CHLVoiceStatusHelper : public IVoiceStatusHelper -{ -public: - virtual void GetPlayerTextColor(int entindex, int color[3]) - { - color[0] = color[1] = color[2] = 255; - - if( entindex >= 0 && entindex < sizeof(g_PlayerExtraInfo)/sizeof(g_PlayerExtraInfo[0]) ) - { - int iTeam = g_PlayerExtraInfo[entindex].teamnumber; - - if ( iTeam < 0 ) - { - iTeam = 0; - } - - iTeam = iTeam % iNumberOfTeamColors; - - color[0] = kTeamColors[iTeam][0]; - color[1] = kTeamColors[iTeam][1]; - color[2] = kTeamColors[iTeam][2]; - - // Draw commander voice differently - short thePlayerClass = g_PlayerExtraInfo[entindex].playerclass; - switch(thePlayerClass) - { - case PLAYERCLASS_COMMANDER: - color[0] = color[1] = color[2] = 255; - break; - } - } - } - - virtual void UpdateCursorState() - { - gViewPort->UpdateCursorState(); - } - - virtual int GetAckIconHeight() - { - return ScreenHeight() - gHUD.m_iFontHeight*3 - 6; - } - - virtual bool CanShowSpeakerLabels() - { - if( gViewPort && gViewPort->m_pScoreBoard ) - return !gViewPort->m_pScoreBoard->isVisible(); - else - return false; - } -}; -static CHLVoiceStatusHelper g_VoiceStatusHelper; - - -extern client_sprite_t *GetSpriteList(client_sprite_t *pList, const char *psz, int iRes, int iCount); -//CImageLabel* gTestLabel = NULL; -//extern Label* gTestLabel; - -extern cvar_t *sensitivity; -cvar_t *cl_lw = NULL; - -void ShutdownInput (void); - -int __MsgFunc_ResetHUD(const char *pszName, int iSize, void *pbuf) -{ - return gHUD.MsgFunc_ResetHUD(pszName, iSize, pbuf ); -} - -int __MsgFunc_InitHUD(const char *pszName, int iSize, void *pbuf) -{ - gHUD.MsgFunc_InitHUD( pszName, iSize, pbuf ); - return 0; -} - -int __MsgFunc_ViewMode(const char *pszName, int iSize, void *pbuf) -{ - gHUD.MsgFunc_ViewMode( pszName, iSize, pbuf ); - return 1; -} - -int __MsgFunc_SetFOV(const char *pszName, int iSize, void *pbuf) -{ - return gHUD.MsgFunc_SetFOV( pszName, iSize, pbuf ); -} - -int __MsgFunc_TeamNames(const char *pszName, int iSize, void *pbuf) -{ - if (gViewPort) - return gViewPort->MsgFunc_TeamNames( pszName, iSize, pbuf ); - return 0; -} - -int __MsgFunc_MOTD(const char *pszName, int iSize, void *pbuf) -{ - if (gViewPort) - return gViewPort->MsgFunc_MOTD( pszName, iSize, pbuf ); - return 0; -} - -int __MsgFunc_ServerName(const char *pszName, int iSize, void *pbuf) -{ - if (gViewPort) - return gViewPort->MsgFunc_ServerName( pszName, iSize, pbuf ); - return 0; -} - -int __MsgFunc_ScoreInfo(const char *pszName, int iSize, void *pbuf) -{ - if (gViewPort) - return gViewPort->MsgFunc_ScoreInfo( pszName, iSize, pbuf ); - return 0; -} - -int __MsgFunc_TeamScore(const char *pszName, int iSize, void *pbuf) -{ - if (gViewPort) - return gViewPort->MsgFunc_TeamScore( pszName, iSize, pbuf ); - return 0; -} - -int __MsgFunc_TeamInfo(const char *pszName, int iSize, void *pbuf) -{ - if (gViewPort) - return gViewPort->MsgFunc_TeamInfo( pszName, iSize, pbuf ); - return 0; -} - -void __CmdFunc_SpecialDummy(void) {} - -// This is called every time the DLL is loaded -void CHud :: Init( void ) -{ - HOOK_MESSAGE( ResetHUD ); - HOOK_MESSAGE( InitHUD ); - HOOK_MESSAGE( ViewMode ); - HOOK_MESSAGE( SetFOV ); - - HOOK_COMMAND( "special", SpecialDummy); - HOOK_COMMAND( "_special", SpecialDummy); //prevent abuse - - - HOOK_MESSAGE( TeamNames ); - HOOK_MESSAGE( MOTD ); - HOOK_MESSAGE( ServerName ); - HOOK_MESSAGE( ScoreInfo ); - HOOK_MESSAGE( TeamScore ); - HOOK_MESSAGE( TeamInfo ); - - CVAR_CREATE( "hud_classautokill", "1", FCVAR_ARCHIVE | FCVAR_USERINFO ); // controls whether or not to suicide immediately on TF class switch - CVAR_CREATE( "hud_takesshots", "0", FCVAR_ARCHIVE ); // controls whether or not to automatically take screenshots at the end of a round - -#ifdef DEBUG - CVAR_CREATE( "hud_hideview", "0", FCVAR_ARCHIVE ); -#endif - - m_iLogo = 0; - m_iFOV = 0; - - // tankefugl: duck toggle - g_bDuckToggled = false; - // :tankefugl - - CVAR_CREATE( "zoom_sensitivity_ratio", "1.2", 0 ); - default_fov = CVAR_CREATE( "default_fov", "90", 0 ); - m_pCvarStealMouse = CVAR_CREATE( "hud_capturemouse", "1", FCVAR_ARCHIVE ); - m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); - cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); - - CVAR_CREATE( "cl_showspeed", "0", 0); - CVAR_CREATE( kvLabelMaps, "0", FCVAR_ARCHIVE); - CVAR_CREATE( kvGammaRamp, "0", FCVAR_ARCHIVE); - - m_pSpriteList = NULL; - - // Clear any old HUD list - if ( m_pHudList ) - { - HUDLIST *pList; - while ( m_pHudList ) - { - pList = m_pHudList; - m_pHudList = m_pHudList->pNext; - free( pList ); - } - m_pHudList = NULL; - } - - // In case we get messages before the first update -- time will be valid - m_flTime = 1.0; - - m_Ammo.Init(); - m_Health.Init(); - m_Spectator.Init(); - m_SayText.Init(); - m_Geiger.Init(); - m_Train.Init(); - m_Battery.Init(); - m_Flash.Init(); - m_Message.Init(); - m_StatusBar.Init(); - m_DeathNotice.Init(); - m_AmmoSecondary.Init(); - m_TextMessage.Init(); - m_StatusIcons.Init(); - - m_Spectator.m_chatEnabled = (m_SayText.m_HUD_saytext->value!=0); - - GetClientVoiceMgr()->Init(&g_VoiceStatusHelper, (vgui::Panel**)&gViewPort); - - m_Menu.Init(); - - ServersInit(); - - MsgFunc_ResetHUD(0, 0, NULL ); -} - -// CHud destructor -// cleans up memory allocated for m_rg* arrays -CHud :: ~CHud() -{ - delete [] m_rghSprites; - delete [] m_rgrcRects; - delete [] m_rgszSpriteNames; - - if ( m_pHudList ) - { - HUDLIST *pList; - while ( m_pHudList ) - { - pList = m_pHudList; - m_pHudList = m_pHudList->pNext; - free( pList ); - } - m_pHudList = NULL; - } - - ServersShutdown(); -} - -// GetSpriteIndex() -// searches through the sprite list loaded from hud.txt for a name matching SpriteName -// returns an index into the gHUD.m_rghSprites[] array -// returns 0 if sprite not found -int CHud :: GetSpriteIndex( const char *SpriteName ) -{ - // look through the loaded sprite name list for SpriteName - for ( int i = 0; i < m_iSpriteCount; i++ ) - { - if ( strncmp( SpriteName, m_rgszSpriteNames + (i * MAX_SPRITE_NAME_LENGTH), MAX_SPRITE_NAME_LENGTH ) == 0 ) - return i; - } - - return -1; // invalid sprite -} - -void CHud :: VidInit( void ) -{ - m_scrinfo.iSize = sizeof(m_scrinfo); - GetScreenInfo(&m_scrinfo); - - // The NS viewport isn't set up yet - int theViewPort[4]; - theViewPort[0] = theViewPort[1] = 0; - theViewPort[2] = this->m_scrinfo.iWidth; - theViewPort[3] = this->m_scrinfo.iHeight; - - gHUD.SetViewport(theViewPort); - - mFont.Load("sprites/font_arial"); - mSmallFont.Load("sprites/font_arialsmall"); - - // ---------- - // Load Sprites - // --------- -// m_hsprFont = LoadSprite("sprites/%d_font.spr"); - - m_hsprLogo = 0; - m_hsprCursor = 0; - - if (ScreenWidth() < 640) - m_iRes = 320; - else - m_iRes = 640; - - // Only load this once - if ( !m_pSpriteList ) - { - // we need to load the hud.txt, and all sprites within - m_pSpriteList = SPR_GetList("sprites/hud.txt", &m_iSpriteCountAllRes); - - if (m_pSpriteList) - { - // count the number of sprites of the appropriate res - m_iSpriteCount = 0; - client_sprite_t *p = m_pSpriteList; - for ( int j = 0; j < m_iSpriteCountAllRes; j++ ) - { - if ( p->iRes == m_iRes ) - m_iSpriteCount++; - p++; - } - - // allocated memory for sprite handle arrays - m_rghSprites = new HSPRITE[m_iSpriteCount]; - m_rgrcRects = new wrect_t[m_iSpriteCount]; - m_rgszSpriteNames = new char[m_iSpriteCount * MAX_SPRITE_NAME_LENGTH]; - - p = m_pSpriteList; - int index = 0; - for ( j = 0; j < m_iSpriteCountAllRes; j++ ) - { - if ( p->iRes == m_iRes ) - { - char sz[256]; - sprintf(sz, "sprites/%s.spr", p->szSprite); - m_rghSprites[index] = Safe_SPR_Load(sz); - m_rgrcRects[index] = p->rc; - strncpy( &m_rgszSpriteNames[index * MAX_SPRITE_NAME_LENGTH], p->szName, MAX_SPRITE_NAME_LENGTH ); - - index++; - } - - p++; - } - } - } - else - { - // we have already have loaded the sprite reference from hud.txt, but - // we need to make sure all the sprites have been loaded (we've gone through a transition, or loaded a save game) - client_sprite_t *p = m_pSpriteList; - int index = 0; - for ( int j = 0; j < m_iSpriteCountAllRes; j++ ) - { - if ( p->iRes == m_iRes ) - { - char sz[256]; - sprintf( sz, "sprites/%s.spr", p->szSprite ); - m_rghSprites[index] = Safe_SPR_Load(sz); - index++; - } - - p++; - } - } - - // assumption: number_1, number_2, etc, are all listed and loaded sequentially - m_HUD_number_0 = GetSpriteIndex( "number_0" ); - - m_iFontHeight = m_rgrcRects[m_HUD_number_0].bottom - m_rgrcRects[m_HUD_number_0].top; - - m_Ammo.VidInit(); - m_Health.VidInit(); - m_Spectator.VidInit(); - m_Geiger.VidInit(); - m_Train.VidInit(); - m_Battery.VidInit(); - m_Flash.VidInit(); - m_Message.VidInit(); - m_StatusBar.VidInit(); - m_DeathNotice.VidInit(); - m_SayText.VidInit(); - m_Menu.VidInit(); - m_AmmoSecondary.VidInit(); - m_TextMessage.VidInit(); - m_StatusIcons.VidInit(); - GetClientVoiceMgr()->VidInit(); -} - -float g_lastFOV = 0.0; - -/* -============ -COM_FileBase -============ -*/ -// Extracts the base name of a file (no path, no extension, assumes '/' as path separator) -void COM_FileBase ( const char *in, char *out) -{ - int len, start, end; - - len = (int)strlen( in ); - - // scan backward for '.' - end = len - 1; - while ( end && in[end] != '.' && in[end] != '/' && in[end] != '\\' ) - end--; - - if ( in[end] != '.' ) // no '.', copy to end - end = len-1; - else - end--; // Found ',', copy to left of '.' - - - // Scan backward for '/' - start = len-1; - while ( start >= 0 && in[start] != '/' && in[start] != '\\' ) - start--; - - if ( in[start] != '/' && in[start] != '\\' ) - start = 0; - else - start++; - - // Length of new sting - len = end - start + 1; - - // Copy partial string - strncpy( out, &in[start], len ); - // Terminate it - out[len] = 0; -} - -/* -================= -HUD_IsGame - -================= -*/ -int HUD_IsGame( const char *game ) -{ - const char *gamedir; - char gd[ 1024 ]; - - gamedir = gEngfuncs.pfnGetGameDirectory(); - if ( gamedir && gamedir[0] ) - { - COM_FileBase( gamedir, gd ); - if ( !stricmp( gd, game ) ) - return 1; - } - return 0; -} - -/* -===================== -HUD_GetFOV - -Returns last FOV -===================== -*/ -float HUD_GetFOV( void ) -{ - if ( gEngfuncs.pDemoAPI->IsRecording() ) - { - // Write it - int i = 0; - unsigned char buf[ 100 ]; - - // Active - *( float * )&buf[ i ] = g_lastFOV; - i += sizeof( float ); - - Demo_WriteBuffer( TYPE_ZOOM, i, buf ); - } - - if ( gEngfuncs.pDemoAPI->IsPlayingback() ) - { - g_lastFOV = g_demozoom; - } - return g_lastFOV; -} - -int CHud::MsgFunc_SetFOV(const char *pszName, int iSize, void *pbuf) -{ - int newfov; - NetMsg_SetFOV( pbuf, iSize, newfov ); - int def_fov = CVAR_GET_FLOAT( "default_fov" ); - - //Weapon prediction already takes care of changing the fog. ( g_lastFOV ). - if ( cl_lw && cl_lw->value ) - return 1; - - g_lastFOV = newfov; - - if ( newfov == 0 ) - { - m_iFOV = def_fov; - } - else - { - m_iFOV = newfov; - } - - // the clients fov is actually set in the client data update section of the hud - - // Set a new sensitivity - if ( m_iFOV == def_fov ) - { - // reset to saved sensitivity - m_flMouseSensitivity = 0; - } - else - { - // set a new sensitivity that is proportional to the change from the FOV default - m_flMouseSensitivity = sensitivity->value * ((float)newfov / (float)def_fov) * CVAR_GET_FLOAT("zoom_sensitivity_ratio"); - } - - return 1; -} - - -void CHud::AddHudElem(CHudBase *phudelem) -{ - HUDLIST *pdl, *ptemp; - -//phudelem->Think(); - - if (!phudelem) - return; - - pdl = (HUDLIST *)malloc(sizeof(HUDLIST)); - if (!pdl) - return; - - memset(pdl, 0, sizeof(HUDLIST)); - pdl->p = phudelem; - - if (!m_pHudList) - { - m_pHudList = pdl; - return; - } - - ptemp = m_pHudList; - - while (ptemp->pNext) - ptemp = ptemp->pNext; - - ptemp->pNext = pdl; -} - -float CHud::GetSensitivity( void ) -{ - return m_flMouseSensitivity; -} - - +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ +// +// hud.cpp +// +// implementation of CHud class +// + +#include "hud.h" +#include "cl_util.h" +#include +#include +#include "hud_servers.h" +#include "vgui_TeamFortressViewport.h" +#include "vgui_int.h" + +#include "demo.h" +#include "common/demo_api.h" +#include "ui/UIComponent.h" +#include "vgui_scorepanel.h" + +#include "mod/AvHNetworkMessages.h" +#include "ui/ChatPanel.h" +#include "mod/AvHClientVariables.h" +// : duck toggle +bool g_bDuckToggled; +// : + +class CHLVoiceStatusHelper : public IVoiceStatusHelper +{ +public: + virtual void GetPlayerTextColor(int entindex, int color[3]) + { + color[0] = color[1] = color[2] = 255; + + if( entindex >= 0 && entindex < sizeof(g_PlayerExtraInfo)/sizeof(g_PlayerExtraInfo[0]) ) + { + int iTeam = g_PlayerExtraInfo[entindex].teamnumber; + + if ( iTeam < 0 ) + { + iTeam = 0; + } + + iTeam = iTeam % iNumberOfTeamColors; + + color[0] = kTeamColors[iTeam][0]; + color[1] = kTeamColors[iTeam][1]; + color[2] = kTeamColors[iTeam][2]; + + // Draw commander voice differently + short thePlayerClass = g_PlayerExtraInfo[entindex].playerclass; + switch(thePlayerClass) + { + case PLAYERCLASS_COMMANDER: + color[0] = color[1] = color[2] = 255; + break; + } + } + } + + virtual void UpdateCursorState() + { + gViewPort->UpdateCursorState(); + } + + virtual int GetAckIconHeight() + { + return ScreenHeight() - gHUD.m_iFontHeight*3 - 6; + } + + virtual bool CanShowSpeakerLabels() + { + if( gViewPort && gViewPort->m_pScoreBoard ) + return !gViewPort->m_pScoreBoard->isVisible(); + else + return false; + } +}; +static CHLVoiceStatusHelper g_VoiceStatusHelper; + + +extern client_sprite_t *GetSpriteList(client_sprite_t *pList, const char *psz, int iRes, int iCount); +//CImageLabel* gTestLabel = NULL; +//extern Label* gTestLabel; + +extern cvar_t *sensitivity; +cvar_t *cl_lw = NULL; + +void ShutdownInput (void); + +int __MsgFunc_ResetHUD(const char *pszName, int iSize, void *pbuf) +{ + return gHUD.MsgFunc_ResetHUD(pszName, iSize, pbuf ); +} + +int __MsgFunc_InitHUD(const char *pszName, int iSize, void *pbuf) +{ + gHUD.MsgFunc_InitHUD( pszName, iSize, pbuf ); + return 0; +} + +int __MsgFunc_ViewMode(const char *pszName, int iSize, void *pbuf) +{ + gHUD.MsgFunc_ViewMode( pszName, iSize, pbuf ); + return 1; +} + +int __MsgFunc_SetFOV(const char *pszName, int iSize, void *pbuf) +{ + return gHUD.MsgFunc_SetFOV( pszName, iSize, pbuf ); +} + +int __MsgFunc_TeamNames(const char *pszName, int iSize, void *pbuf) +{ + if (gViewPort) + return gViewPort->MsgFunc_TeamNames( pszName, iSize, pbuf ); + return 0; +} + +int __MsgFunc_MOTD(const char *pszName, int iSize, void *pbuf) +{ + if (gViewPort) + return gViewPort->MsgFunc_MOTD( pszName, iSize, pbuf ); + return 0; +} + +int __MsgFunc_ServerName(const char *pszName, int iSize, void *pbuf) +{ + if (gViewPort) + return gViewPort->MsgFunc_ServerName( pszName, iSize, pbuf ); + return 0; +} + +int __MsgFunc_ScoreInfo(const char *pszName, int iSize, void *pbuf) +{ + if (gViewPort) + return gViewPort->MsgFunc_ScoreInfo( pszName, iSize, pbuf ); + return 0; +} + +int __MsgFunc_TeamScore(const char *pszName, int iSize, void *pbuf) +{ + if (gViewPort) + return gViewPort->MsgFunc_TeamScore( pszName, iSize, pbuf ); + return 0; +} + +int __MsgFunc_TeamInfo(const char *pszName, int iSize, void *pbuf) +{ + if (gViewPort) + return gViewPort->MsgFunc_TeamInfo( pszName, iSize, pbuf ); + return 0; +} + +void __CmdFunc_SpecialDummy(void) {} + +void __CmdFunc_ClRateDummy(void) { } + +// This is called every time the DLL is loaded +void CHud :: Init( void ) +{ + HOOK_MESSAGE( ResetHUD ); + HOOK_MESSAGE( InitHUD ); + HOOK_MESSAGE( ViewMode ); + HOOK_MESSAGE( SetFOV ); + + HOOK_COMMAND( "special", SpecialDummy); + HOOK_COMMAND( "_special", SpecialDummy); //prevent abuse + + HOOK_COMMAND( "cl_rate", ClRateDummy); + + HOOK_MESSAGE( TeamNames ); + HOOK_MESSAGE( MOTD ); + HOOK_MESSAGE( ServerName ); + HOOK_MESSAGE( ScoreInfo ); + HOOK_MESSAGE( TeamScore ); + HOOK_MESSAGE( TeamInfo ); + + CVAR_CREATE( "hud_classautokill", "1", FCVAR_ARCHIVE | FCVAR_USERINFO ); // controls whether or not to suicide immediately on TF class switch + CVAR_CREATE( "hud_takesshots", "0", FCVAR_ARCHIVE ); // controls whether or not to automatically take screenshots at the end of a round + +#ifdef DEBUG + CVAR_CREATE( "hud_hideview", "0", FCVAR_ARCHIVE ); +#endif + + m_iLogo = 0; + m_iFOV = 0; + + // : duck toggle + g_bDuckToggled = false; + // : + + CVAR_CREATE( "zoom_sensitivity_ratio", "1.2", 0 ); + default_fov = CVAR_CREATE( "default_fov", "90", 0 ); + m_pCvarStealMouse = CVAR_CREATE( "hud_capturemouse", "1", FCVAR_ARCHIVE ); + m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); + cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); + + CVAR_CREATE( "cl_showspeed", "0", 0); + CVAR_CREATE( kvLabelMaps, "1", FCVAR_ARCHIVE); + CVAR_CREATE( kvGammaRamp, "1", FCVAR_ARCHIVE); + CVAR_CREATE( kvCustomCrosshair, "1", FCVAR_ARCHIVE); + CVAR_CREATE( kvHudMapZoom, "3", FCVAR_ARCHIVE); + CVAR_CREATE( kvLabelHivesight, "1", FCVAR_ARCHIVE); + CVAR_CREATE( "cl_iconr", "0", FCVAR_ARCHIVE); + CVAR_CREATE( "cl_icong", "149", FCVAR_ARCHIVE); + CVAR_CREATE( "cl_iconb", "221", FCVAR_ARCHIVE); + + m_pSpriteList = NULL; + + // Clear any old HUD list + if ( m_pHudList ) + { + HUDLIST *pList; + while ( m_pHudList ) + { + pList = m_pHudList; + m_pHudList = m_pHudList->pNext; + free( pList ); + } + m_pHudList = NULL; + } + + // In case we get messages before the first update -- time will be valid + m_flTime = 1.0; + + m_Ammo.Init(); + m_Health.Init(); + m_Spectator.Init(); + m_SayText.Init(); + m_Geiger.Init(); + m_Train.Init(); + m_Battery.Init(); + m_Flash.Init(); + m_Message.Init(); + m_StatusBar.Init(); + m_DeathNotice.Init(); + m_AmmoSecondary.Init(); + m_TextMessage.Init(); + m_StatusIcons.Init(); + + m_Spectator.m_chatEnabled = (m_SayText.m_HUD_saytext->value!=0); + + GetClientVoiceMgr()->Init(&g_VoiceStatusHelper, (vgui::Panel**)&gViewPort); + + m_Menu.Init(); + + ServersInit(); + + MsgFunc_ResetHUD(0, 0, NULL ); +} + +// CHud destructor +// cleans up memory allocated for m_rg* arrays +CHud :: ~CHud() +{ + delete [] m_rghSprites; + delete [] m_rgrcRects; + delete [] m_rgszSpriteNames; + + if ( m_pHudList ) + { + HUDLIST *pList; + while ( m_pHudList ) + { + pList = m_pHudList; + m_pHudList = m_pHudList->pNext; + free( pList ); + } + m_pHudList = NULL; + } + + ServersShutdown(); +} + +// GetSpriteIndex() +// searches through the sprite list loaded from hud.txt for a name matching SpriteName +// returns an index into the gHUD.m_rghSprites[] array +// returns 0 if sprite not found +int CHud :: GetSpriteIndex( const char *SpriteName ) +{ + // look through the loaded sprite name list for SpriteName + for ( int i = 0; i < m_iSpriteCount; i++ ) + { + if ( strncmp( SpriteName, m_rgszSpriteNames + (i * MAX_SPRITE_NAME_LENGTH), MAX_SPRITE_NAME_LENGTH ) == 0 ) + return i; + } + + return -1; // invalid sprite +} + +void CHud :: VidInit( void ) +{ + m_scrinfo.iSize = sizeof(m_scrinfo); + GetScreenInfo(&m_scrinfo); + + // The NS viewport isn't set up yet + int theViewPort[4]; + theViewPort[0] = theViewPort[1] = 0; + theViewPort[2] = this->m_scrinfo.iWidth; + theViewPort[3] = this->m_scrinfo.iHeight; + + gHUD.SetViewport(theViewPort); + + mFont.Load("sprites/font_arial"); + mSmallFont.Load("sprites/font_arialsmall"); + + // ---------- + // Load Sprites + // --------- +// m_hsprFont = LoadSprite("sprites/%d_font.spr"); + + m_hsprLogo = 0; + m_hsprCursor = 0; + + if (ScreenWidth() < 640) + m_iRes = 320; + else + m_iRes = 640; + + // Only load this once + if ( !m_pSpriteList ) + { + // we need to load the hud.txt, and all sprites within + m_pSpriteList = SPR_GetList("sprites/hud.txt", &m_iSpriteCountAllRes); + + if (m_pSpriteList) + { + // count the number of sprites of the appropriate res + m_iSpriteCount = 0; + client_sprite_t *p = m_pSpriteList; + for ( int j = 0; j < m_iSpriteCountAllRes; j++ ) + { + if ( p->iRes == m_iRes ) + m_iSpriteCount++; + p++; + } + + // allocated memory for sprite handle arrays + m_rghSprites = new HSPRITE[m_iSpriteCount]; + m_rgrcRects = new wrect_t[m_iSpriteCount]; + m_rgszSpriteNames = new char[m_iSpriteCount * MAX_SPRITE_NAME_LENGTH]; + + p = m_pSpriteList; + int index = 0; + for ( j = 0; j < m_iSpriteCountAllRes; j++ ) + { + if ( p->iRes == m_iRes ) + { + char sz[256]; + sprintf(sz, "sprites/%s.spr", p->szSprite); + m_rghSprites[index] = Safe_SPR_Load(sz); + m_rgrcRects[index] = p->rc; + strncpy( &m_rgszSpriteNames[index * MAX_SPRITE_NAME_LENGTH], p->szName, MAX_SPRITE_NAME_LENGTH ); + + index++; + } + + p++; + } + } + } + else + { + // we have already have loaded the sprite reference from hud.txt, but + // we need to make sure all the sprites have been loaded (we've gone through a transition, or loaded a save game) + client_sprite_t *p = m_pSpriteList; + int index = 0; + for ( int j = 0; j < m_iSpriteCountAllRes; j++ ) + { + if ( p->iRes == m_iRes ) + { + char sz[256]; + sprintf( sz, "sprites/%s.spr", p->szSprite ); + m_rghSprites[index] = Safe_SPR_Load(sz); + index++; + } + + p++; + } + } + + // assumption: number_1, number_2, etc, are all listed and loaded sequentially + m_HUD_number_0 = GetSpriteIndex( "number_0" ); + + m_iFontHeight = m_rgrcRects[m_HUD_number_0].bottom - m_rgrcRects[m_HUD_number_0].top; + + m_Ammo.VidInit(); + m_Health.VidInit(); + m_Spectator.VidInit(); + m_Geiger.VidInit(); + m_Train.VidInit(); + m_Battery.VidInit(); + m_Flash.VidInit(); + m_Message.VidInit(); + m_StatusBar.VidInit(); + m_DeathNotice.VidInit(); + m_SayText.VidInit(); + m_Menu.VidInit(); + m_AmmoSecondary.VidInit(); + m_TextMessage.VidInit(); + m_StatusIcons.VidInit(); + GetClientVoiceMgr()->VidInit(); +} + +float g_lastFOV = 0.0; + +/* +============ +COM_FileBase +============ +*/ +// Extracts the base name of a file (no path, no extension, assumes '/' as path separator) +void COM_FileBase ( const char *in, char *out) +{ + int len, start, end; + + len = (int)strlen( in ); + + // scan backward for '.' + end = len - 1; + while ( end && in[end] != '.' && in[end] != '/' && in[end] != '\\' ) + end--; + + if ( in[end] != '.' ) // no '.', copy to end + end = len-1; + else + end--; // Found ',', copy to left of '.' + + + // Scan backward for '/' + start = len-1; + while ( start >= 0 && in[start] != '/' && in[start] != '\\' ) + start--; + + if ( in[start] != '/' && in[start] != '\\' ) + start = 0; + else + start++; + + // Length of new sting + len = end - start + 1; + + // Copy partial string + strncpy( out, &in[start], len ); + // Terminate it + out[len] = 0; +} + +/* +================= +HUD_IsGame + +================= +*/ +int HUD_IsGame( const char *game ) +{ + const char *gamedir; + char gd[ 1024 ]; + + gamedir = gEngfuncs.pfnGetGameDirectory(); + if ( gamedir && gamedir[0] ) + { + COM_FileBase( gamedir, gd ); + if ( !stricmp( gd, game ) ) + return 1; + } + return 0; +} + +/* +===================== +HUD_GetFOV + +Returns last FOV +===================== +*/ +float HUD_GetFOV( void ) +{ + if ( gEngfuncs.pDemoAPI->IsRecording() ) + { + // Write it + int i = 0; + unsigned char buf[ 100 ]; + + // Active + *( float * )&buf[ i ] = g_lastFOV; + i += sizeof( float ); + + Demo_WriteBuffer( TYPE_ZOOM, i, buf ); + } + + if ( gEngfuncs.pDemoAPI->IsPlayingback() ) + { + g_lastFOV = g_demozoom; + } + return g_lastFOV; +} + +int CHud::MsgFunc_SetFOV(const char *pszName, int iSize, void *pbuf) +{ + int newfov; + NetMsg_SetFOV( pbuf, iSize, newfov ); + int def_fov = CVAR_GET_FLOAT( "default_fov" ); + + //Weapon prediction already takes care of changing the fog. ( g_lastFOV ). + if ( cl_lw && cl_lw->value ) + return 1; + + g_lastFOV = newfov; + + if ( newfov == 0 ) + { + m_iFOV = def_fov; + } + else + { + m_iFOV = newfov; + } + + // the clients fov is actually set in the client data update section of the hud + + // Set a new sensitivity + if ( m_iFOV == def_fov ) + { + // reset to saved sensitivity + m_flMouseSensitivity = 0; + } + else + { + // set a new sensitivity that is proportional to the change from the FOV default + m_flMouseSensitivity = sensitivity->value * ((float)newfov / (float)def_fov) * CVAR_GET_FLOAT("zoom_sensitivity_ratio"); + } + + return 1; +} + + +void CHud::AddHudElem(CHudBase *phudelem) +{ + HUDLIST *pdl, *ptemp; + +//phudelem->Think(); + + if (!phudelem) + return; + + pdl = (HUDLIST *)malloc(sizeof(HUDLIST)); + if (!pdl) + return; + + memset(pdl, 0, sizeof(HUDLIST)); + pdl->p = phudelem; + + if (!m_pHudList) + { + m_pHudList = pdl; + return; + } + + ptemp = m_pHudList; + + while (ptemp->pNext) + ptemp = ptemp->pNext; + + ptemp->pNext = pdl; +} + +float CHud::GetSensitivity( void ) +{ + return m_flMouseSensitivity; +} + + diff --git a/main/source/cl_dll/hud_msg.cpp b/main/source/cl_dll/hud_msg.cpp index 026ce6e..35580e9 100644 --- a/main/source/cl_dll/hud_msg.cpp +++ b/main/source/cl_dll/hud_msg.cpp @@ -1,77 +1,77 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -// -// hud_msg.cpp -// - -#include "hud.h" -#include "cl_util.h" -#include "common/r_efx.h" -#include "mod/AvHNetworkMessages.h" - -// tankefugl: duck toggle -extern bool g_bDuckToggled; -// :tankefugl - -#define MAX_CLIENTS 32 - -#if !defined( _TFC ) -extern BEAM *pBeam; -extern BEAM *pBeam2; -#endif -/// USER-DEFINED SERVER MESSAGE HANDLERS - -int CHud :: MsgFunc_ResetHUD(const char *pszName, int iSize, void *pbuf ) -{ - NetMsg_ResetHUD( pbuf, iSize ); - - // clear all hud data - HUDLIST *pList = m_pHudList; - - while ( pList ) - { - if ( pList->p ) - pList->p->Reset(); - pList = pList->pNext; - } - - // reset sensitivity - m_flMouseSensitivity = 0; - - // tankefugl: duck toggle - g_bDuckToggled = false; - // :tankefugl - - return 0; -} - -void CAM_ToFirstPerson(void); -void CHud :: MsgFunc_ViewMode( const char *pszName, int iSize, void *pbuf ) -{ - CAM_ToFirstPerson(); -} - -void CHud :: MsgFunc_InitHUD( const char *pszName, int iSize, void *pbuf ) -{ - // prepare all hud data - HUDLIST *pList = m_pHudList; - - while (pList) - { - if ( pList->p ) - pList->p->InitHUDData(); - pList = pList->pNext; - } -} +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ +// +// hud_msg.cpp +// + +#include "hud.h" +#include "cl_util.h" +#include "common/r_efx.h" +#include "mod/AvHNetworkMessages.h" + +// : duck toggle +extern bool g_bDuckToggled; +// : + +#define MAX_CLIENTS 32 + +#if !defined( _TFC ) +extern BEAM *pBeam; +extern BEAM *pBeam2; +#endif +/// USER-DEFINED SERVER MESSAGE HANDLERS + +int CHud :: MsgFunc_ResetHUD(const char *pszName, int iSize, void *pbuf ) +{ + NetMsg_ResetHUD( pbuf, iSize ); + + // clear all hud data + HUDLIST *pList = m_pHudList; + + while ( pList ) + { + if ( pList->p ) + pList->p->Reset(); + pList = pList->pNext; + } + + // reset sensitivity + m_flMouseSensitivity = 0; + + // : duck toggle + g_bDuckToggled = false; + // : + + return 0; +} + +void CAM_ToFirstPerson(void); +void CHud :: MsgFunc_ViewMode( const char *pszName, int iSize, void *pbuf ) +{ + CAM_ToFirstPerson(); +} + +void CHud :: MsgFunc_InitHUD( const char *pszName, int iSize, void *pbuf ) +{ + // prepare all hud data + HUDLIST *pList = m_pHudList; + + while (pList) + { + if ( pList->p ) + pList->p->InitHUDData(); + pList = pList->pNext; + } +} diff --git a/main/source/cl_dll/hud_spectator.cpp b/main/source/cl_dll/hud_spectator.cpp index e662f10..f6b3a9f 100644 --- a/main/source/cl_dll/hud_spectator.cpp +++ b/main/source/cl_dll/hud_spectator.cpp @@ -390,8 +390,10 @@ int CHudSpectator::Draw(float flTime) // draw only in spectator mode if ( !g_iUser1 ) return 0; +// string error; +// gHUD.Update( flTime, error); - // Removed by mmcguire. + // Removed by mmcguire. /* // if user pressed zoom, aplly changes if ( (m_zoomDelta != 0.0f) && ( g_iUser1 == OBS_MAP_FREE ) ) @@ -515,7 +517,7 @@ void CHudSpectator::DrawOverviewMap() theDrawInfo.mY = YRES(SPECTATOR_PANEL_HEIGHT + 4); theDrawInfo.mWidth = ScreenWidth() - theDrawInfo.mX - XRES(4); theDrawInfo.mHeight = ScreenHeight() - YRES(SPECTATOR_PANEL_HEIGHT + 4) - theDrawInfo.mY; - + theDrawInfo.mZoomScale = 1.0f; AvHMapExtents theMapExtents; theOverviewMap.GetMapExtents(theMapExtents); @@ -1924,6 +1926,7 @@ void CHudSpectator::Reset() void CHudSpectator::InitHUDData() { + gHUD.InitHUDData(); m_lastPrimaryObject = m_lastSecondaryObject = 0; m_flNextObserverInput = 0.0f; m_lastHudMessage = 0; diff --git a/main/source/cl_dll/input.cpp b/main/source/cl_dll/input.cpp index 97d8fda..ed44796 100644 --- a/main/source/cl_dll/input.cpp +++ b/main/source/cl_dll/input.cpp @@ -1,1597 +1,1639 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: input.cpp $ -// $Date: 2002/10/16 02:12:21 $ -// -//------------------------------------------------------------------------------- -// $Log: input.cpp,v $ -// Revision 1.28 2002/10/16 02:12:21 Flayra -// - Valve anti-cheat integrated! -// -// Revision 1.27 2002/08/09 00:13:04 Flayra -// - Removed explicitly allowing specific commands. I can't remember why this was needed, but it doesn't appear to be anymore. -// -// Revision 1.26 2002/08/02 21:39:03 Flayra -// - Refactored variable names -// -// Revision 1.25 2002/07/08 16:13:31 Flayra -// - Fixed bug where command was able to switch weapons via mousewheel (bug #239) -// -//=============================================================================== -// cl.input.c -- builds an intended movement command to send to the server - -//xxxxxx Move bob and pitch drifting code here and other stuff from view if needed - -// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All -// rights reserved. -#include "hud.h" -#include "cl_util.h" -#include "camera.h" -extern "C" -{ -#include "kbutton.h" -} -#include "common/cvardef.h" -#include "common/usercmd.h" -#include "common/const.h" -#include "camera.h" -#include "in_defs.h" -#include "view.h" -#include -#include -#include "engine/keydefs.h" - -#include "vgui_TeamFortressViewport.h" -#include "mod/AvHServerVariables.h"// should we go ahead and just make a SharedVariables.h instead? -#include "mod/AvHClientVariables.h" -#include "mod/AvHMessage.h" -#include "fmod.h" -#include "mod/AvHScriptManager.h" -#include "mod/AvHScrollHandler.h" -#include "mod/AvHCommanderModeHandler.h" -#include "Util/Mat3.h" - -#include "engine/APIProxy.h" -#include "Exports.h" - -// tankefugl: duck toggle -extern bool g_bDuckToggled; -// :tankefugl - -extern int g_iAlive; - -extern int g_weaponselect; -extern cl_enginefunc_t gEngfuncs; -bool gResetViewAngles = false; -vec3_t gViewAngles; -extern AvHCommanderModeHandler gCommanderHandler; - -/////////////////////////////// -// Begin Max's Code -/////////////////////////////// - -float gPlayerAngles[3] = { 0, 0, 0 }; -float gTargetPlayerAngles[3] = { 0, 0, 0 }; -float gWorldViewAngles[3] = { 0, 0, 0 }; - -/////////////////////////////// -// End Max's Code -/////////////////////////////// - -// Defined in pm_math.c -//extern "C" float anglemod( float a ); -float anglemod( float a ); - -void IN_Init (void); -void IN_Move ( float frametime, float ioRotationDeltas[3], float ioTranslationDeltas[3]); -void IN_Shutdown( void ); -void V_Init( void ); -void VectorAngles( const float *forward, float *angles ); -int CL_ButtonBits( int ); - -// xxx need client dll function to get and clear impuse -extern cvar_t *in_joystick; - -int in_impulse = 0; -int in_cancel = 0; - -cvar_t *m_pitch; -cvar_t *m_yaw; -cvar_t *m_forward; -cvar_t *m_side; - -cvar_t *lookstrafe; -cvar_t *lookspring; -//cvar_t *cl_pitchup; -//cvar_t *cl_pitchdown; -cvar_t *cl_upspeed; -cvar_t *cl_movespeedkey; -cvar_t *cl_yawspeed; -cvar_t *cl_pitchspeed; -cvar_t *cl_anglespeedkey; -cvar_t *cl_vsmoothing; -cvar_t *cl_autohelp; -cvar_t *cl_centerentityid; -cvar_t *cl_musicenabled; -cvar_t *cl_musicdelay; -cvar_t *cl_musicvolume; -cvar_t *cl_musicdir; -cvar_t *cl_quickselecttime; -cvar_t *cl_highdetail; -cvar_t *cl_cmhotkeys; -cvar_t *cl_forcedefaultfov; -cvar_t *cl_dynamiclights; -cvar_t *cl_buildmessages; -cvar_t *cl_particleinfo; - -/* -=============================================================================== - -KEY BUTTONS - -Continuous button event tracking is complicated by the fact that two different -input sources (say, mouse button 1 and the control key) can both press the -same button, but the button should only be released when both of the -pressing key have been released. - -When a key event issues a button command (+forward, +attack, etc), it appends -its key number as a parameter to the command so it can be matched up with -the release. - -state bit 0 is the current state of the key -state bit 1 is edge triggered on the up to down transition -state bit 2 is edge triggered on the down to up transition - -=============================================================================== -*/ - - -kbutton_t in_mlook; -kbutton_t in_klook; -kbutton_t in_jlook; -kbutton_t in_left; -kbutton_t in_right; -kbutton_t in_forward; -kbutton_t in_back; -kbutton_t in_lookup; -kbutton_t in_lookdown; -kbutton_t in_moveleft; -kbutton_t in_moveright; -kbutton_t in_strafe; -kbutton_t in_speed; -kbutton_t in_use; -kbutton_t in_jump; -kbutton_t in_attack; -kbutton_t in_attack2; -kbutton_t in_up; -kbutton_t in_down; -kbutton_t in_duck; -kbutton_t in_reload; -kbutton_t in_alt1; -kbutton_t in_score; -kbutton_t in_break; -kbutton_t in_graph; // Display the netgraph - -typedef struct kblist_s -{ - struct kblist_s *next; - kbutton_t *pkey; - char name[32]; -} kblist_t; - -kblist_t *g_kbkeys = NULL; - -vector< pair > g_PrevCmds; - -/* -============ -KB_ConvertString - -Removes references to +use and replaces them with the keyname in the output string. If - a binding is unfound, then the original text is retained. -NOTE: Only works for text with +word in it. -============ -*/ -int KB_ConvertString( char *in, char **ppout ) -{ - char sz[ 4096 ]; - char binding[ 64 ]; - char *p; - char *pOut; - char *pEnd; - const char *pBinding; - - if ( !ppout ) - return 0; - - *ppout = NULL; - p = in; - pOut = sz; - while ( *p ) - { - if ( *p == '+' ) - { - pEnd = binding; - while ( *p && ( isalnum( *p ) || ( pEnd == binding ) ) && ( ( pEnd - binding ) < 63 ) ) - { - *pEnd++ = *p++; - } - - *pEnd = '\0'; - - pBinding = NULL; - if ( strlen( binding + 1 ) > 0 ) - { - // See if there is a binding for binding? - pBinding = gEngfuncs.Key_LookupBinding( binding + 1 ); - } - - if ( pBinding ) - { - *pOut++ = '['; - pEnd = (char *)pBinding; - } - else - { - pEnd = binding; - } - - while ( *pEnd ) - { - *pOut++ = *pEnd++; - } - - if ( pBinding ) - { - *pOut++ = ']'; - } - } - else - { - *pOut++ = *p++; - } - } - - *pOut = '\0'; - - pOut = ( char * )malloc( strlen( sz ) + 1 ); - strcpy( pOut, sz ); - *ppout = pOut; - - return 1; -} - -/* -============ -KB_Find - -Allows the engine to get a kbutton_t directly ( so it can check +mlook state, etc ) for saving out to .cfg files -============ -*/ -struct kbutton_s CL_DLLEXPORT *KB_Find( const char *name ) -{ - RecClFindKey(name); - - kblist_t *p; - p = g_kbkeys; - while ( p ) - { - if ( !stricmp( name, p->name ) ) - return p->pkey; - - p = p->next; - } - return NULL; -} - -/* -============ -KB_Add - -Add a kbutton_t * to the list of pointers the engine can retrieve via KB_Find -============ -*/ -void KB_Add( const char *name, kbutton_t *pkb ) -{ - kblist_t *p; - kbutton_t *kb; - - kb = KB_Find( name ); - - if ( kb ) - return; - - p = ( kblist_t * )malloc( sizeof( kblist_t ) ); - memset( p, 0, sizeof( *p ) ); - - strcpy( p->name, name ); - p->pkey = pkb; - - p->next = g_kbkeys; - g_kbkeys = p; -} - -/* -============ -KB_Init - -Add kbutton_t definitions that the engine can query if needed -============ -*/ -void KB_Init( void ) -{ - g_kbkeys = NULL; - - KB_Add( "in_graph", &in_graph ); - KB_Add( "in_mlook", &in_mlook ); - KB_Add( "in_jlook", &in_jlook ); -} - -/* -============ -KB_Shutdown - -Clear kblist -============ -*/ -void KB_Shutdown( void ) -{ - kblist_t *p, *n; - p = g_kbkeys; - while ( p ) - { - n = p->next; - free( p ); - p = n; - } - g_kbkeys = NULL; -} - -void KeyDown (kbutton_t *b); -void KeyUp (kbutton_t *b); - -/* -============ -KeyDown -============ -*/ -void KeyDown (kbutton_t *b) -{ - int k; - char *c; - - c = gEngfuncs.Cmd_Argv(1); - if (c[0]) - k = atoi(c); - else - k = -1; // typed manually at the console for continuous down - - int theBlockScripts = (int)gHUD.GetServerVariableFloat(kvBlockScripts); - - char *pCmd = gEngfuncs.Cmd_Argv(0); - - if(theBlockScripts && pCmd) - { - bool bFound = false; - - //Check thier last few commands (this prevents false positives if a player is hits several keys real fast) - for (int i = 0; i < g_PrevCmds.size(); i++) - { - //Check both the key pressed and the command it executed. - if(k == g_PrevCmds[i].first && !strcmp(pCmd, g_PrevCmds[i].second.c_str())) - { - bFound = true; - break; - } - } - -// //If they used mwheeldown/mwheelup to activate repeating command, make sure they didnt use +attack or +jump to prevent exploits. -// if(k == K_MWHEELDOWN || k == K_MWHEELUP && theBlockScripts == 2) -// { -// if(strstr(pCmd, "+"))//I could also do pCmd[0] == '+', but that could possibly be bypassed. -// bFound = false; -// } - - - if(!bFound - && strcmp(pCmd, "+mlook") - && strcmp(pCmd, "+jlook") - && strcmp(pCmd, "+showscores") - && strcmp(pCmd, "+use")) - { - gEngfuncs.pfnCenterPrint("Scripting is not allowed on this server."); - b->down[0] = b->down[1] = 0; - b->state = 4; // impulse up - return; - } - } - - if (k == b->down[0] || k == b->down[1]) - return; // repeating key - - if (!b->down[0]) - b->down[0] = k; - else if (!b->down[1]) - b->down[1] = k; - else - { - gEngfuncs.Con_DPrintf ("Three keys down for a button '%c' '%c' '%c'!\n", b->down[0], b->down[1], c); - return; - } - - if (b->state & 1) - return; // still down - b->state |= 1 + 2; // down + impulse down -} - -/* -============ -KeyUp -============ -*/ -void KeyUp (kbutton_t *b) -{ - int k; - char *c; - - c = gEngfuncs.Cmd_Argv(1); - if (c[0]) - k = atoi(c); - else - { // typed manually at the console, assume for unsticking, so clear all - b->down[0] = b->down[1] = 0; - b->state = 4; // impulse up - return; - } - - if (b->down[0] == k) - b->down[0] = 0; - else if (b->down[1] == k) - b->down[1] = 0; - else - return; // key up without coresponding down (menu pass through) - if (b->down[0] || b->down[1]) - { - //Con_Printf ("Keys down for button: '%c' '%c' '%c' (%d,%d,%d)!\n", b->down[0], b->down[1], c, b->down[0], b->down[1], c); - return; // some other key is still holding it down - } - - if (!(b->state & 1)) - return; // still up (this should not happen) - - b->state &= ~1; // now up - b->state |= 4; // impulse up -} - - -bool AvHContainsBlockedCommands(const char* inInput) -{ - if (inInput == NULL) - { - return false; - } - - const char* kBlockedCommand[] = - { - "exec", - "wait", - "special", - "_special" - }; - - // Check for a ; indicating multiple commands. - - if (strchr(inInput, ';') != NULL) - { - return true; - } - - // Check if any of the blocked commands are being used. - - const char* theCommandEnd = strpbrk(inInput, " \t"); - - int theCommandLength; - - if (theCommandEnd == NULL) - { - theCommandLength = (int)strlen(inInput); - } - else - { - theCommandLength = theCommandEnd - inInput; - } - - for (int i = 0; i < sizeof(kBlockedCommand) / sizeof(const char*); ++i) - { - if ((int)strlen(kBlockedCommand[i]) == theCommandLength && - strncmp(inInput, kBlockedCommand[i], theCommandLength) == 0) - { - return true; - } - } - return false; -} - -/* -============ -HUD_Key_Event - -Return 1 to allow engine to process the key, otherwise, act on it as needed -============ -*/ -int CL_DLLEXPORT HUD_Key_Event( int down, int keynum, const char *pszCurrentBinding ) -{ - RecClKeyEvent(down, keynum, pszCurrentBinding); - - // Check to see if the event has any outlawed commands in it. - float theBlockScripts = gHUD.GetServerVariableFloat(kvBlockScripts); - - if (theBlockScripts && AvHContainsBlockedCommands(pszCurrentBinding)) - { - if(down)//voogru: only show when going down. - gEngfuncs.pfnCenterPrint("Scripting is not allowed on this server.\n"); - return 0; - } - - if(pszCurrentBinding) - { - if(g_PrevCmds.size() >= 5) - g_PrevCmds.erase(g_PrevCmds.begin());//remove the oldest command - - g_PrevCmds.push_back(make_pair(keynum, (string)pszCurrentBinding)); - } - - int theProcessKeyBinding = 1; - -// char theKeyBinding[256] = "none"; -// if(pszCurrentBinding) -// { -// sprintf(theKeyBinding, pszCurrentBinding); -// } -// -// char theMessage[512]; -// sprintf(theMessage, "%s (down: %d, keynum %d)", theKeyBinding, down, keynum); -// CenterPrint(theMessage); - - if(gViewPort /*&& gViewPort->IsOptionsMenuVisible()*/) - { - - theProcessKeyBinding = gViewPort->KeyInput(down, keynum, pszCurrentBinding); - - if(pszCurrentBinding && (!strcmp(pszCurrentBinding, "toggleconsole") || !strcmp(pszCurrentBinding, "cancelselect"))) - { - theProcessKeyBinding = 1; - } - } - - - // Process topdown commands with precedence first - if(theProcessKeyBinding && gHUD.GetInTopDownMode()) - { - if((keynum != 0) && down/*&& pszCurrentBinding*/) - { - AvHMessageID theMessageID = gHUD.HotKeyHit(keynum); - if((theMessageID != MESSAGE_NULL) || (keynum == K_ESCAPE)) - { - // If ESC or cancel was hit, cancel ghost building - if((keynum == K_ESCAPE) || (theMessageID == MESSAGE_CANCEL)) - { - gHUD.SetGhostBuildingMode(MESSAGE_NULL); - } - - theProcessKeyBinding = 0; - } - -// // Else look for hotkey commands -// int thePrefixLength = strlen(kHotKeyPrefix); -// if(down && !strncmp(pszCurrentBinding, kHotKeyPrefix, thePrefixLength)) -// { -// // Strip out number, pass it to HUD -// char theNumberString[8]; -// memset(theNumberString, 0, 8); -// int theBindingLength = strlen(pszCurrentBinding); -// ASSERT(theBindingLength > thePrefixLength); -// strncpy(theNumberString, pszCurrentBinding + thePrefixLength, theBindingLength - thePrefixLength); -// -// int theBuildTech = MakeIntFromString(string(theNumberString)); -// -// gHUD.HotKeyHit(theBuildTech); -// -// theProcessKeyBinding = 0; -// } -// else -// { -// // Look for top down only commands, like scrolling -// if(!strcmp("+forward", pszCurrentBinding)) -// { -// AvHScrollHandler::ScrollUp(); -// theProcessKeyBinding = 0; -// } -// else if(!strcmp("-forward", pszCurrentBinding)) -// { -// AvHScrollHandler::StopScroll(); -// theProcessKeyBinding = 0; -// } -// else if(!strcmp("+back", pszCurrentBinding)) -// { -// AvHScrollHandler::ScrollDown(); -// theProcessKeyBinding = 0; -// } -// else if(!strcmp("-back", pszCurrentBinding)) -// { -// AvHScrollHandler::StopScroll(); -// theProcessKeyBinding = 0; -// } -// else if(!strcmp("+moveleft", pszCurrentBinding)) -// { -// AvHScrollHandler::ScrollLeft(); -// theProcessKeyBinding = 0; -// } -// else if(!strcmp("-moveleft", pszCurrentBinding)) -// { -// AvHScrollHandler::StopScroll(); -// theProcessKeyBinding = 0; -// } -// else if(!strcmp("+moveright", pszCurrentBinding)) -// { -// AvHScrollHandler::ScrollRight(); -// theProcessKeyBinding = 0; -// } -// else if(!strcmp("-moveright", pszCurrentBinding)) -// { -// AvHScrollHandler::StopScroll(); -// theProcessKeyBinding = 0; -// } -// } - } - } - - if(theProcessKeyBinding) - { - // Process only a couple keybindings in top down mode -// if(!gHUD.GetInTopDownMode() || (pszCurrentBinding && -// -// // Misc. commands -// (!strcmp(pszCurrentBinding, "toggleconsole") || !strcmp(pszCurrentBinding, "cancelselect") || !strcmp(pszCurrentBinding, "+showscores") || !strcmp(pszCurrentBinding, "-showscores") || !strcmp(pszCurrentBinding, "messagemode") || !strcmp(pszCurrentBinding, "messagemode2") || !strcmp(pszCurrentBinding, "snapshot") || !strcmp(pszCurrentBinding, "screenshot") || !strcmp(pszCurrentBinding, "+jump") || !strcmp(pszCurrentBinding, "addbot") || !strcmp(pszCurrentBinding, "+voicerecord") || !strcmp(pszCurrentBinding, "-voicerecord") || -// -// // Movement commands -// !strcmp(pszCurrentBinding, "testevent") /*|| !strcmp(pszCurrentBinding, "invnext") || !strcmp(pszCurrentBinding, "invprev")*/ || !strcmp(pszCurrentBinding, "+moveleft") || !strcmp(pszCurrentBinding, "-moveleft") || !strcmp(pszCurrentBinding, "+moveright") || !strcmp(pszCurrentBinding, "-moveright") || !strcmp(pszCurrentBinding, "+moveup") || !strcmp(pszCurrentBinding, "-moveup") || !strcmp(pszCurrentBinding, "+movedown") || !strcmp(pszCurrentBinding, "-movedown") || -// -// // For selecting groups -// !strcmp(pszCurrentBinding, "slot1") || !strcmp(pszCurrentBinding, "slot2") || !strcmp(pszCurrentBinding, "slot3") || !strcmp(pszCurrentBinding, "slot4") || !strcmp(pszCurrentBinding, "slot5") || -// -// // For creating groups -// !strcmp(pszCurrentBinding, "+duck") || !strcmp(pszCurrentBinding, "-duck") || -// -// // For testing ease -// !strcmp(pszCurrentBinding, "givepoints") -// -// ))) -// { - if (gViewPort) - theProcessKeyBinding = gViewPort->KeyInput(down, keynum, pszCurrentBinding); - -// // Don't -// if(!strcmp(pszCurrentBinding, "+jump") && gHUD.GetInTopDownMode()) -// { -// gHUD.GotoAlert(); -// } - -// } - } - - return theProcessKeyBinding; -} - -void IN_BreakDown( void ) { KeyDown( &in_break );}; -void IN_BreakUp( void ) { KeyUp( &in_break ); }; -void IN_KLookDown (void) {KeyDown(&in_klook);} -void IN_KLookUp (void) {KeyUp(&in_klook);} -void IN_JLookDown (void) {KeyDown(&in_jlook);} -void IN_JLookUp (void) {KeyUp(&in_jlook);} -void IN_MLookDown (void) {KeyDown(&in_mlook);} -void IN_UpDown(void) {KeyDown(&in_up);} -void IN_UpUp(void) {KeyUp(&in_up);} -void IN_DownDown(void) {KeyDown(&in_down);} -void IN_DownUp(void) {KeyUp(&in_down);} -void IN_LeftDown(void) {KeyDown(&in_left);} -void IN_LeftUp(void) {KeyUp(&in_left);} -void IN_RightDown(void) {KeyDown(&in_right);} -void IN_RightUp(void) {KeyUp(&in_right);} - -void IN_ForwardDown(void) -{ - KeyDown(&in_forward); - gHUD.m_Spectator.HandleButtonsDown( IN_FORWARD ); -} - -void IN_ForwardUp(void) -{ - KeyUp(&in_forward); - gHUD.m_Spectator.HandleButtonsUp( IN_FORWARD ); -} - -void IN_BackDown(void) -{ - KeyDown(&in_back); - gHUD.m_Spectator.HandleButtonsDown( IN_BACK ); -} - -void IN_BackUp(void) -{ - KeyUp(&in_back); - gHUD.m_Spectator.HandleButtonsUp( IN_BACK ); -} -void IN_LookupDown(void) {KeyDown(&in_lookup);} -void IN_LookupUp(void) {KeyUp(&in_lookup);} -void IN_LookdownDown(void) {KeyDown(&in_lookdown);} -void IN_LookdownUp(void) {KeyUp(&in_lookdown);} -void IN_MoveleftDown(void) -{ - KeyDown(&in_moveleft); - gHUD.m_Spectator.HandleButtonsDown( IN_MOVELEFT ); -} - -void IN_MoveleftUp(void) -{ - KeyUp(&in_moveleft); - gHUD.m_Spectator.HandleButtonsUp( IN_MOVELEFT ); -} - -void IN_MoverightDown(void) -{ - KeyDown(&in_moveright); - gHUD.m_Spectator.HandleButtonsDown( IN_MOVERIGHT ); -} - -void IN_MoverightUp(void) -{ - KeyUp(&in_moveright); - gHUD.m_Spectator.HandleButtonsUp( IN_MOVERIGHT ); -} -void IN_SpeedDown(void) {KeyDown(&in_speed);} -void IN_SpeedUp(void) {KeyUp(&in_speed);} -void IN_StrafeDown(void) {KeyDown(&in_strafe);} -void IN_StrafeUp(void) {KeyUp(&in_strafe);} -void IN_Attack2Down(void) {KeyDown(&in_attack2);} -void IN_Attack2Up(void) {KeyUp(&in_attack2);} -void IN_UseDown (void) -{ - KeyDown(&in_use); - gHUD.m_Spectator.HandleButtonsDown( IN_USE ); -} -void IN_UseUp (void) {KeyUp(&in_use);} -void IN_JumpDown (void) -{ - KeyDown(&in_jump); - - if(gHUD.GetInTopDownMode()) - { - //gHUD.GotoAlert(); - } - - gHUD.m_Spectator.HandleButtonsDown( IN_JUMP ); - -} - -void IN_JumpUp (void) -{ - KeyUp(&in_jump); - gHUD.m_Spectator.HandleButtonsUp( IN_JUMP ); -} - -void IN_DuckDown(void) -{ - KeyDown(&in_duck); - gHUD.m_Spectator.HandleButtonsDown( IN_DUCK ); -} - -void IN_DuckUp(void) {KeyUp(&in_duck);} -// tankefugl: duck toggle -void IN_DuckToggle(void) -{ - g_bDuckToggled = !g_bDuckToggled; -} -// :tankefugl -void IN_ReloadDown(void) {KeyDown(&in_reload);} -void IN_ReloadUp(void) {KeyUp(&in_reload);} -void IN_Alt1Down(void) {KeyDown(&in_alt1);} -void IN_Alt1Up(void) {KeyUp(&in_alt1);} -void IN_GraphDown(void) {KeyDown(&in_graph);} -void IN_GraphUp(void) {KeyUp(&in_graph);} - -void IN_AttackDown(void) -{ - KeyDown( &in_attack ); - gHUD.m_Spectator.HandleButtonsDown( IN_ATTACK ); -} - -void IN_AttackUp(void) -{ - KeyUp( &in_attack ); - in_cancel = 0; -} - -// Special handling -void IN_Cancel(void) -{ - in_cancel = 1; -} - -void IN_Impulse (void) -{ - //char msg[1024]; - //sprintf(msg, "in_impulse=%s\n", gEngfuncs.Cmd_Argv(1)); - //CenterPrint(msg); - - in_impulse = atoi( gEngfuncs.Cmd_Argv(1) ); -} - -void IN_ScoreDown(void) -{ - KeyDown(&in_score); - - if ( gViewPort ) - { - //if(gHUD.SwitchUIMode(SCOREBOARD_MODE)) - //{ - gViewPort->ShowScoreBoard(); - //} - } -} - -void IN_ScoreUp(void) -{ - KeyUp(&in_score); - - // Removed because it was getting called and hiding mouse <<< cgc >>> - if ( gViewPort ) - { - //if(gHUD.SwitchUIMode(MAIN_MODE)) - //{ - gViewPort->HideScoreBoard(); - //} - } -} - -void IN_MLookUp (void) -{ - KeyUp( &in_mlook ); - if ( !( in_mlook.state & 1 ) && lookspring->value ) - { - V_StartPitchDrift(); - } -} - -/* -=============== -CL_KeyState - -Returns 0.25 if a key was pressed and released during the frame, -0.5 if it was pressed and held -0 if held then released, and -1.0 if held for the entire time -=============== -*/ -float CL_KeyState (kbutton_t *key) -{ - float val = 0.0; - int impulsedown, impulseup, down; - - impulsedown = key->state & 2; - impulseup = key->state & 4; - down = key->state & 1; - - if ( impulsedown && !impulseup ) - { - // pressed and held this frame? - val = down ? 0.5 : 0.0; - } - - if ( impulseup && !impulsedown ) - { - // released this frame? - val = down ? 0.0 : 0.0; - } - - if ( !impulsedown && !impulseup ) - { - // held the entire frame? - val = down ? 1.0 : 0.0; - } - - if ( impulsedown && impulseup ) - { - if ( down ) - { - // released and re-pressed this frame - val = 0.75; - } - else - { - // pressed and released this frame - val = 0.25; - } - } - - // clear impulses - key->state &= 1; - return val; -} - -/* -================ -CL_AdjustAngles - -Moves the local angle positions -================ -*/ -void CL_AdjustAngles ( float frametime, float *viewangles ) -{ - float speed; - float up, down; - - if (in_speed.state & 1) - { - speed = frametime * cl_anglespeedkey->value; - } - else - { - speed = frametime; - } - - if (!(in_strafe.state & 1)) - { - viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right); - viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left); - viewangles[YAW] = anglemod(viewangles[YAW]); - } - if (in_klook.state & 1) - { - V_StopPitchDrift (); - viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_forward); - viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_back); - } - - up = CL_KeyState (&in_lookup); - down = CL_KeyState(&in_lookdown); - - viewangles[PITCH] -= speed*cl_pitchspeed->value * up; - viewangles[PITCH] += speed*cl_pitchspeed->value * down; - - if (up || down) - V_StopPitchDrift (); - - if (viewangles[PITCH] > 89) - viewangles[PITCH] = 89; - if (viewangles[PITCH] < -89) - viewangles[PITCH] = -89; - - if (viewangles[ROLL] > 50) - viewangles[ROLL] = 50; - if (viewangles[ROLL] < -50) - viewangles[ROLL] = -50; -} - -/* -================ -CL_CreateMove - -Send the intended movement message to the server -if active == 1 then we are 1) not playing back demos ( where our commands are ignored ) and -2 ) we have finished signing on to server -================ -*/ -void CL_DLLEXPORT CL_CreateMove ( float frametime, struct usercmd_s *cmd, int active ) -{ - RecClCL_CreateMove(frametime, cmd, active); - - float spd; - vec3_t viewangles; - static vec3_t oldangles; - - if ( active && (!gViewPort || !gViewPort->IsOptionsMenuVisible()) /*&& !gHUD.GetShowingMap()*/) - { - int theButtonState = CL_ButtonBits( 1 ); - - memset (cmd, 0, sizeof(*cmd)); - - float theRotationDeltas[3] = {0,0,0}; - float theTranslationDeltas[3] = {0,0,0}; - - IN_Move(frametime,theRotationDeltas,theTranslationDeltas); - - if(gResetViewAngles) - { - VectorCopy(gViewAngles,viewangles); - gResetViewAngles = false; - } - else - { - gEngfuncs.GetViewAngles( (float *)viewangles ); - } - VectorAdd(viewangles,theRotationDeltas,viewangles); - CL_AdjustAngles ( frametime, viewangles ); - - gEngfuncs.SetViewAngles( (float *)viewangles ); - VectorCopy (viewangles,gWorldViewAngles); - - // If we're in topdown mode - bool theProcessedMove = false; - bool theIsSendingSpecialEvent = false; - bool theOverrideImpulse = false; - float theWorldX, theWorldY; - int theScriptImpulse = 0; - - AvHMessageID theAlienAbility = MESSAGE_NULL; - AvHMessageID theGroupMessage = MESSAGE_NULL; - - //int theUpgradeVar = gEngfuncs.GetLocalPlayer()->curstate.iuser4; - //bool theIsParalyzed = GetHasUpgrade(theUpgradeVar, PLAYER_PARALYZED); - if(AvHScriptManager::Instance()->GetClientMove(theButtonState, theScriptImpulse)) - { - if(theScriptImpulse) - { - theOverrideImpulse = true; - } - //theProcessedMove = true; - - // TODO: Pass theButtonState to override all CL_KeyState() calls - } - else if(gHUD.GetInTopDownMode()) - { - cmd->upmove = cmd->sidemove = cmd->forwardmove = 0; - - // If a button was JUST pressed or released - vec3_t theMouseNormPos; - AvHMessageID theTechEvent = MESSAGE_NULL; - if(gCommanderHandler.GetMoveToWorldPosition(theWorldX, theWorldY)) - { - // Commander wants to scroll to an area of the mini-map - cmd->impulse = COMMANDER_MOVETO; - cmd->upmove = theWorldX/kWorldPosNetworkConstant; - cmd->sidemove = theWorldY/kWorldPosNetworkConstant; - gCommanderHandler.ClearMoveToPosition(); - gHUD.ClearTrackingEntity(); - - theIsSendingSpecialEvent = true; - theProcessedMove = true; - } - else if(gCommanderHandler.GetDefaultOrderPosition(theWorldX, theWorldY)) - { - // Commander wants to scroll to an area of the mini-map - cmd->impulse = COMMANDER_DEFAULTORDER; - cmd->upmove = theWorldX/kWorldPosNetworkConstant; - cmd->sidemove = theWorldY/kWorldPosNetworkConstant; - gCommanderHandler.ClearDefaultOrderPosition(); - gHUD.ClearTrackingEntity(); - - theIsSendingSpecialEvent = true; - theProcessedMove = true; - } - else if(gHUD.GetAndClearTechEvent(theTechEvent)) - { - cmd->impulse = theTechEvent; - theProcessedMove = true; - theIsSendingSpecialEvent = true; - gHUD.ClearTrackingEntity(); - - } - // else scroll - else - { - // Scroll the view if the HUD tells us to, otherwise use normal key presses - int theScrollX = 0, theScrollY = 0, theScrollZ = 0; - gHUD.GetAndClearTopDownScrollAmount(theScrollX, theScrollY, theScrollZ); - - if(theScrollX || theScrollY || theScrollZ) - { - // Commander move speed - float kCommanderMoveSpeed = 1000; - cmd->upmove += kCommanderMoveSpeed * theScrollY; - cmd->sidemove += kCommanderMoveSpeed * theScrollX; - cmd->forwardmove += kCommanderMoveSpeed * theScrollZ; - - cmd->impulse = COMMANDER_SCROLL; - theOverrideImpulse = true; - - gHUD.ClearTrackingEntity(); - - //theIsSendingSpecialEvent = true; - theProcessedMove = true; - } - else if(gHUD.GetAndClearGroupEvent(theGroupMessage)) - { - cmd->impulse = theGroupMessage; - theIsSendingSpecialEvent = true; - theProcessedMove = true; - - gHUD.SetLastHotkeySelectionEvent(theGroupMessage); - } -// else if(gHUD.GetTrackingEntity() > 0) -// { -// int theTrackingEntity = gHUD.GetTrackingEntity(); -// cmd->upmove = theTrackingEntity*kHotKeyNetworkFactor; -// cmd->impulse = COMMANDER_TRACKENTITY; -// -// theIsSendingSpecialEvent = true; -// theProcessedMove = true; -// } - else if(in_impulse != 0) - { - bool theProcessImpulse = false; - switch(in_impulse) - { - case COMMANDER_SELECTALL: - case COMMANDER_NEXTIDLE: - case COMMANDER_NEXTAMMO: - case COMMANDER_NEXTHEALTH: - theProcessImpulse = true; - break; - } - - if(theProcessImpulse) - { - cmd->impulse = in_impulse; - in_impulse = 0; - - theProcessedMove = true; - theIsSendingSpecialEvent = true; - } - } - - if(!theProcessedMove && gHUD.GetAndClearSelectionEvent(theMouseNormPos, theTechEvent)) - { - // Store world position x,y in upmove,sidemove - cmd->upmove = theMouseNormPos.x*kSelectionNetworkConstant; - cmd->sidemove = theMouseNormPos.y*kSelectionNetworkConstant; - cmd->forwardmove = theMouseNormPos.z*kSelectionNetworkConstant; - - // Set impulse indicating this - //cmd->impulse = COMMANDER_MOUSECOORD; - - // This could be COMMANDER_MOUSECOORD or BUILD_TURRET or one of the other BUILD_ events - // They are all sent the same way - cmd->impulse = theTechEvent; - - // Order mode isn't currently used but may be in the future - //cmd->weaponselect = gHUD.GetOrderMode(); - - // Set buttons. Attack gets turned off when we're in mouse mode (apparently) - // so we need to set the buttons manually - cmd->buttons = theButtonState; - if(gHUD.GetMouseOneDown()) - { - cmd->buttons |= IN_ATTACK; - } - if(gHUD.GetMouseTwoDown()) - { - cmd->buttons |= IN_ATTACK2; - } - - gHUD.ClearTrackingEntity(); - - theIsSendingSpecialEvent = true; - theProcessedMove = true; - } - } - } - else if(gHUD.GetAndClearAlienAbility(theAlienAbility)) - { - cmd->impulse = theAlienAbility; - - // Added by mmcguire. - // 255 signifies that the impulse came from us and not from the console. - cmd->weaponselect = 255; - - theProcessedMove = true; - theIsSendingSpecialEvent = true; - - } - - // else process move normally - if(!theProcessedMove) - { - if ( in_strafe.state & 1 ) - { - cmd->sidemove += kSideSpeed * CL_KeyState (&in_right); - cmd->sidemove -= kSideSpeed * CL_KeyState (&in_left); - } - - cmd->sidemove += kSideSpeed * CL_KeyState (&in_moveright); - cmd->sidemove -= kSideSpeed * CL_KeyState (&in_moveleft); - - cmd->upmove += cl_upspeed->value * CL_KeyState (&in_up); - cmd->upmove -= cl_upspeed->value * CL_KeyState (&in_down); - - if ( !(in_klook.state & 1 ) ) - { - cmd->forwardmove += kForwardSpeed * CL_KeyState (&in_forward); - cmd->forwardmove -= kBackSpeed * CL_KeyState (&in_back); - } - } - - if(!theIsSendingSpecialEvent) - { - // adjust for speed key - if ( in_speed.state & 1 ) - { - cmd->forwardmove *= cl_movespeedkey->value; - cmd->sidemove *= cl_movespeedkey->value; - cmd->upmove *= cl_movespeedkey->value; - } - - // clip to maxspeed - spd = gEngfuncs.GetClientMaxspeed(); - - if ( spd != 0.0 ) - { - // scale the 3 speeds so that the total velocity is not > cl.maxspeed - float fmov = sqrt( (cmd->forwardmove*cmd->forwardmove) + (cmd->sidemove*cmd->sidemove) + (cmd->upmove*cmd->upmove) ); - - if ( fmov > spd ) - { - float fratio = spd / fmov; - cmd->forwardmove *= fratio; - cmd->sidemove *= fratio; - cmd->upmove *= fratio; - } - } - - // Allow mice and other controllers to add their inputs - cmd->forwardmove += theTranslationDeltas[0]; - cmd->sidemove += theTranslationDeltas[1]; - cmd->upmove += theTranslationDeltas[2]; - - if(!theOverrideImpulse) - { - cmd->impulse = in_impulse; - in_impulse = 0; - } - - cmd->weaponselect = g_weaponselect; - g_weaponselect = 0; - - // - // set button and flag bits - // - cmd->buttons = theButtonState; - - // If they're in a modal dialog, or we're stunned, ignore the attack button. - int theLocalUpgrades = gHUD.GetLocalUpgrades(); - if( GetClientVoiceMgr()->IsInSquelchMode() ) - { - cmd->buttons &= ~IN_ATTACK; - } - - // Using joystick? - if ( in_joystick->value ) - { - if ( cmd->forwardmove > 0 ) - { - cmd->buttons |= IN_FORWARD; - } - else if ( cmd->forwardmove < 0 ) - { - cmd->buttons |= IN_BACK; - } - } - } - } - - gEngfuncs.GetViewAngles( (float *)viewangles ); - - // Set current view angles but not if frozen (this still allows you to rotate in first-person, but player model won't change) - int theUser4 = gHUD.GetLocalUpgrades(); - bool theIsFrozen = GetHasUpgrade(theUser4, MASK_PLAYER_STUNNED) || GetHasUpgrade(theUser4, MASK_ALIEN_EMBRYO); - - if ( g_iAlive && !theIsFrozen) - { - VectorCopy( viewangles, cmd->viewangles ); - VectorCopy( viewangles, oldangles ); - } - else - { - VectorCopy( oldangles, cmd->viewangles ); - } -} - -/* -============ -CL_IsDead - -Returns 1 if health is <= 0 -============ -*/ -int CL_IsDead( void ) -{ - return ( gHUD.m_Health.m_iHealth <= 0 ) ? 1 : 0; -} - -/* -============ -CL_ButtonBits - -Returns appropriate button info for keyboard and mouse state -Set bResetState to 1 to clear old state info -============ -*/ -int CL_ButtonBits( int bResetState ) -{ - int bits = 0; - - if ( in_attack.state & 3 ) - { - bits |= IN_ATTACK; - } - - if ( in_speed.state & 3 ) - { - bits |= IN_WALK; - } - - // tankefugl: duck toggle - if ( g_bDuckToggled ) - { - if (!(in_duck.state & 3)) - { - bits |= IN_DUCK; - } - } - else if (in_duck.state & 3) - { - bits |= IN_DUCK; - } - // :tankefugl - - if (in_jump.state & 3) - { - bits |= IN_JUMP; - } - - if ( in_forward.state & 3 ) - { - bits |= IN_FORWARD; - } - - if (in_back.state & 3) - { - bits |= IN_BACK; - } - - if (in_use.state & 3) - { - bits |= IN_USE; - } - - if (in_cancel) - { - bits |= IN_CANCEL; - } - - if ( in_left.state & 3 ) - { - bits |= IN_LEFT; - } - - if (in_right.state & 3) - { - bits |= IN_RIGHT; - } - - if ( in_moveleft.state & 3 ) - { - bits |= IN_MOVELEFT; - } - - if (in_moveright.state & 3) - { - bits |= IN_MOVERIGHT; - } - - if (in_attack2.state & 3) - { - bits |= IN_ATTACK2; - } - - if (in_reload.state & 3) - { - bits |= IN_RELOAD; - } - - if (in_alt1.state & 3) - { - bits |= IN_ALT1; - } - - if ( in_score.state & 3 ) - { - bits |= IN_SCORE; - } - - // Dead or in intermission? Shore scoreboard, too - if ( CL_IsDead() || gHUD.m_iIntermission ) - { - bits |= IN_SCORE; - } - - if ( bResetState ) - { - in_attack.state &= ~2; - in_speed.state &= ~2; - in_duck.state &= ~2; - in_jump.state &= ~2; - in_forward.state &= ~2; - in_back.state &= ~2; - in_use.state &= ~2; - in_left.state &= ~2; - in_right.state &= ~2; - in_moveleft.state &= ~2; - in_moveright.state &= ~2; - in_attack2.state &= ~2; - in_reload.state &= ~2; - in_alt1.state &= ~2; - in_score.state &= ~2; - } - - return bits; -} - -/* -============ -CL_ResetButtonBits - -============ -*/ -void CL_ResetButtonBits( int bits ) -{ - int bitsNew = CL_ButtonBits( 0 ) ^ bits; - - // Has the attack button been changed - if ( bitsNew & IN_ATTACK ) - { - // Was it pressed? or let go? - if ( bits & IN_ATTACK ) - { - KeyDown( &in_attack ); - } - else - { - // totally clear state - in_attack.state &= ~7; - } - } -} - -/* -============ -InitInput -============ -*/ -void InitInput (void) -{ - gEngfuncs.pfnAddCommand ("+moveup",IN_UpDown); - gEngfuncs.pfnAddCommand ("-moveup",IN_UpUp); - gEngfuncs.pfnAddCommand ("+movedown",IN_DownDown); - gEngfuncs.pfnAddCommand ("-movedown",IN_DownUp); - gEngfuncs.pfnAddCommand ("+left",IN_LeftDown); - gEngfuncs.pfnAddCommand ("-left",IN_LeftUp); - gEngfuncs.pfnAddCommand ("+right",IN_RightDown); - gEngfuncs.pfnAddCommand ("-right",IN_RightUp); - gEngfuncs.pfnAddCommand ("+forward",IN_ForwardDown); - gEngfuncs.pfnAddCommand ("-forward",IN_ForwardUp); - gEngfuncs.pfnAddCommand ("+back",IN_BackDown); - gEngfuncs.pfnAddCommand ("-back",IN_BackUp); - gEngfuncs.pfnAddCommand ("+lookup", IN_LookupDown); - gEngfuncs.pfnAddCommand ("-lookup", IN_LookupUp); - gEngfuncs.pfnAddCommand ("+lookdown", IN_LookdownDown); - gEngfuncs.pfnAddCommand ("-lookdown", IN_LookdownUp); - gEngfuncs.pfnAddCommand ("+strafe", IN_StrafeDown); - gEngfuncs.pfnAddCommand ("-strafe", IN_StrafeUp); - gEngfuncs.pfnAddCommand ("+moveleft", IN_MoveleftDown); - gEngfuncs.pfnAddCommand ("-moveleft", IN_MoveleftUp); - gEngfuncs.pfnAddCommand ("+moveright", IN_MoverightDown); - gEngfuncs.pfnAddCommand ("-moveright", IN_MoverightUp); - gEngfuncs.pfnAddCommand ("+speed", IN_SpeedDown); - gEngfuncs.pfnAddCommand ("-speed", IN_SpeedUp); - gEngfuncs.pfnAddCommand ("+attack", IN_AttackDown); - gEngfuncs.pfnAddCommand ("-attack", IN_AttackUp); - gEngfuncs.pfnAddCommand ("+attack2", IN_Attack2Down); - gEngfuncs.pfnAddCommand ("-attack2", IN_Attack2Up); - gEngfuncs.pfnAddCommand ("+use", IN_UseDown); - gEngfuncs.pfnAddCommand ("-use", IN_UseUp); - gEngfuncs.pfnAddCommand ("+jump", IN_JumpDown); - gEngfuncs.pfnAddCommand ("-jump", IN_JumpUp); - gEngfuncs.pfnAddCommand ("impulse", IN_Impulse); - gEngfuncs.pfnAddCommand ("+klook", IN_KLookDown); - gEngfuncs.pfnAddCommand ("-klook", IN_KLookUp); - gEngfuncs.pfnAddCommand ("+mlook", IN_MLookDown); - gEngfuncs.pfnAddCommand ("-mlook", IN_MLookUp); - gEngfuncs.pfnAddCommand ("+jlook", IN_JLookDown); - gEngfuncs.pfnAddCommand ("-jlook", IN_JLookUp); - gEngfuncs.pfnAddCommand ("+duck", IN_DuckDown); - gEngfuncs.pfnAddCommand ("-duck", IN_DuckUp); - // tankefugl: duck toggle - gEngfuncs.pfnAddCommand ("toggleduck", IN_DuckToggle); - // :tankefugl - gEngfuncs.pfnAddCommand ("+reload", IN_ReloadDown); - gEngfuncs.pfnAddCommand ("-reload", IN_ReloadUp); - gEngfuncs.pfnAddCommand ("+alt1", IN_Alt1Down); - gEngfuncs.pfnAddCommand ("-alt1", IN_Alt1Up); - gEngfuncs.pfnAddCommand ("+score", IN_ScoreDown); - gEngfuncs.pfnAddCommand ("-score", IN_ScoreUp); - gEngfuncs.pfnAddCommand ("+showscores", IN_ScoreDown); - gEngfuncs.pfnAddCommand ("-showscores", IN_ScoreUp); - gEngfuncs.pfnAddCommand ("+graph", IN_GraphDown); - gEngfuncs.pfnAddCommand ("-graph", IN_GraphUp); - gEngfuncs.pfnAddCommand ("+break",IN_BreakDown); - gEngfuncs.pfnAddCommand ("-break",IN_BreakUp); - - lookstrafe = gEngfuncs.pfnRegisterVariable ( "lookstrafe", "0", FCVAR_ARCHIVE ); - lookspring = gEngfuncs.pfnRegisterVariable ( "lookspring", "0", FCVAR_ARCHIVE ); - cl_anglespeedkey = gEngfuncs.pfnRegisterVariable ( "cl_anglespeedkey", "0.67", 0 ); - cl_yawspeed = gEngfuncs.pfnRegisterVariable ( "cl_yawspeed", "210", 0 ); - cl_pitchspeed = gEngfuncs.pfnRegisterVariable ( "cl_pitchspeed", "225", 0 ); - cl_upspeed = gEngfuncs.pfnRegisterVariable ( "cl_upspeed", "320", 0 ); - cl_movespeedkey = gEngfuncs.pfnRegisterVariable ( "cl_movespeedkey", "0.3", 0 ); - //cl_pitchup = gEngfuncs.pfnRegisterVariable ( "cl_pitchup", "89", 0 ); - //cl_pitchdown = gEngfuncs.pfnRegisterVariable ( "cl_pitchdown", "89", 0 ); - - cl_vsmoothing = gEngfuncs.pfnRegisterVariable ( "cl_vsmoothing", "0.05", FCVAR_ARCHIVE ); - - m_pitch = gEngfuncs.pfnRegisterVariable ( "m_pitch","0.022", FCVAR_ARCHIVE ); - m_yaw = gEngfuncs.pfnRegisterVariable ( "m_yaw","0.022", FCVAR_ARCHIVE ); - m_forward = gEngfuncs.pfnRegisterVariable ( "m_forward","1", FCVAR_ARCHIVE ); - m_side = gEngfuncs.pfnRegisterVariable ( "m_side","0.8", FCVAR_ARCHIVE ); - - cl_autohelp = gEngfuncs.pfnRegisterVariable ( kvAutoHelp, "1.0", FCVAR_ARCHIVE ); - cl_centerentityid = gEngfuncs.pfnRegisterVariable ( kvCenterEntityID, "0.0", FCVAR_ARCHIVE ); - cl_musicenabled = gEngfuncs.pfnRegisterVariable ( kvMusicEnabled, "1.0", FCVAR_ARCHIVE ); - cl_musicvolume = gEngfuncs.pfnRegisterVariable ( kvMusicVolume, "155", FCVAR_ARCHIVE ); - cl_musicdir = gEngfuncs.pfnRegisterVariable ( kvMusicDirectory, "", FCVAR_ARCHIVE); - cl_musicdelay = gEngfuncs.pfnRegisterVariable ( kvMusicDelay, "90", FCVAR_ARCHIVE); - cl_forcedefaultfov = gEngfuncs.pfnRegisterVariable ( kvForceDefaultFOV, "0", FCVAR_ARCHIVE ); - cl_dynamiclights = gEngfuncs.pfnRegisterVariable ( kvDynamicLights, "1", FCVAR_ARCHIVE ); - cl_buildmessages = gEngfuncs.pfnRegisterVariable ( kvBuildMessages, "1", FCVAR_ARCHIVE); - cl_quickselecttime = gEngfuncs.pfnRegisterVariable ( kvQuickSelectTime, ".15", FCVAR_ARCHIVE ); - cl_highdetail = gEngfuncs.pfnRegisterVariable ( kvHighDetail, "1", FCVAR_ARCHIVE ); - cl_cmhotkeys = gEngfuncs.pfnRegisterVariable ( kvCMHotkeys, "qwerasdfzxcv", FCVAR_ARCHIVE ); - cl_forcedefaultfov = gEngfuncs.pfnRegisterVariable ( kvForceDefaultFOV, "0", FCVAR_ARCHIVE ); - cl_particleinfo = gEngfuncs.pfnRegisterVariable ( kvParticleInfo, "0", FCVAR_ARCHIVE ); - - // Initialize third person camera controls. - CAM_Init(); - // Initialize inputs - IN_Init(); - // Initialize keyboard - KB_Init(); - // Initialize view system - V_Init(); -} - -/* -============ -ShutdownInput -============ -*/ -void ShutdownInput (void) -{ - IN_Shutdown(); - KB_Shutdown(); -} - -void CL_DLLEXPORT HUD_Shutdown( void ) -{ - RecClShutdown(); - - ShutdownInput(); - - gHUD.Shutdown(); -} +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: input.cpp $ +// $Date: 2002/10/16 02:12:21 $ +// +//------------------------------------------------------------------------------- +// $Log: input.cpp,v $ +// Revision 1.28 2002/10/16 02:12:21 Flayra +// - Valve anti-cheat integrated! +// +// Revision 1.27 2002/08/09 00:13:04 Flayra +// - Removed explicitly allowing specific commands. I can't remember why this was needed, but it doesn't appear to be anymore. +// +// Revision 1.26 2002/08/02 21:39:03 Flayra +// - Refactored variable names +// +// Revision 1.25 2002/07/08 16:13:31 Flayra +// - Fixed bug where command was able to switch weapons via mousewheel (bug #239) +// +//=============================================================================== +// cl.input.c -- builds an intended movement command to send to the server + +//xxxxxx Move bob and pitch drifting code here and other stuff from view if needed + +// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All +// rights reserved. +#include "hud.h" +#include "cl_util.h" +#include "camera.h" +extern "C" +{ +#include "kbutton.h" +} +#include "common/cvardef.h" +#include "common/usercmd.h" +#include "common/const.h" +#include "camera.h" +#include "in_defs.h" +#include "view.h" +#include +#include +#include "engine/keydefs.h" + +#include "vgui_TeamFortressViewport.h" +#include "mod/AvHServerVariables.h"// should we go ahead and just make a SharedVariables.h instead? +#include "mod/AvHClientVariables.h" +#include "mod/AvHMessage.h" +#include "fmod.h" +#include "mod/AvHScriptManager.h" +#include "mod/AvHScrollHandler.h" +#include "mod/AvHCommanderModeHandler.h" +#include "Util/Mat3.h" + +#include "engine/APIProxy.h" +#include "Exports.h" + +// : duck toggle +extern bool g_bDuckToggled; +// : + +extern int g_iAlive; + +extern int g_weaponselect; +extern cl_enginefunc_t gEngfuncs; +bool gResetViewAngles = false; +vec3_t gViewAngles; +extern AvHCommanderModeHandler gCommanderHandler; + +/////////////////////////////// +// Begin Max's Code +/////////////////////////////// + +float gPlayerAngles[3] = { 0, 0, 0 }; +float gTargetPlayerAngles[3] = { 0, 0, 0 }; +float gWorldViewAngles[3] = { 0, 0, 0 }; + +/////////////////////////////// +// End Max's Code +/////////////////////////////// + +// Defined in pm_math.c +//extern "C" float anglemod( float a ); +float anglemod( float a ); + +void IN_Init (void); +void IN_Move ( float frametime, float ioRotationDeltas[3], float ioTranslationDeltas[3]); +void IN_Shutdown( void ); +void V_Init( void ); +void VectorAngles( const float *forward, float *angles ); +int CL_ButtonBits( int ); + +// xxx need client dll function to get and clear impuse +extern cvar_t *in_joystick; + +int in_impulse = 0; +int in_cancel = 0; + +cvar_t *m_pitch; +cvar_t *m_yaw; +cvar_t *m_forward; +cvar_t *m_side; + +cvar_t *lookstrafe; +cvar_t *lookspring; +//cvar_t *cl_pitchup; +//cvar_t *cl_pitchdown; +cvar_t *cl_upspeed; +cvar_t *cl_movespeedkey; +cvar_t *cl_yawspeed; +cvar_t *cl_pitchspeed; +cvar_t *cl_anglespeedkey; +cvar_t *cl_vsmoothing; +cvar_t *cl_autohelp; +cvar_t *cl_centerentityid; +cvar_t *cl_musicenabled; +cvar_t *cl_musicdelay; +cvar_t *cl_musicvolume; +cvar_t *cl_musicdir; +cvar_t *cl_quickselecttime; +cvar_t *cl_highdetail; +cvar_t *cl_cmhotkeys; +cvar_t *cl_forcedefaultfov; +cvar_t *cl_dynamiclights; +cvar_t *cl_buildmessages; +cvar_t *cl_particleinfo; + +/* +=============================================================================== + +KEY BUTTONS + +Continuous button event tracking is complicated by the fact that two different +input sources (say, mouse button 1 and the control key) can both press the +same button, but the button should only be released when both of the +pressing key have been released. + +When a key event issues a button command (+forward, +attack, etc), it appends +its key number as a parameter to the command so it can be matched up with +the release. + +state bit 0 is the current state of the key +state bit 1 is edge triggered on the up to down transition +state bit 2 is edge triggered on the down to up transition + +=============================================================================== +*/ + + +kbutton_t in_mlook; +kbutton_t in_klook; +kbutton_t in_jlook; +kbutton_t in_left; +kbutton_t in_right; +kbutton_t in_forward; +kbutton_t in_back; +kbutton_t in_lookup; +kbutton_t in_lookdown; +kbutton_t in_moveleft; +kbutton_t in_moveright; +kbutton_t in_strafe; +kbutton_t in_speed; +kbutton_t in_use; +kbutton_t in_jump; +kbutton_t in_attack; +kbutton_t in_attack2; +kbutton_t in_up; +kbutton_t in_down; +kbutton_t in_duck; +kbutton_t in_reload; +kbutton_t in_alt1; +kbutton_t in_score; +kbutton_t in_break; +kbutton_t in_graph; // Display the netgraph + +typedef struct kblist_s +{ + struct kblist_s *next; + kbutton_t *pkey; + char name[32]; +} kblist_t; + +kblist_t *g_kbkeys = NULL; + +vector< pair > g_PrevCmds; + +/* +============ +KB_ConvertString + +Removes references to +use and replaces them with the keyname in the output string. If + a binding is unfound, then the original text is retained. +NOTE: Only works for text with +word in it. +============ +*/ +int KB_ConvertString( char *in, char **ppout ) +{ + char sz[ 4096 ]; + char binding[ 64 ]; + char *p; + char *pOut; + char *pEnd; + const char *pBinding; + + if ( !ppout ) + return 0; + + *ppout = NULL; + p = in; + pOut = sz; + while ( *p ) + { + if ( *p == '+' ) + { + pEnd = binding; + while ( *p && ( isalnum( *p ) || ( pEnd == binding ) ) && ( ( pEnd - binding ) < 63 ) ) + { + *pEnd++ = *p++; + } + + *pEnd = '\0'; + + pBinding = NULL; + if ( strlen( binding + 1 ) > 0 ) + { + // See if there is a binding for binding? + pBinding = gEngfuncs.Key_LookupBinding( binding + 1 ); + } + + if ( pBinding ) + { + *pOut++ = '['; + pEnd = (char *)pBinding; + } + else + { + pEnd = binding; + } + + while ( *pEnd ) + { + *pOut++ = *pEnd++; + } + + if ( pBinding ) + { + *pOut++ = ']'; + } + } + else + { + *pOut++ = *p++; + } + } + + *pOut = '\0'; + + pOut = ( char * )malloc( strlen( sz ) + 1 ); + strcpy( pOut, sz ); + *ppout = pOut; + + return 1; +} + +/* +============ +KB_Find + +Allows the engine to get a kbutton_t directly ( so it can check +mlook state, etc ) for saving out to .cfg files +============ +*/ +struct kbutton_s CL_DLLEXPORT *KB_Find( const char *name ) +{ + RecClFindKey(name); + + kblist_t *p; + p = g_kbkeys; + while ( p ) + { + if ( !stricmp( name, p->name ) ) + return p->pkey; + + p = p->next; + } + return NULL; +} + +/* +============ +KB_Add + +Add a kbutton_t * to the list of pointers the engine can retrieve via KB_Find +============ +*/ +void KB_Add( const char *name, kbutton_t *pkb ) +{ + kblist_t *p; + kbutton_t *kb; + + kb = KB_Find( name ); + + if ( kb ) + return; + + p = ( kblist_t * )malloc( sizeof( kblist_t ) ); + memset( p, 0, sizeof( *p ) ); + + strcpy( p->name, name ); + p->pkey = pkb; + + p->next = g_kbkeys; + g_kbkeys = p; +} + +/* +============ +KB_Init + +Add kbutton_t definitions that the engine can query if needed +============ +*/ +void KB_Init( void ) +{ + g_kbkeys = NULL; + + KB_Add( "in_graph", &in_graph ); + KB_Add( "in_mlook", &in_mlook ); + KB_Add( "in_jlook", &in_jlook ); +} + +/* +============ +KB_Shutdown + +Clear kblist +============ +*/ +void KB_Shutdown( void ) +{ + kblist_t *p, *n; + p = g_kbkeys; + while ( p ) + { + n = p->next; + free( p ); + p = n; + } + g_kbkeys = NULL; +} + +void KeyDown (kbutton_t *b); +void KeyUp (kbutton_t *b); +void KeyDownForced (kbutton_t *b); +void KeyUpForced (kbutton_t *b); + +/* +============ +KeyDown +============ +*/ +void KeyDown (kbutton_t *b) +{ + int k; + char *c; + + c = gEngfuncs.Cmd_Argv(1); + if (c[0]) + k = atoi(c); + else + k = -1; // typed manually at the console for continuous down + + int theBlockScripts = (int)gHUD.GetServerVariableFloat(kvBlockScripts); + + char *pCmd = gEngfuncs.Cmd_Argv(0); + + if(theBlockScripts && pCmd) + { + bool bFound = false; + + //Check thier last few commands (this prevents false positives if a player is hits several keys real fast) + for (int i = 0; i < g_PrevCmds.size(); i++) + { + //Check both the key pressed and the command it executed. + if(k == g_PrevCmds[i].first && !strcmp(pCmd, g_PrevCmds[i].second.c_str())) + { + bFound = true; + break; + } + } + + + if(!bFound + && strcmp(pCmd, "+mlook") + && strcmp(pCmd, "+jlook") + && strcmp(pCmd, "+showscores") + && strcmp(pCmd, "+use")) + { + gEngfuncs.pfnCenterPrint("Scripting is not allowed on this server."); + b->down[0] = b->down[1] = 0; + b->state = 4; // impulse up + return; + } + } + + if (k == b->down[0] || k == b->down[1]) + return; // repeating key + + if (!b->down[0]) + b->down[0] = k; + else if (!b->down[1]) + b->down[1] = k; + else + { + gEngfuncs.Con_DPrintf ("Three keys down for a button '%c' '%c' '%c'!\n", b->down[0], b->down[1], c); + return; + } + + if (b->state & 1) + return; // still down + b->state |= 1 + 2; // down + impulse down +} + +/* +============ +KeyDownForced +============ +*/ +void KeyDownForced (kbutton_t *b) +{ + b->down[0] = 0; + b->down[1] = 0; + + if (b->state & 1) + return; // still down + b->state |= 1 + 2; // down + impulse down +} + +/* +============ +KeyUp +============ +*/ +void KeyUp (kbutton_t *b) +{ + int k; + char *c; + + c = gEngfuncs.Cmd_Argv(1); + if (c[0]) + k = atoi(c); + else + { // typed manually at the console, assume for unsticking, so clear all + b->down[0] = b->down[1] = 0; + b->state = 4; // impulse up + return; + } + + if (b->down[0] == k) + b->down[0] = 0; + else if (b->down[1] == k) + b->down[1] = 0; + else + return; // key up without coresponding down (menu pass through) + if (b->down[0] || b->down[1]) + { + //Con_Printf ("Keys down for button: '%c' '%c' '%c' (%d,%d,%d)!\n", b->down[0], b->down[1], c, b->down[0], b->down[1], c); + return; // some other key is still holding it down + } + + if (!(b->state & 1)) + return; // still up (this should not happen) + + b->state &= ~1; // now up + b->state |= 4; // impulse up +} + +/* +============ +KeyUpForced +============ +*/ +void KeyUpForced (kbutton_t *b) +{ + b->state &= ~1; // now up + b->state |= 4; // impulse up +} + +bool AvHContainsBlockedCommands(const char* inInput) +{ + if (inInput == NULL) + { + return false; + } + + const char* kBlockedCommand[] = + { + "exec", + "wait", + "special", + "_special" + }; + + // Check for a ; indicating multiple commands. + + if (strchr(inInput, ';') != NULL) + { + return true; + } + + // Check if any of the blocked commands are being used. + + const char* theCommandEnd = strpbrk(inInput, " \t"); + + int theCommandLength; + + if (theCommandEnd == NULL) + { + theCommandLength = (int)strlen(inInput); + } + else + { + theCommandLength = theCommandEnd - inInput; + } + + for (int i = 0; i < sizeof(kBlockedCommand) / sizeof(const char*); ++i) + { + if ((int)strlen(kBlockedCommand[i]) == theCommandLength && + strncmp(inInput, kBlockedCommand[i], theCommandLength) == 0) + { + return true; + } + } + return false; +} + +/* +============ +HUD_Key_Event + +Return 1 to allow engine to process the key, otherwise, act on it as needed +============ +*/ +int CL_DLLEXPORT HUD_Key_Event( int down, int keynum, const char *pszCurrentBinding ) +{ + RecClKeyEvent(down, keynum, pszCurrentBinding); + + // Check to see if the event has any outlawed commands in it. + float theBlockScripts = gHUD.GetServerVariableFloat(kvBlockScripts); + + if (theBlockScripts && AvHContainsBlockedCommands(pszCurrentBinding)) + { + if(down)//: only show when going down. + gEngfuncs.pfnCenterPrint("Scripting is not allowed on this server.\n"); + return 0; + } + + if(pszCurrentBinding) + { + if(g_PrevCmds.size() >= 5) + g_PrevCmds.erase(g_PrevCmds.begin());//remove the oldest command + + g_PrevCmds.push_back(make_pair(keynum, (string)pszCurrentBinding)); + } + + int theProcessKeyBinding = 1; + +// char theKeyBinding[256] = "none"; +// if(pszCurrentBinding) +// { +// sprintf(theKeyBinding, pszCurrentBinding); +// } +// +// char theMessage[512]; +// sprintf(theMessage, "%s (down: %d, keynum %d)", theKeyBinding, down, keynum); +// CenterPrint(theMessage); + + if(gViewPort /*&& gViewPort->IsOptionsMenuVisible()*/) + { + + theProcessKeyBinding = gViewPort->KeyInput(down, keynum, pszCurrentBinding); + + if(pszCurrentBinding && (!strcmp(pszCurrentBinding, "toggleconsole") || !strcmp(pszCurrentBinding, "cancelselect"))) + { + theProcessKeyBinding = 1; + } + } + + + // Process topdown commands with precedence first + if(theProcessKeyBinding && gHUD.GetInTopDownMode()) + { + if((keynum != 0) && down/*&& pszCurrentBinding*/) + { + AvHMessageID theMessageID = gHUD.HotKeyHit(keynum); + if((theMessageID != MESSAGE_NULL) || (keynum == K_ESCAPE)) + { + // If ESC or cancel was hit, cancel ghost building + if((keynum == K_ESCAPE) || (theMessageID == MESSAGE_CANCEL)) + { + gHUD.SetGhostBuildingMode(MESSAGE_NULL); + } + + theProcessKeyBinding = 0; + } + +// // Else look for hotkey commands +// int thePrefixLength = strlen(kHotKeyPrefix); +// if(down && !strncmp(pszCurrentBinding, kHotKeyPrefix, thePrefixLength)) +// { +// // Strip out number, pass it to HUD +// char theNumberString[8]; +// memset(theNumberString, 0, 8); +// int theBindingLength = strlen(pszCurrentBinding); +// ASSERT(theBindingLength > thePrefixLength); +// strncpy(theNumberString, pszCurrentBinding + thePrefixLength, theBindingLength - thePrefixLength); +// +// int theBuildTech = MakeIntFromString(string(theNumberString)); +// +// gHUD.HotKeyHit(theBuildTech); +// +// theProcessKeyBinding = 0; +// } +// else +// { +// // Look for top down only commands, like scrolling +// if(!strcmp("+forward", pszCurrentBinding)) +// { +// AvHScrollHandler::ScrollUp(); +// theProcessKeyBinding = 0; +// } +// else if(!strcmp("-forward", pszCurrentBinding)) +// { +// AvHScrollHandler::StopScroll(); +// theProcessKeyBinding = 0; +// } +// else if(!strcmp("+back", pszCurrentBinding)) +// { +// AvHScrollHandler::ScrollDown(); +// theProcessKeyBinding = 0; +// } +// else if(!strcmp("-back", pszCurrentBinding)) +// { +// AvHScrollHandler::StopScroll(); +// theProcessKeyBinding = 0; +// } +// else if(!strcmp("+moveleft", pszCurrentBinding)) +// { +// AvHScrollHandler::ScrollLeft(); +// theProcessKeyBinding = 0; +// } +// else if(!strcmp("-moveleft", pszCurrentBinding)) +// { +// AvHScrollHandler::StopScroll(); +// theProcessKeyBinding = 0; +// } +// else if(!strcmp("+moveright", pszCurrentBinding)) +// { +// AvHScrollHandler::ScrollRight(); +// theProcessKeyBinding = 0; +// } +// else if(!strcmp("-moveright", pszCurrentBinding)) +// { +// AvHScrollHandler::StopScroll(); +// theProcessKeyBinding = 0; +// } +// } + } + } + + if(theProcessKeyBinding) + { + // Process only a couple keybindings in top down mode +// if(!gHUD.GetInTopDownMode() || (pszCurrentBinding && +// +// // Misc. commands +// (!strcmp(pszCurrentBinding, "toggleconsole") || !strcmp(pszCurrentBinding, "cancelselect") || !strcmp(pszCurrentBinding, "+showscores") || !strcmp(pszCurrentBinding, "-showscores") || !strcmp(pszCurrentBinding, "messagemode") || !strcmp(pszCurrentBinding, "messagemode2") || !strcmp(pszCurrentBinding, "snapshot") || !strcmp(pszCurrentBinding, "screenshot") || !strcmp(pszCurrentBinding, "+jump") || !strcmp(pszCurrentBinding, "addbot") || !strcmp(pszCurrentBinding, "+voicerecord") || !strcmp(pszCurrentBinding, "-voicerecord") || +// +// // Movement commands +// !strcmp(pszCurrentBinding, "testevent") /*|| !strcmp(pszCurrentBinding, "invnext") || !strcmp(pszCurrentBinding, "invprev")*/ || !strcmp(pszCurrentBinding, "+moveleft") || !strcmp(pszCurrentBinding, "-moveleft") || !strcmp(pszCurrentBinding, "+moveright") || !strcmp(pszCurrentBinding, "-moveright") || !strcmp(pszCurrentBinding, "+moveup") || !strcmp(pszCurrentBinding, "-moveup") || !strcmp(pszCurrentBinding, "+movedown") || !strcmp(pszCurrentBinding, "-movedown") || +// +// // For selecting groups +// !strcmp(pszCurrentBinding, "slot1") || !strcmp(pszCurrentBinding, "slot2") || !strcmp(pszCurrentBinding, "slot3") || !strcmp(pszCurrentBinding, "slot4") || !strcmp(pszCurrentBinding, "slot5") || +// +// // For creating groups +// !strcmp(pszCurrentBinding, "+duck") || !strcmp(pszCurrentBinding, "-duck") || +// +// // For testing ease +// !strcmp(pszCurrentBinding, "givepoints") +// +// ))) +// { + if (gViewPort) + theProcessKeyBinding = gViewPort->KeyInput(down, keynum, pszCurrentBinding); + +// // Don't +// if(!strcmp(pszCurrentBinding, "+jump") && gHUD.GetInTopDownMode()) +// { +// gHUD.GotoAlert(); +// } + +// } + } + + return theProcessKeyBinding; +} + +void IN_BreakDown( void ) { KeyDown( &in_break );}; +void IN_BreakUp( void ) { KeyUp( &in_break ); }; +void IN_KLookDown (void) {KeyDown(&in_klook);} +void IN_KLookUp (void) {KeyUp(&in_klook);} +void IN_JLookDown (void) {KeyDown(&in_jlook);} +void IN_JLookUp (void) {KeyUp(&in_jlook);} +void IN_MLookDown (void) {KeyDown(&in_mlook);} +void IN_UpDown(void) {KeyDown(&in_up);} +void IN_UpUp(void) {KeyUp(&in_up);} +void IN_DownDown(void) {KeyDown(&in_down);} +void IN_DownUp(void) {KeyUp(&in_down);} +void IN_LeftDown(void) {KeyDown(&in_left);} +void IN_LeftUp(void) {KeyUp(&in_left);} +void IN_RightDown(void) {KeyDown(&in_right);} +void IN_RightUp(void) {KeyUp(&in_right);} + +void IN_ForwardDown(void) +{ + KeyDown(&in_forward); + gHUD.m_Spectator.HandleButtonsDown( IN_FORWARD ); +} + +void IN_ForwardUp(void) +{ + KeyUp(&in_forward); + gHUD.m_Spectator.HandleButtonsUp( IN_FORWARD ); +} + +void IN_BackDown(void) +{ + KeyDown(&in_back); + gHUD.m_Spectator.HandleButtonsDown( IN_BACK ); +} + +void IN_BackUp(void) +{ + KeyUp(&in_back); + gHUD.m_Spectator.HandleButtonsUp( IN_BACK ); +} +void IN_LookupDown(void) {KeyDown(&in_lookup);} +void IN_LookupUp(void) {KeyUp(&in_lookup);} +void IN_LookdownDown(void) {KeyDown(&in_lookdown);} +void IN_LookdownUp(void) {KeyUp(&in_lookdown);} +void IN_MoveleftDown(void) +{ + KeyDown(&in_moveleft); + gHUD.m_Spectator.HandleButtonsDown( IN_MOVELEFT ); +} + +void IN_MoveleftUp(void) +{ + KeyUp(&in_moveleft); + gHUD.m_Spectator.HandleButtonsUp( IN_MOVELEFT ); +} + +void IN_MoverightDown(void) +{ + KeyDown(&in_moveright); + gHUD.m_Spectator.HandleButtonsDown( IN_MOVERIGHT ); +} + +void IN_MoverightUp(void) +{ + KeyUp(&in_moveright); + gHUD.m_Spectator.HandleButtonsUp( IN_MOVERIGHT ); +} +void IN_SpeedDown(void) {KeyDown(&in_speed);} +void IN_SpeedUp(void) {KeyUp(&in_speed);} +void IN_StrafeDown(void) {KeyDown(&in_strafe);} +void IN_StrafeUp(void) {KeyUp(&in_strafe);} +void IN_Attack2Down(void) {KeyDownForced(&in_attack2);} +void IN_Attack2Up(void) {KeyUpForced(&in_attack2);} +void IN_UseDown (void) +{ + KeyDown(&in_use); + gHUD.m_Spectator.HandleButtonsDown( IN_USE ); +} +void IN_UseUp (void) {KeyUp(&in_use);} +void IN_JumpDown (void) +{ + KeyDown(&in_jump); + + if(gHUD.GetInTopDownMode()) + { + //gHUD.GotoAlert(); + } + + gHUD.m_Spectator.HandleButtonsDown( IN_JUMP ); + +} + +void IN_JumpUp (void) +{ + KeyUp(&in_jump); + gHUD.m_Spectator.HandleButtonsUp( IN_JUMP ); +} + +void IN_DuckDown(void) +{ + KeyDown(&in_duck); + gHUD.m_Spectator.HandleButtonsDown( IN_DUCK ); +} + +void IN_DuckUp(void) {KeyUp(&in_duck);} +// : duck toggle +void IN_DuckToggle(void) +{ + g_bDuckToggled = !g_bDuckToggled; +} +// : +void IN_ReloadDown(void) {KeyDownForced(&in_reload);} +void IN_ReloadUp(void) {KeyUpForced(&in_reload);} +void IN_Alt1Down(void) {KeyDown(&in_alt1);} +void IN_Alt1Up(void) {KeyUp(&in_alt1);} +void IN_GraphDown(void) {KeyDown(&in_graph);} +void IN_GraphUp(void) {KeyUp(&in_graph);} + +void IN_AttackDown(void) +{ + KeyDown( &in_attack ); + gHUD.m_Spectator.HandleButtonsDown( IN_ATTACK ); +} + +void IN_AttackUp(void) +{ + KeyUp( &in_attack ); + in_cancel = 0; + IN_Attack2Up(); +} + +void IN_AttackDownForced(void) +{ + KeyDownForced( &in_attack ); +} + +void IN_AttackUpForced(void) +{ + KeyUpForced( &in_attack ); +} + +// Special handling +void IN_Cancel(void) +{ + in_cancel = 1; +} + +bool CheckInAttack(void) +{ + return (in_attack.state & 1 || in_attack2.state & 1); +} + +bool CheckInAttack2(void) +{ + return (in_attack2.state & 1); +} + +void IN_Impulse (void) +{ + //char msg[1024]; + //sprintf(msg, "in_impulse=%s\n", gEngfuncs.Cmd_Argv(1)); + //CenterPrint(msg); + + in_impulse = atoi( gEngfuncs.Cmd_Argv(1) ); +} + +void IN_ScoreDown(void) +{ + KeyDown(&in_score); + + if ( gViewPort ) + { + //if(gHUD.SwitchUIMode(SCOREBOARD_MODE)) + //{ + gViewPort->ShowScoreBoard(); + //} + } +} + +void IN_ScoreUp(void) +{ + KeyUp(&in_score); + + // Removed because it was getting called and hiding mouse <<< cgc >>> + if ( gViewPort ) + { + //if(gHUD.SwitchUIMode(MAIN_MODE)) + //{ + gViewPort->HideScoreBoard(); + //} + } +} + +void IN_MLookUp (void) +{ + /*KeyUp( &in_mlook ); + if ( !( in_mlook.state & 1 ) && lookspring->value ) + { + V_StartPitchDrift(); + }*/ +} + +/* +=============== +CL_KeyState + +Returns 0.25 if a key was pressed and released during the frame, +0.5 if it was pressed and held +0 if held then released, and +1.0 if held for the entire time +=============== +*/ +float CL_KeyState (kbutton_t *key) +{ + float val = 0.0; + int impulsedown, impulseup, down; + + impulsedown = key->state & 2; + impulseup = key->state & 4; + down = key->state & 1; + + if ( impulsedown && !impulseup ) + { + // pressed and held this frame? + val = down ? 0.5 : 0.0; + } + + if ( impulseup && !impulsedown ) + { + // released this frame? + val = down ? 0.0 : 0.0; + } + + if ( !impulsedown && !impulseup ) + { + // held the entire frame? + val = down ? 1.0 : 0.0; + } + + if ( impulsedown && impulseup ) + { + if ( down ) + { + // released and re-pressed this frame + val = 0.75; + } + else + { + // pressed and released this frame + val = 0.25; + } + } + + // clear impulses + key->state &= 1; + return val; +} + +/* +================ +CL_AdjustAngles + +Moves the local angle positions +================ +*/ +void CL_AdjustAngles ( float frametime, float *viewangles ) +{ + float speed; + float up, down; + + if (in_speed.state & 1) + { + speed = frametime * cl_anglespeedkey->value; + } + else + { + speed = frametime; + } + + if (!(in_strafe.state & 1)) + { + viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right); + viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left); + viewangles[YAW] = anglemod(viewangles[YAW]); + } + if (in_klook.state & 1) + { + V_StopPitchDrift (); + viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_forward); + viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_back); + } + + up = CL_KeyState (&in_lookup); + down = CL_KeyState(&in_lookdown); + + viewangles[PITCH] -= speed*cl_pitchspeed->value * up; + viewangles[PITCH] += speed*cl_pitchspeed->value * down; + + if (up || down) + V_StopPitchDrift (); + + if (viewangles[PITCH] > 89) + viewangles[PITCH] = 89; + if (viewangles[PITCH] < -89) + viewangles[PITCH] = -89; + + if (viewangles[ROLL] > 50) + viewangles[ROLL] = 50; + if (viewangles[ROLL] < -50) + viewangles[ROLL] = -50; +} + +/* +================ +CL_CreateMove + +Send the intended movement message to the server +if active == 1 then we are 1) not playing back demos ( where our commands are ignored ) and +2 ) we have finished signing on to server +================ +*/ +void CL_DLLEXPORT CL_CreateMove ( float frametime, struct usercmd_s *cmd, int active ) +{ + RecClCL_CreateMove(frametime, cmd, active); + + float spd; + vec3_t viewangles; + static vec3_t oldangles; + + if ( active && (!gViewPort || !gViewPort->IsOptionsMenuVisible()) /*&& !gHUD.GetShowingMap()*/) + { + int theButtonState = CL_ButtonBits( 1 ); + + memset (cmd, 0, sizeof(*cmd)); + + float theRotationDeltas[3] = {0,0,0}; + float theTranslationDeltas[3] = {0,0,0}; + + IN_Move(frametime,theRotationDeltas,theTranslationDeltas); + + if(gResetViewAngles) + { + VectorCopy(gViewAngles,viewangles); + gResetViewAngles = false; + } + else + { + gEngfuncs.GetViewAngles( (float *)viewangles ); + } + VectorAdd(viewangles,theRotationDeltas,viewangles); + CL_AdjustAngles ( frametime, viewangles ); + + gEngfuncs.SetViewAngles( (float *)viewangles ); + VectorCopy (viewangles,gWorldViewAngles); + + // If we're in topdown mode + bool theProcessedMove = false; + bool theIsSendingSpecialEvent = false; + bool theOverrideImpulse = false; + float theWorldX, theWorldY; + int theScriptImpulse = 0; + + AvHMessageID theAlienAbility = MESSAGE_NULL; + AvHMessageID theGroupMessage = MESSAGE_NULL; + + //int theUpgradeVar = gEngfuncs.GetLocalPlayer()->curstate.iuser4; + //bool theIsParalyzed = GetHasUpgrade(theUpgradeVar, PLAYER_PARALYZED); + if(AvHScriptManager::Instance()->GetClientMove(theButtonState, theScriptImpulse)) + { + if(theScriptImpulse) + { + theOverrideImpulse = true; + } + //theProcessedMove = true; + + // TODO: Pass theButtonState to override all CL_KeyState() calls + } + else if(gHUD.GetInTopDownMode()) + { + cmd->upmove = cmd->sidemove = cmd->forwardmove = 0; + + // If a button was JUST pressed or released + vec3_t theMouseNormPos; + AvHMessageID theTechEvent = MESSAGE_NULL; + if(gCommanderHandler.GetMoveToWorldPosition(theWorldX, theWorldY)) + { + // Commander wants to scroll to an area of the mini-map + cmd->impulse = COMMANDER_MOVETO; + cmd->upmove = theWorldX/kWorldPosNetworkConstant; + cmd->sidemove = theWorldY/kWorldPosNetworkConstant; + gCommanderHandler.ClearMoveToPosition(); + gHUD.ClearTrackingEntity(); + + theIsSendingSpecialEvent = true; + theProcessedMove = true; + } + else if(gCommanderHandler.GetDefaultOrderPosition(theWorldX, theWorldY)) + { + // Commander wants to scroll to an area of the mini-map + cmd->impulse = COMMANDER_DEFAULTORDER; + cmd->upmove = theWorldX/kWorldPosNetworkConstant; + cmd->sidemove = theWorldY/kWorldPosNetworkConstant; + gCommanderHandler.ClearDefaultOrderPosition(); + gHUD.ClearTrackingEntity(); + + theIsSendingSpecialEvent = true; + theProcessedMove = true; + } + else if(gHUD.GetAndClearTechEvent(theTechEvent)) + { + cmd->impulse = theTechEvent; + theProcessedMove = true; + theIsSendingSpecialEvent = true; + gHUD.ClearTrackingEntity(); + + } + // else scroll + else + { + // Scroll the view if the HUD tells us to, otherwise use normal key presses + int theScrollX = 0, theScrollY = 0, theScrollZ = 0; + gHUD.GetAndClearTopDownScrollAmount(theScrollX, theScrollY, theScrollZ); + + if(theScrollX || theScrollY || theScrollZ) + { + // Commander move speed + float kCommanderMoveSpeed = 1000; + cmd->upmove += kCommanderMoveSpeed * theScrollY; + cmd->sidemove += kCommanderMoveSpeed * theScrollX; + cmd->forwardmove += kCommanderMoveSpeed * theScrollZ; + + cmd->impulse = COMMANDER_SCROLL; + theOverrideImpulse = true; + + gHUD.ClearTrackingEntity(); + + //theIsSendingSpecialEvent = true; + theProcessedMove = true; + } + else if(gHUD.GetAndClearGroupEvent(theGroupMessage)) + { + cmd->impulse = theGroupMessage; + theIsSendingSpecialEvent = true; + theProcessedMove = true; + + gHUD.SetLastHotkeySelectionEvent(theGroupMessage); + } +// else if(gHUD.GetTrackingEntity() > 0) +// { +// int theTrackingEntity = gHUD.GetTrackingEntity(); +// cmd->upmove = theTrackingEntity*kHotKeyNetworkFactor; +// cmd->impulse = COMMANDER_TRACKENTITY; +// +// theIsSendingSpecialEvent = true; +// theProcessedMove = true; +// } + else if(in_impulse != 0) + { + bool theProcessImpulse = false; + switch(in_impulse) + { + case COMMANDER_SELECTALL: + case COMMANDER_NEXTIDLE: + case COMMANDER_NEXTAMMO: + case COMMANDER_NEXTHEALTH: + theProcessImpulse = true; + break; + } + + if(theProcessImpulse) + { + cmd->impulse = in_impulse; + in_impulse = 0; + + theProcessedMove = true; + theIsSendingSpecialEvent = true; + } + } + + if(!theProcessedMove && gHUD.GetAndClearSelectionEvent(theMouseNormPos, theTechEvent)) + { + // Store world position x,y in upmove,sidemove + cmd->upmove = theMouseNormPos.x*kSelectionNetworkConstant; + cmd->sidemove = theMouseNormPos.y*kSelectionNetworkConstant; + cmd->forwardmove = theMouseNormPos.z*kSelectionNetworkConstant; + + // Set impulse indicating this + //cmd->impulse = COMMANDER_MOUSECOORD; + + // This could be COMMANDER_MOUSECOORD or BUILD_TURRET or one of the other BUILD_ events + // They are all sent the same way + cmd->impulse = theTechEvent; + + // Order mode isn't currently used but may be in the future + //cmd->weaponselect = gHUD.GetOrderMode(); + + // Set buttons. Attack gets turned off when we're in mouse mode (apparently) + // so we need to set the buttons manually + cmd->buttons = theButtonState; + if(gHUD.GetMouseOneDown()) + { + cmd->buttons |= IN_ATTACK; + } + if(gHUD.GetMouseTwoDown()) + { + cmd->buttons |= IN_ATTACK2; + } + + gHUD.ClearTrackingEntity(); + + theIsSendingSpecialEvent = true; + theProcessedMove = true; + } + } + } + else if(gHUD.GetAndClearAlienAbility(theAlienAbility)) + { + cmd->impulse = theAlienAbility; + + // Added by mmcguire. + // 255 signifies that the impulse came from us and not from the console. + cmd->weaponselect = 255; + + theProcessedMove = true; + theIsSendingSpecialEvent = true; + + } + + // else process move normally + if(!theProcessedMove) + { + if ( in_strafe.state & 1 ) + { + cmd->sidemove += kSideSpeed * CL_KeyState (&in_right); + cmd->sidemove -= kSideSpeed * CL_KeyState (&in_left); + } + + cmd->sidemove += kSideSpeed * CL_KeyState (&in_moveright); + cmd->sidemove -= kSideSpeed * CL_KeyState (&in_moveleft); + + cmd->upmove += cl_upspeed->value * CL_KeyState (&in_up); + cmd->upmove -= cl_upspeed->value * CL_KeyState (&in_down); + + if ( !(in_klook.state & 1 ) ) + { + cmd->forwardmove += kForwardSpeed * CL_KeyState (&in_forward); + cmd->forwardmove -= kBackSpeed * CL_KeyState (&in_back); + } + } + + if(!theIsSendingSpecialEvent) + { + // adjust for speed key + if ( in_speed.state & 1 ) + { + cmd->forwardmove *= cl_movespeedkey->value; + cmd->sidemove *= cl_movespeedkey->value; + cmd->upmove *= cl_movespeedkey->value; + } + + // clip to maxspeed + spd = gEngfuncs.GetClientMaxspeed(); + + if ( spd != 0.0 ) + { + // scale the 3 speeds so that the total velocity is not > cl.maxspeed + float fmov = sqrt( (cmd->forwardmove*cmd->forwardmove) + (cmd->sidemove*cmd->sidemove) + (cmd->upmove*cmd->upmove) ); + + if ( fmov > spd ) + { + float fratio = spd / fmov; + cmd->forwardmove *= fratio; + cmd->sidemove *= fratio; + cmd->upmove *= fratio; + } + } + + // Allow mice and other controllers to add their inputs + cmd->forwardmove += theTranslationDeltas[0]; + cmd->sidemove += theTranslationDeltas[1]; + cmd->upmove += theTranslationDeltas[2]; + + if(!theOverrideImpulse) + { + cmd->impulse = in_impulse; + in_impulse = 0; + } + + cmd->weaponselect = g_weaponselect; + g_weaponselect = 0; + + // + // set button and flag bits + // + cmd->buttons = theButtonState; + + // If they're in a modal dialog, or we're stunned, ignore the attack button. + int theLocalUpgrades = gHUD.GetLocalUpgrades(); + if( GetClientVoiceMgr()->IsInSquelchMode() ) + { + cmd->buttons &= ~IN_ATTACK; + } + + // Using joystick? + if ( in_joystick->value ) + { + if ( cmd->forwardmove > 0 ) + { + cmd->buttons |= IN_FORWARD; + } + else if ( cmd->forwardmove < 0 ) + { + cmd->buttons |= IN_BACK; + } + } + } + } + + gEngfuncs.GetViewAngles( (float *)viewangles ); + + // Set current view angles but not if frozen (this still allows you to rotate in first-person, but player model won't change) + int theUser4 = gHUD.GetLocalUpgrades(); + bool theIsFrozen = GetHasUpgrade(theUser4, MASK_PLAYER_STUNNED) || GetHasUpgrade(theUser4, MASK_ALIEN_EMBRYO); + + if ( g_iAlive && !theIsFrozen) + { + VectorCopy( viewangles, cmd->viewangles ); + VectorCopy( viewangles, oldangles ); + } + else + { + VectorCopy( oldangles, cmd->viewangles ); + } +} + +/* +============ +CL_IsDead + +Returns 1 if health is <= 0 +============ +*/ +int CL_IsDead( void ) +{ + return ( gHUD.m_Health.m_iHealth <= 0 ) ? 1 : 0; +} + +/* +============ +CL_ButtonBits + +Returns appropriate button info for keyboard and mouse state +Set bResetState to 1 to clear old state info +============ +*/ +int CL_ButtonBits( int bResetState ) +{ + int bits = 0; + + if ( in_attack.state & 3 ) + { + bits |= IN_ATTACK; + } + + if ( in_speed.state & 3 ) + { + bits |= IN_WALK; + } + + // : duck toggle + if ( g_bDuckToggled ) + { + if (!(in_duck.state & 3)) + { + bits |= IN_DUCK; + } + } + else if (in_duck.state & 3) + { + bits |= IN_DUCK; + } + // : + + if (in_jump.state & 3) + { + bits |= IN_JUMP; + } + + if ( in_forward.state & 3 ) + { + bits |= IN_FORWARD; + } + + if (in_back.state & 3) + { + bits |= IN_BACK; + } + + if (in_use.state & 3) + { + bits |= IN_USE; + } + + if (in_cancel) + { + bits |= IN_CANCEL; + } + + if ( in_left.state & 3 ) + { + bits |= IN_LEFT; + } + + if (in_right.state & 3) + { + bits |= IN_RIGHT; + } + + if ( in_moveleft.state & 3 ) + { + bits |= IN_MOVELEFT; + } + + if (in_moveright.state & 3) + { + bits |= IN_MOVERIGHT; + } + + if (in_attack2.state & 3) + { + bits |= IN_ATTACK2; + } + + if (in_reload.state & 3) + { + bits |= IN_RELOAD; + } + + if (in_alt1.state & 3) + { + bits |= IN_ALT1; + } + + if ( in_score.state & 3 ) + { + bits |= IN_SCORE; + } + + // Dead or in intermission? Shore scoreboard, too + if ( CL_IsDead() || gHUD.m_iIntermission ) + { + bits |= IN_SCORE; + } + + if ( bResetState ) + { + in_attack.state &= ~2; + in_speed.state &= ~2; + in_duck.state &= ~2; + in_jump.state &= ~2; + in_forward.state &= ~2; + in_back.state &= ~2; + in_use.state &= ~2; + in_left.state &= ~2; + in_right.state &= ~2; + in_moveleft.state &= ~2; + in_moveright.state &= ~2; + in_attack2.state &= ~2; + in_reload.state &= ~2; + in_alt1.state &= ~2; + in_score.state &= ~2; + } + + return bits; +} + +/* +============ +CL_ResetButtonBits + +============ +*/ +void CL_ResetButtonBits( int bits ) +{ + int bitsNew = CL_ButtonBits( 0 ) ^ bits; + + // Has the attack button been changed + if ( bitsNew & IN_ATTACK ) + { + // Was it pressed? or let go? + if ( bits & IN_ATTACK ) + { + KeyDown( &in_attack ); + } + else + { + // totally clear state + in_attack.state &= ~7; + in_attack2.state &= ~7; + } + } +} + +/* +============ +InitInput +============ +*/ +void InitInput (void) +{ + gEngfuncs.pfnAddCommand ("+moveup",IN_UpDown); + gEngfuncs.pfnAddCommand ("-moveup",IN_UpUp); + gEngfuncs.pfnAddCommand ("+movedown",IN_DownDown); + gEngfuncs.pfnAddCommand ("-movedown",IN_DownUp); + gEngfuncs.pfnAddCommand ("+left",IN_LeftDown); + gEngfuncs.pfnAddCommand ("-left",IN_LeftUp); + gEngfuncs.pfnAddCommand ("+right",IN_RightDown); + gEngfuncs.pfnAddCommand ("-right",IN_RightUp); + gEngfuncs.pfnAddCommand ("+forward",IN_ForwardDown); + gEngfuncs.pfnAddCommand ("-forward",IN_ForwardUp); + gEngfuncs.pfnAddCommand ("+back",IN_BackDown); + gEngfuncs.pfnAddCommand ("-back",IN_BackUp); + gEngfuncs.pfnAddCommand ("+lookup", IN_LookupDown); + gEngfuncs.pfnAddCommand ("-lookup", IN_LookupUp); + gEngfuncs.pfnAddCommand ("+lookdown", IN_LookdownDown); + gEngfuncs.pfnAddCommand ("-lookdown", IN_LookdownUp); + gEngfuncs.pfnAddCommand ("+strafe", IN_StrafeDown); + gEngfuncs.pfnAddCommand ("-strafe", IN_StrafeUp); + gEngfuncs.pfnAddCommand ("+moveleft", IN_MoveleftDown); + gEngfuncs.pfnAddCommand ("-moveleft", IN_MoveleftUp); + gEngfuncs.pfnAddCommand ("+moveright", IN_MoverightDown); + gEngfuncs.pfnAddCommand ("-moveright", IN_MoverightUp); + gEngfuncs.pfnAddCommand ("+speed", IN_SpeedDown); + gEngfuncs.pfnAddCommand ("-speed", IN_SpeedUp); + gEngfuncs.pfnAddCommand ("+attack", IN_AttackDown); + gEngfuncs.pfnAddCommand ("-attack", IN_AttackUp); + //gEngfuncs.pfnAddCommand ("+movement", IN_Attack2Down); + //gEngfuncs.pfnAddCommand ("-movement", IN_Attack2Up); + gEngfuncs.pfnAddCommand ("+use", IN_UseDown); + gEngfuncs.pfnAddCommand ("-use", IN_UseUp); + gEngfuncs.pfnAddCommand ("+jump", IN_JumpDown); + gEngfuncs.pfnAddCommand ("-jump", IN_JumpUp); + gEngfuncs.pfnAddCommand ("impulse", IN_Impulse); + gEngfuncs.pfnAddCommand ("+klook", IN_KLookDown); + gEngfuncs.pfnAddCommand ("-klook", IN_KLookUp); + gEngfuncs.pfnAddCommand ("+mlook", IN_MLookDown); + gEngfuncs.pfnAddCommand ("-mlook", IN_MLookUp); + gEngfuncs.pfnAddCommand ("+jlook", IN_JLookDown); + gEngfuncs.pfnAddCommand ("-jlook", IN_JLookUp); + gEngfuncs.pfnAddCommand ("+duck", IN_DuckDown); + gEngfuncs.pfnAddCommand ("-duck", IN_DuckUp); + // : duck toggle + gEngfuncs.pfnAddCommand ("toggleduck", IN_DuckToggle); + // : + gEngfuncs.pfnAddCommand ("+reload", IN_ReloadDown); + gEngfuncs.pfnAddCommand ("-reload", IN_ReloadUp); + gEngfuncs.pfnAddCommand ("+alt1", IN_Alt1Down); + gEngfuncs.pfnAddCommand ("-alt1", IN_Alt1Up); + gEngfuncs.pfnAddCommand ("+score", IN_ScoreDown); + gEngfuncs.pfnAddCommand ("-score", IN_ScoreUp); + gEngfuncs.pfnAddCommand ("+showscores", IN_ScoreDown); + gEngfuncs.pfnAddCommand ("-showscores", IN_ScoreUp); + gEngfuncs.pfnAddCommand ("+graph", IN_GraphDown); + gEngfuncs.pfnAddCommand ("-graph", IN_GraphUp); + gEngfuncs.pfnAddCommand ("+break",IN_BreakDown); + gEngfuncs.pfnAddCommand ("-break",IN_BreakUp); + + lookstrafe = gEngfuncs.pfnRegisterVariable ( "lookstrafe", "0", FCVAR_ARCHIVE ); + lookspring = gEngfuncs.pfnRegisterVariable ( "lookspring", "0", FCVAR_ARCHIVE ); + cl_anglespeedkey = gEngfuncs.pfnRegisterVariable ( "cl_anglespeedkey", "0.67", 0 ); + cl_yawspeed = gEngfuncs.pfnRegisterVariable ( "cl_yawspeed", "210", 0 ); + cl_pitchspeed = gEngfuncs.pfnRegisterVariable ( "cl_pitchspeed", "225", 0 ); + cl_upspeed = gEngfuncs.pfnRegisterVariable ( "cl_upspeed", "320", 0 ); + cl_movespeedkey = gEngfuncs.pfnRegisterVariable ( "cl_movespeedkey", "0.3", 0 ); + //cl_pitchup = gEngfuncs.pfnRegisterVariable ( "cl_pitchup", "89", 0 ); + //cl_pitchdown = gEngfuncs.pfnRegisterVariable ( "cl_pitchdown", "89", 0 ); + + cl_vsmoothing = gEngfuncs.pfnRegisterVariable ( "cl_vsmoothing", "0.05", FCVAR_ARCHIVE ); + + m_pitch = gEngfuncs.pfnRegisterVariable ( "m_pitch","0.022", FCVAR_ARCHIVE ); + m_yaw = gEngfuncs.pfnRegisterVariable ( "m_yaw","0.022", FCVAR_ARCHIVE ); + m_forward = gEngfuncs.pfnRegisterVariable ( "m_forward","1", FCVAR_ARCHIVE ); + m_side = gEngfuncs.pfnRegisterVariable ( "m_side","0.8", FCVAR_ARCHIVE ); + + cl_autohelp = gEngfuncs.pfnRegisterVariable ( kvAutoHelp, "1.0", FCVAR_ARCHIVE ); + cl_centerentityid = gEngfuncs.pfnRegisterVariable ( kvCenterEntityID, "0.0", FCVAR_ARCHIVE ); + cl_musicenabled = gEngfuncs.pfnRegisterVariable ( kvMusicEnabled, "1.0", FCVAR_ARCHIVE ); + cl_musicvolume = gEngfuncs.pfnRegisterVariable ( kvMusicVolume, "155", FCVAR_ARCHIVE ); + cl_musicdir = gEngfuncs.pfnRegisterVariable ( kvMusicDirectory, "", FCVAR_ARCHIVE); + cl_musicdelay = gEngfuncs.pfnRegisterVariable ( kvMusicDelay, "90", FCVAR_ARCHIVE); + cl_forcedefaultfov = gEngfuncs.pfnRegisterVariable ( kvForceDefaultFOV, "0", FCVAR_ARCHIVE ); + cl_dynamiclights = gEngfuncs.pfnRegisterVariable ( kvDynamicLights, "1", FCVAR_ARCHIVE ); + cl_buildmessages = gEngfuncs.pfnRegisterVariable ( kvBuildMessages, "1", FCVAR_ARCHIVE); + cl_quickselecttime = gEngfuncs.pfnRegisterVariable ( kvQuickSelectTime, ".15", FCVAR_ARCHIVE ); + cl_highdetail = gEngfuncs.pfnRegisterVariable ( kvHighDetail, "1", FCVAR_ARCHIVE ); + cl_cmhotkeys = gEngfuncs.pfnRegisterVariable ( kvCMHotkeys, "qwerasdfzxcv", FCVAR_ARCHIVE ); + cl_forcedefaultfov = gEngfuncs.pfnRegisterVariable ( kvForceDefaultFOV, "0", FCVAR_ARCHIVE ); + cl_particleinfo = gEngfuncs.pfnRegisterVariable ( kvParticleInfo, "0", FCVAR_ARCHIVE ); + + // Initialize third person camera controls. + CAM_Init(); + // Initialize inputs + IN_Init(); + // Initialize keyboard + KB_Init(); + // Initialize view system + V_Init(); +} + +/* +============ +ShutdownInput +============ +*/ +void ShutdownInput (void) +{ + IN_Shutdown(); + KB_Shutdown(); +} + +void CL_DLLEXPORT HUD_Shutdown( void ) +{ + RecClShutdown(); + + ShutdownInput(); + + gHUD.Shutdown(); +} diff --git a/main/source/cl_dll/inputw32.cpp b/main/source/cl_dll/inputw32.cpp index 2128dc2..a02a3d9 100644 --- a/main/source/cl_dll/inputw32.cpp +++ b/main/source/cl_dll/inputw32.cpp @@ -427,7 +427,7 @@ void IN_MouseMove ( float frametime, float ioRotationDeltas[3], float ioTranslat } } -/* + //#define TRACE_TEST #if defined( TRACE_TEST ) { @@ -437,7 +437,7 @@ void IN_MouseMove ( float frametime, float ioRotationDeltas[3], float ioTranslat V_Move( mx, my ); } #endif -*/ + } /* diff --git a/main/source/cl_dll/saytext.cpp b/main/source/cl_dll/saytext.cpp index 2699e35..45e7795 100644 --- a/main/source/cl_dll/saytext.cpp +++ b/main/source/cl_dll/saytext.cpp @@ -1,376 +1,378 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -// -// saytext.cpp -// -// implementation of CHudSayText class -// - -#include "hud.h" -#include "cl_util.h" -#include "mod/AvHNetworkMessages.h" - -#include -#include - -#include "vgui_TeamFortressViewport.h" -#include "mod/AvHClientUtil.h" -#include "ui/ChatPanel.h" - -float* GetClientColor(int clientIndex); - -#define MAX_LINES 5 -#define MAX_CHARS_PER_LINE 256 /* it can be less than this, depending on char size */ - -// allow 20 pixels on either side of the text -#define MAX_LINE_WIDTH ( ScreenWidth() - 40 ) -#define LINE_START 10 -static float SCROLL_SPEED = 5; - -static char g_szLineBuffer[ MAX_LINES + 1 ][ MAX_CHARS_PER_LINE ]; -static float *g_pflNameColors[ MAX_LINES + 1 ]; -static int g_iNameLengths[ MAX_LINES + 1 ]; -static float flScrollTime = 0; // the time at which the lines next scroll up - -static int Y_START = 0; -static int line_height = 0; - -DECLARE_MESSAGE( m_SayText, SayText ); - -int CHudSayText :: Init( void ) -{ - gHUD.AddHudElem( this ); - - HOOK_MESSAGE( SayText ); - - InitHUDData(); - - m_HUD_saytext = gEngfuncs.pfnRegisterVariable( "hud_saytext", "1", 0 ); - m_HUD_saytext_time = gEngfuncs.pfnRegisterVariable( "hud_saytext_time", "5", 0 ); - - m_iFlags |= HUD_INTERMISSION; // is always drawn during an intermission - - return 1; -} - - -void CHudSayText :: InitHUDData( void ) -{ - memset( g_szLineBuffer, 0, sizeof g_szLineBuffer ); - memset( g_pflNameColors, 0, sizeof g_pflNameColors ); - memset( g_iNameLengths, 0, sizeof g_iNameLengths ); -} - -int CHudSayText :: VidInit( void ) -{ - return 1; -} - - -int ScrollTextUp( void ) -{ - ConsolePrint( g_szLineBuffer[0] ); // move the first line into the console buffer - g_szLineBuffer[MAX_LINES][0] = 0; - memmove( g_szLineBuffer[0], g_szLineBuffer[1], sizeof(g_szLineBuffer) - sizeof(g_szLineBuffer[0]) ); // overwrite the first line - memmove( &g_pflNameColors[0], &g_pflNameColors[1], sizeof(g_pflNameColors) - sizeof(g_pflNameColors[0]) ); - memmove( &g_iNameLengths[0], &g_iNameLengths[1], sizeof(g_iNameLengths) - sizeof(g_iNameLengths[0]) ); - g_szLineBuffer[MAX_LINES-1][0] = 0; - - if ( g_szLineBuffer[0][0] == ' ' ) // also scroll up following lines - { - g_szLineBuffer[0][0] = 2; - return 1 + ScrollTextUp(); - } - - return 1; -} - -int CHudSayText :: Draw( float flTime ) -{ - int y = Y_START; - - if ( ( gViewPort && gViewPort->AllowedToPrintText() == FALSE) || !m_HUD_saytext->value ) - return 1; - - // make sure the scrolltime is within reasonable bounds, to guard against the clock being reset - flScrollTime = min( flScrollTime, flTime + m_HUD_saytext_time->value ); - - // make sure the scrolltime is within reasonable bounds, to guard against the clock being reset - flScrollTime = min( flScrollTime, flTime + m_HUD_saytext_time->value ); - - if ( flScrollTime <= flTime ) - { - if ( *g_szLineBuffer[0] ) - { - flScrollTime = flTime + m_HUD_saytext_time->value; - // push the console up - ScrollTextUp(); - } - else - { // buffer is empty, just disable drawing of this section - m_iFlags &= ~HUD_ACTIVE; - } - } - - for ( int i = 0; i < MAX_LINES; i++ ) - { - if ( *g_szLineBuffer[i] ) - { - if ( *g_szLineBuffer[i] == 2 && g_pflNameColors[i] ) - { - // it's a saytext string - static char buf[MAX_PLAYER_NAME_LENGTH+32]; - - // draw the first x characters in the player color - strncpy( buf, g_szLineBuffer[i], min(g_iNameLengths[i], MAX_PLAYER_NAME_LENGTH+32) ); - buf[ min(g_iNameLengths[i], MAX_PLAYER_NAME_LENGTH+31) ] = 0; - DrawSetTextColor( g_pflNameColors[i][0], g_pflNameColors[i][1], g_pflNameColors[i][2] ); - - // If we're an alien, move chat over a bit so it doesn't overlap energy bar - int theDrawX = LINE_START; - //if(gHUD.GetIsAlien()) - //{ - // theDrawX += .07f*ScreenWidth; - //} - int x = DrawConsoleString(theDrawX, y, buf ); - - // color is reset after each string draw - DrawConsoleString( x, y, g_szLineBuffer[i] + g_iNameLengths[i] ); - } - else - { - // normal draw - DrawConsoleString( LINE_START, y, g_szLineBuffer[i] ); - } - } - - y += line_height; - } - - - return 1; -} - -int CHudSayText :: MsgFunc_SayText( const char *pszName, int iSize, void *pbuf ) -{ - - int client_index; - string content, location; - NetMsg_SayText( pbuf, iSize, client_index, content, location ); - - string theTranslatedLocation; - if(LocalizeString(location.c_str(), theTranslatedLocation)) - { - // If player is on our team, add location - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(client_index); - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - - if(theEntity && theLocalPlayer && (theEntity->curstate.team == theLocalPlayer->curstate.team)) - { - // Search for first : so we can insert location - int theColonIndex = (int)content.find_first_of(":"); - if((theColonIndex > 0) && (theColonIndex < (int)content.length())) - { - AvHCUTrimExtraneousLocationText(theTranslatedLocation); - - // Insert location - string theNewMessage = content.substr(0, theColonIndex); - theNewMessage += " ("; - - theNewMessage += theTranslatedLocation; - theNewMessage += ")"; - theNewMessage += content.substr(theColonIndex); - - // Replace the message with new one - content = theNewMessage; - } - } - } - - SayTextPrint(content.c_str(), (int)content.length(), client_index ); - - return 1; -} - -void CHudSayText :: SayTextPrint( const char *pszBuf, int iBufSize, int clientIndex ) -{ - if ( gViewPort && gViewPort->AllowedToPrintText() == FALSE ) - { - // Print it straight to the console - ConsolePrint( pszBuf ); - return; - } - - // find an empty string slot - for ( int i = 0; i < MAX_LINES; i++ ) - { - if ( ! *g_szLineBuffer[i] ) - break; - } - if ( i == MAX_LINES ) - { - // force scroll buffer up - ScrollTextUp(); - i = MAX_LINES - 1; - } - - g_iNameLengths[i] = 0; - g_pflNameColors[i] = NULL; - - // if it's a say message, search for the players name in the string - if ( *pszBuf == 2 && clientIndex > 0 ) - { - GetPlayerInfo( clientIndex, &g_PlayerInfoList[clientIndex] ); - const char *pName = g_PlayerInfoList[clientIndex].name; - - if ( pName ) - { - const char *nameInString = strstr( pszBuf, pName ); - - if ( nameInString ) - { - g_iNameLengths[i] = (int)strlen( pName ) + (nameInString - pszBuf); - g_pflNameColors[i] = GetClientColor(clientIndex); - } - } - } - - strncpy( g_szLineBuffer[i], pszBuf, min(iBufSize -1, MAX_CHARS_PER_LINE-1) ); - - // make sure the text fits in one line - EnsureTextFitsInOneLineAndWrapIfHaveTo( i ); - - // Set scroll time - if ( i == 0 ) - { - flScrollTime = gHUD.m_flTime + m_HUD_saytext_time->value; - } - - m_iFlags |= HUD_ACTIVE; - //PlaySound( "misc/talk.wav", 1 ); - gHUD.PlayHUDSound("misc/talk.wav", 1); - - Y_START = ScreenHeight()*.7f; - - ChatPanel* theChatPanel = gViewPort->GetChatPanel(); - - if (theChatPanel != NULL) - { - - int theX; - int theY; - int theWidth; - int theHeight; - - gViewPort->GetChatPanel()->getPos(theX, theY); - gViewPort->GetChatPanel()->getSize(theWidth, theHeight); - - //Y_START = theY + theHeight + 5; //voogru: this is too high imo. - //KGP: then move the viewport - Y_START = theY + theHeight + 5; - } - -} - -void CHudSayText :: EnsureTextFitsInOneLineAndWrapIfHaveTo( int line ) -{ - int line_width = 0; - GetConsoleStringSize( g_szLineBuffer[line], &line_width, &line_height ); - - if ( (line_width + LINE_START) > MAX_LINE_WIDTH ) - { // string is too long to fit on line - // scan the string until we find what word is too long, and wrap the end of the sentence after the word - int length = LINE_START; - int tmp_len = 0; - char *last_break = NULL; - for ( char *x = g_szLineBuffer[line]; *x != 0; x++ ) - { - // check for a color change, if so skip past it - if ( x[0] == '/' && x[1] == '(' ) - { - x += 2; - // skip forward until past mode specifier - while ( *x != 0 && *x != ')' ) - x++; - - if ( *x != 0 ) - x++; - - if ( *x == 0 ) - break; - } - - char buf[2]; - buf[1] = 0; - - if ( *x == ' ' && x != g_szLineBuffer[line] ) // store each line break, except for the very first character - last_break = x; - - buf[0] = *x; // get the length of the current character - GetConsoleStringSize( buf, &tmp_len, &line_height ); - length += tmp_len; - - if ( length > MAX_LINE_WIDTH ) - { // needs to be broken up - if ( !last_break ) - last_break = x-1; - - x = last_break; - - // find an empty string slot - int j; - do - { - for ( j = 0; j < MAX_LINES; j++ ) - { - if ( ! *g_szLineBuffer[j] ) - break; - } - if ( j == MAX_LINES ) - { - // need to make more room to display text, scroll stuff up then fix the pointers - int linesmoved = ScrollTextUp(); - line -= linesmoved; - last_break = last_break - (sizeof(g_szLineBuffer[0]) * linesmoved); - } - } - while ( j == MAX_LINES ); - - // copy remaining string into next buffer, making sure it starts with a space character - if ( (char)*last_break == (char)' ' ) - { - int linelen = (int)strlen(g_szLineBuffer[j]); - int remaininglen = (int)strlen(last_break); - - if ( (linelen - remaininglen) <= MAX_CHARS_PER_LINE ) - strcat( g_szLineBuffer[j], last_break ); - } - else - { - if ( (strlen(g_szLineBuffer[j]) - strlen(last_break) - 2) < MAX_CHARS_PER_LINE ) - { - strcat( g_szLineBuffer[j], " " ); - strcat( g_szLineBuffer[j], last_break ); - } - } - - *last_break = 0; // cut off the last string - - EnsureTextFitsInOneLineAndWrapIfHaveTo( j ); - break; - } - } - } +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ +// +// saytext.cpp +// +// implementation of CHudSayText class +// + +#include "hud.h" +#include "cl_util.h" +#include "mod/AvHNetworkMessages.h" + +#include +#include + +#include "vgui_TeamFortressViewport.h" +#include "mod/AvHClientUtil.h" +#include "ui/ChatPanel.h" + +float* GetClientColor(int clientIndex); + +#define MAX_LINES 5 +#define MAX_CHARS_PER_LINE 256 /* it can be less than this, depending on char size */ + +// allow 20 pixels on either side of the text +#define MAX_LINE_WIDTH ( ScreenWidth() - 40 ) +#define LINE_START 10 +static float SCROLL_SPEED = 5; + +static char g_szLineBuffer[ MAX_LINES + 1 ][ MAX_CHARS_PER_LINE ]; +static float *g_pflNameColors[ MAX_LINES + 1 ]; +static int g_iNameLengths[ MAX_LINES + 1 ]; +static float flScrollTime = 0; // the time at which the lines next scroll up + +static int Y_START = 0; +static int line_height = 0; + +DECLARE_MESSAGE( m_SayText, SayText ); + +int CHudSayText :: Init( void ) +{ + gHUD.AddHudElem( this ); + + HOOK_MESSAGE( SayText ); + + InitHUDData(); + + m_HUD_saytext = gEngfuncs.pfnRegisterVariable( "hud_saytext", "1", 0 ); + m_HUD_saytext_time = gEngfuncs.pfnRegisterVariable( "hud_saytext_time", "5", 0 ); + + m_iFlags |= HUD_INTERMISSION; // is always drawn during an intermission + + return 1; +} + + +void CHudSayText :: InitHUDData( void ) +{ + memset( g_szLineBuffer, 0, sizeof g_szLineBuffer ); + memset( g_pflNameColors, 0, sizeof g_pflNameColors ); + memset( g_iNameLengths, 0, sizeof g_iNameLengths ); +} + +int CHudSayText :: VidInit( void ) +{ + return 1; +} + + +int ScrollTextUp( void ) +{ + ConsolePrint( g_szLineBuffer[0] ); // move the first line into the console buffer + g_szLineBuffer[MAX_LINES][0] = 0; + memmove( g_szLineBuffer[0], g_szLineBuffer[1], sizeof(g_szLineBuffer) - sizeof(g_szLineBuffer[0]) ); // overwrite the first line + memmove( &g_pflNameColors[0], &g_pflNameColors[1], sizeof(g_pflNameColors) - sizeof(g_pflNameColors[0]) ); + memmove( &g_iNameLengths[0], &g_iNameLengths[1], sizeof(g_iNameLengths) - sizeof(g_iNameLengths[0]) ); + g_szLineBuffer[MAX_LINES-1][0] = 0; + + if ( g_szLineBuffer[0][0] == ' ' ) // also scroll up following lines + { + g_szLineBuffer[0][0] = 2; + return 1 + ScrollTextUp(); + } + + return 1; +} + +int CHudSayText :: Draw( float flTime ) +{ + int y = Y_START; + + if ( ( gViewPort && gViewPort->AllowedToPrintText() == FALSE) || !m_HUD_saytext->value ) + return 1; + + // make sure the scrolltime is within reasonable bounds, to guard against the clock being reset + flScrollTime = min( flScrollTime, flTime + m_HUD_saytext_time->value ); + + // make sure the scrolltime is within reasonable bounds, to guard against the clock being reset + flScrollTime = min( flScrollTime, flTime + m_HUD_saytext_time->value ); + + if ( flScrollTime <= flTime ) + { + if ( *g_szLineBuffer[0] ) + { + flScrollTime = flTime + m_HUD_saytext_time->value; + // push the console up + ScrollTextUp(); + } + else + { // buffer is empty, just disable drawing of this section + m_iFlags &= ~HUD_ACTIVE; + } + } + + for ( int i = 0; i < MAX_LINES; i++ ) + { + if ( *g_szLineBuffer[i] ) + { + if ( *g_szLineBuffer[i] == 2 && g_pflNameColors[i] ) + { + // it's a saytext string + static char buf[MAX_PLAYER_NAME_LENGTH+32]; + + // draw the first x characters in the player color + strncpy( buf, g_szLineBuffer[i], min(g_iNameLengths[i], MAX_PLAYER_NAME_LENGTH+32) ); + buf[ min(g_iNameLengths[i], MAX_PLAYER_NAME_LENGTH+31) ] = 0; + DrawSetTextColor( g_pflNameColors[i][0], g_pflNameColors[i][1], g_pflNameColors[i][2] ); + + // If we're an alien, move chat over a bit so it doesn't overlap energy bar + int theDrawX = LINE_START; + //if(gHUD.GetIsAlien()) + //{ + // theDrawX += .07f*ScreenWidth; + //} + int x = DrawConsoleString(theDrawX, y, buf ); + + // color is reset after each string draw + DrawConsoleString( x, y, g_szLineBuffer[i] + g_iNameLengths[i] ); + } + else + { + // normal draw + DrawConsoleString( LINE_START, y, g_szLineBuffer[i] ); + } + } + + y += line_height; + } + + + return 1; +} + +int CHudSayText :: MsgFunc_SayText( const char *pszName, int iSize, void *pbuf ) +{ + + int client_index; + string content, location; + NetMsg_SayText( pbuf, iSize, client_index, content, location ); + + string theTranslatedLocation; + if(LocalizeString(location.c_str(), theTranslatedLocation)) + { + // If player is on our team, add location + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(client_index); + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + + if(theEntity && theLocalPlayer && (theEntity->curstate.team == theLocalPlayer->curstate.team)) + { + // Search for first : so we can insert location + int theColonIndex = (int)content.find_first_of(":"); + if((theColonIndex > 0) && (theColonIndex < (int)content.length())) + { + AvHCUTrimExtraneousLocationText(theTranslatedLocation); + + // Insert location + string theNewMessage = content.substr(0, theColonIndex); + theNewMessage += " ("; + + theNewMessage += theTranslatedLocation; + theNewMessage += ")"; + theNewMessage += content.substr(theColonIndex); + + // Replace the message with new one + content = theNewMessage; + } + } + } + + SayTextPrint(content.c_str(), (int)content.length(), client_index ); + + return 1; +} + +void CHudSayText :: SayTextPrint( const char *pszBuf, int iBufSize, int clientIndex ) +{ + if ( gViewPort && gViewPort->AllowedToPrintText() == FALSE ) + { + // Print it straight to the console + ConsolePrint( pszBuf ); + return; + } + + // find an empty string slot + for ( int i = 0; i < MAX_LINES; i++ ) + { + if ( ! *g_szLineBuffer[i] ) + break; + } + if ( i == MAX_LINES ) + { + // force scroll buffer up + ScrollTextUp(); + i = MAX_LINES - 1; + } + + g_iNameLengths[i] = 0; + g_pflNameColors[i] = NULL; + + // if it's a say message, search for the players name in the string + if ( *pszBuf == 2 && clientIndex > 0 ) + { + GetPlayerInfo( clientIndex, &g_PlayerInfoList[clientIndex] ); + const char *pName = g_PlayerInfoList[clientIndex].name; + + if ( pName ) + { + const char *nameInString = strstr( pszBuf, pName ); + + if ( nameInString ) + { + g_iNameLengths[i] = (int)strlen( pName ) + (nameInString - pszBuf); + g_pflNameColors[i] = GetClientColor(clientIndex); + } + } + } + + // : 0001087 + // don't strip last character ( often resulted in no carriage returns in the log ) + strncpy( g_szLineBuffer[i], pszBuf, min(iBufSize, MAX_CHARS_PER_LINE-1) ); + + // make sure the text fits in one line + EnsureTextFitsInOneLineAndWrapIfHaveTo( i ); + + // Set scroll time + if ( i == 0 ) + { + flScrollTime = gHUD.m_flTime + m_HUD_saytext_time->value; + } + + m_iFlags |= HUD_ACTIVE; + //PlaySound( "misc/talk.wav", 1 ); + gHUD.PlayHUDSound("misc/talk.wav", 1); + + Y_START = ScreenHeight()*.7f; + + ChatPanel* theChatPanel = gViewPort->GetChatPanel(); + + if (theChatPanel != NULL) + { + + int theX; + int theY; + int theWidth; + int theHeight; + + gViewPort->GetChatPanel()->getPos(theX, theY); + gViewPort->GetChatPanel()->getSize(theWidth, theHeight); + + //Y_START = theY + theHeight + 5; //: this is too high imo. + //KGP: then move the viewport + Y_START = theY + theHeight + 5; + } + +} + +void CHudSayText :: EnsureTextFitsInOneLineAndWrapIfHaveTo( int line ) +{ + int line_width = 0; + GetConsoleStringSize( g_szLineBuffer[line], &line_width, &line_height ); + + if ( (line_width + LINE_START) > MAX_LINE_WIDTH ) + { // string is too long to fit on line + // scan the string until we find what word is too long, and wrap the end of the sentence after the word + int length = LINE_START; + int tmp_len = 0; + char *last_break = NULL; + for ( char *x = g_szLineBuffer[line]; *x != 0; x++ ) + { + // check for a color change, if so skip past it + if ( x[0] == '/' && x[1] == '(' ) + { + x += 2; + // skip forward until past mode specifier + while ( *x != 0 && *x != ')' ) + x++; + + if ( *x != 0 ) + x++; + + if ( *x == 0 ) + break; + } + + char buf[2]; + buf[1] = 0; + + if ( *x == ' ' && x != g_szLineBuffer[line] ) // store each line break, except for the very first character + last_break = x; + + buf[0] = *x; // get the length of the current character + GetConsoleStringSize( buf, &tmp_len, &line_height ); + length += tmp_len; + + if ( length > MAX_LINE_WIDTH ) + { // needs to be broken up + if ( !last_break ) + last_break = x-1; + + x = last_break; + + // find an empty string slot + int j; + do + { + for ( j = 0; j < MAX_LINES; j++ ) + { + if ( ! *g_szLineBuffer[j] ) + break; + } + if ( j == MAX_LINES ) + { + // need to make more room to display text, scroll stuff up then fix the pointers + int linesmoved = ScrollTextUp(); + line -= linesmoved; + last_break = last_break - (sizeof(g_szLineBuffer[0]) * linesmoved); + } + } + while ( j == MAX_LINES ); + + // copy remaining string into next buffer, making sure it starts with a space character + if ( (char)*last_break == (char)' ' ) + { + int linelen = (int)strlen(g_szLineBuffer[j]); + int remaininglen = (int)strlen(last_break); + + if ( (linelen - remaininglen) <= MAX_CHARS_PER_LINE ) + strcat( g_szLineBuffer[j], last_break ); + } + else + { + if ( (strlen(g_szLineBuffer[j]) - strlen(last_break) - 2) < MAX_CHARS_PER_LINE ) + { + strcat( g_szLineBuffer[j], " " ); + strcat( g_szLineBuffer[j], last_break ); + } + } + + *last_break = 0; // cut off the last string + + EnsureTextFitsInOneLineAndWrapIfHaveTo( j ); + break; + } + } + } } \ No newline at end of file diff --git a/main/source/cl_dll/text_message.cpp b/main/source/cl_dll/text_message.cpp index eb5ceae..5502b0a 100644 --- a/main/source/cl_dll/text_message.cpp +++ b/main/source/cl_dll/text_message.cpp @@ -162,7 +162,7 @@ int CHudTextMessage::MsgFunc_TextMsg( const char *pszName, int iSize, void *pbuf int destination; StringList message; NetMsg_TextMsg( pbuf, iSize, destination, message ); - + if ( gViewPort && gViewPort->AllowedToPrintText() == FALSE ) return 1; @@ -176,6 +176,24 @@ int CHudTextMessage::MsgFunc_TextMsg( const char *pszName, int iSize, void *pbuf psz[0] = 1; origin = psz + 1; } + + // Ensure that message[0] does not contain exessive %'s, max 4x%s, all other %'s removed. + size_t lastpos = 0; size_t pos; int count = 0; + while(true) + { + pos = message[0].find("%", lastpos); + + if (pos == string::npos) + break; + + if ((message[0].substr(pos + 1, 1) == "s") && (count < 4)) + count++; + else + message[0].replace(pos, 1, " "); + + lastpos = pos + 1; + } + sprintf( origin, message[0].c_str(), message[1].c_str(), message[2].c_str(), message[3].c_str(), message[4].c_str() ); ConvertCRtoNL(psz); diff --git a/main/source/cl_dll/vgui_ScorePanel.cpp b/main/source/cl_dll/vgui_ScorePanel.cpp index d474004..ab6eb97 100644 --- a/main/source/cl_dll/vgui_ScorePanel.cpp +++ b/main/source/cl_dll/vgui_ScorePanel.cpp @@ -1,1540 +1,1659 @@ -//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: VGUI scoreboard -// -// $Workfile: $ -// $Date: 2002/10/24 21:13:15 $ -// -//----------------------------------------------------------------------------- -// $Log: vgui_ScorePanel.cpp,v $ -// Revision 1.15 2002/10/24 21:13:15 Flayra -// - Fixed gamma correction for auth icons -// -// Revision 1.14 2002/10/18 22:13:48 Flayra -// - Gamma-correction for auth icons -// -// Revision 1.13 2002/10/16 00:37:33 Flayra -// - Added support for authentication in scoreboard -// -// Revision 1.12 2002/09/09 19:42:55 Flayra -// - Fixed problem where scores were sometimes displayed for marines -// -// Revision 1.11 2002/08/31 18:02:11 Flayra -// - Work at VALVe -// -// Revision 1.10 2002/08/09 00:11:33 Flayra -// - Fixed scoreboard -// -// Revision 1.9 2002/07/08 16:15:13 Flayra -// - Refactored team color management -// -// Revision 1.8 2002/06/25 17:06:48 Flayra -// - Removed memory overwrite from hlcoder list -// -// Revision 1.7 2002/06/03 16:12:21 Flayra -// - Show player status for players on your team (LEV1, GEST, COMM, etc.) -// -// Revision 1.6 2002/04/16 19:30:21 Charlie -// - Fixed crash when holding tab right when joining server, added "REIN" status -// -// Revision 1.5 2002/03/27 21:17:18 Charlie -// - Scoreboard now shows alien scores, and doesn't show marine scores. Also draws DEAD and COMM indicators, and scoring is handled properly for aliens (points for kills, buildings) -// -// Revision 1.4 2002/03/08 02:37:52 Charlie -// - Refactored crappy-ass score panel. It's not perfect, but it's better. Removed TFC code, added DEAD and COMM tags to scoreboard. -// -// Revision 1.3 2001/11/13 17:51:02 Charlie -// - Increased max teams, changed team colors (allow aliens vs. aliens and fronts vs. fronts), general scoreboard support -// -// Revision 1.2 2001/09/13 22:28:01 Charlie -// - Updated NS with new Half-life 1108 patch in preparation for voice support and spectator mode -// -// Revision 1.1.1.1.2.1 2001/09/13 14:42:30 Charlie -// - HL1108 -// -// -// $NoKeywords: $ -//============================================================================= - - -#include - -#include "hud.h" -#include "cl_util.h" -#include "common/const.h" -#include "common/entity_state.h" -#include "common/cl_entity.h" -#include "vgui_TeamFortressViewport.h" -#include "vgui_ScorePanel.h" -#include "..\game_shared\vgui_helpers.h" -#include "..\game_shared\vgui_loadtga.h" -#include "mod/AvHConstants.h" -#include "mod/AvHTitles.h" -#include "vgui_SpectatorPanel.h" -#include "cl_dll/demo.h" -#include "mod/AvHServerVariables.h" -#include "util\STLUtil.h" -#include "ui/ScoreboardIcon.h" - -#include "common/ITrackerUser.h" -extern ITrackerUser *g_pTrackerUser; - -hud_player_info_t g_PlayerInfoList[MAX_PLAYERS+1]; // player info from the engine -extra_player_info_t g_PlayerExtraInfo[MAX_PLAYERS+1]; // additional player info sent directly to the client dll -team_info_t g_TeamInfo[MAX_TEAMS+1]; -int g_IsSpectator[MAX_PLAYERS+1]; - -int HUD_IsGame( const char *game ); -int EV_TFC_IsAllyTeam( int iTeam1, int iTeam2 ); - -// Scoreboard dimensions -#define SBOARD_TITLE_SIZE_Y YRES(22) - -#define X_BORDER XRES(4) - -void LoadData(void* inBuffer, const unsigned char* inData, int inSizeToCopy, int& inSizeVariable); -void SaveData(unsigned char* inBuffer, const void* inData, int inSizeToCopy, int& inSizeVariable); - -int ScorePanel_InitializeDemoPlayback(int inSize, unsigned char* inBuffer) -{ - int theBytesRead = 0; - - LoadData(&g_PlayerInfoList, inBuffer, (MAX_PLAYERS+1)*sizeof(hud_player_info_t), theBytesRead); - LoadData(&g_PlayerExtraInfo, inBuffer, (MAX_PLAYERS+1)*sizeof(extra_player_info_t), theBytesRead); - LoadData(&g_TeamInfo, inBuffer, (MAX_PLAYERS+1)*sizeof(team_info_t), theBytesRead); - LoadData(&g_IsSpectator, inBuffer, (MAX_PLAYERS+1)*sizeof(int), theBytesRead); - - return theBytesRead; -} - -void ScorePanel_InitializeDemoRecording() -{ - // Now save out team info - int theTotalSize = (MAX_PLAYERS + 1)*(sizeof(hud_player_info_t) + sizeof(extra_player_info_t) + sizeof(team_info_t) + sizeof(int)); - unsigned char* theCharArray = new unsigned char[theTotalSize]; - if(theCharArray) - { - int theCounter = 0; - SaveData(theCharArray, &g_PlayerInfoList, (MAX_PLAYERS+1)*sizeof(hud_player_info_t), theCounter); - SaveData(theCharArray, &g_PlayerExtraInfo, (MAX_PLAYERS+1)*sizeof(extra_player_info_t), theCounter); - SaveData(theCharArray, &g_TeamInfo, (MAX_PLAYERS+1)*sizeof(team_info_t), theCounter); - SaveData(theCharArray, &g_IsSpectator, (MAX_PLAYERS+1)*sizeof(int), theCounter); - - Demo_WriteBuffer(TYPE_PLAYERINFO, theTotalSize, theCharArray); - } -} - -// Column sizes -class SBColumnInfo -{ -public: - char *m_pTitle; // If null, ignore, if starts with #, it's localized, otherwise use the string directly. - int m_Width; // Based on 640 width. Scaled to fit other resolutions. - Label::Alignment m_Alignment; -}; - -// grid size is marked out for 640x480 screen - -SBColumnInfo g_ColumnInfo[NUM_COLUMNS] = -{ - {NULL, 24, Label::a_east}, // tracker column - {NULL, 24, Label::a_east}, // status icons - {NULL, 150, Label::a_east}, // name - {NULL, 56, Label::a_east}, // class - {"#SCORE", 40, Label::a_east}, // score - {"#KILLS", 40, Label::a_east}, // kills - {"#DEATHS", 40, Label::a_east}, // deaths - {"#LATENCY", 40, Label::a_east}, // ping - {"#VOICE", 40, Label::a_east}, - {NULL, 2, Label::a_east}, // blank column to take up the slack -}; - - -#define TEAM_NO 0 -#define TEAM_YES 1 -#define TEAM_SPECTATORS 2 -#define TEAM_BLANK 3 - -//----------------------------------------------------------------------------- -// ScorePanel::HitTestPanel. -//----------------------------------------------------------------------------- - -void ScorePanel::HitTestPanel::internalMousePressed(MouseCode code) -{ - for(int i=0;i<_inputSignalDar.getCount();i++) - { - _inputSignalDar[i]->mousePressed(code,this); - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -vgui::Color BuildColor( int R, int G, int B, float gamma ) -{ - ASSERT( gamma != 0 ); - return vgui::Color( R/gamma, G/gamma, B/gamma, 0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: Create the ScoreBoard panel -//----------------------------------------------------------------------------- -ScorePanel::ScorePanel(int x,int y,int wide,int tall) : Panel(x,y,wide,tall) -{ - CSchemeManager *pSchemes = gViewPort->GetSchemeManager(); - SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle("Scoreboard Title Text"); - SchemeHandle_t hSmallScheme = pSchemes->getSchemeHandle("Scoreboard Small Text"); - SchemeHandle_t hTinyScheme = pSchemes->getSchemeHandle("Scoreboard Tiny Text"); - Font *tfont = pSchemes->getFont(hTitleScheme); - Font *smallfont = pSchemes->getFont(hSmallScheme); - Font *tinyfont = pSchemes->getFont(hTinyScheme); - - setBgColor(0, 0, 0, 96); - m_pCurrentHighlightLabel = NULL; - m_iHighlightRow = -1; - // puzl: 0001073 - m_pTrackerIcon = NULL; - m_pDevIcon = NULL; - m_pPTIcon = NULL; - m_pGuideIcon = NULL; - m_pServerOpIcon = NULL; - m_pContribIcon = NULL; - m_pCheatingDeathIcon = NULL; - m_pVeteranIcon = NULL; - - m_pTrackerIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardtracker.tga"); - m_pDevIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboarddev.tga"); - m_pPTIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardpt.tga"); - m_pGuideIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardguide.tga"); - m_pServerOpIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardserverop.tga"); - m_pContribIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardcontrib.tga"); - m_pCheatingDeathIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardcd.tga"); - m_pVeteranIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardveteran.tga"); - - m_iIconFrame = 0; - m_iLastFrameIncrementTime = gHUD.GetTimeOfLastUpdate(); - - // Initialize the top title. - m_TitleLabel.setFont(tfont); - m_TitleLabel.setText(""); - m_TitleLabel.setBgColor( 0, 0, 0, 255 ); - m_TitleLabel.setFgColor( Scheme::sc_primary1 ); - m_TitleLabel.setContentAlignment( vgui::Label::a_west ); - - LineBorder *border = new LineBorder(Color(60, 60, 60, 128)); - setBorder(border); - setPaintBorderEnabled(true); - - int xpos = g_ColumnInfo[0].m_Width + 3; - if (ScreenWidth() >= 640) - { - // only expand column size for res greater than 640 - xpos = XRES(xpos); - } - m_TitleLabel.setBounds(xpos, 4, wide, SBOARD_TITLE_SIZE_Y); - m_TitleLabel.setContentFitted(false); - m_TitleLabel.setParent(this); - - // Setup the header (labels like "name", "class", etc..). - m_HeaderGrid.SetDimensions(NUM_COLUMNS, 1); - m_HeaderGrid.SetSpacing(0, 0); - - for(int i=0; i < NUM_COLUMNS; i++) - { - if (g_ColumnInfo[i].m_pTitle && g_ColumnInfo[i].m_pTitle[0] == '#') - m_HeaderLabels[i].setText(CHudTextMessage::BufferedLocaliseTextString(g_ColumnInfo[i].m_pTitle)); - else if(g_ColumnInfo[i].m_pTitle) - m_HeaderLabels[i].setText(g_ColumnInfo[i].m_pTitle); - - int xwide = g_ColumnInfo[i].m_Width; - if (ScreenWidth() >= 640) - { - xwide = XRES(xwide); - } - else if (ScreenWidth() == 400) - { - // hack to make 400x300 resolution scoreboard fit - if (i == 1) - { - // reduces size of player name cell - xwide -= 28; - } - else if (i == 0) - { - // tracker icon cell - xwide -= 8; - } - } - - m_HeaderGrid.SetColumnWidth(i, xwide); - m_HeaderGrid.SetEntry(i, 0, &m_HeaderLabels[i]); - - m_HeaderLabels[i].setBgColor(0,0,0,255); - m_HeaderLabels[i].setBgColor(0,0,0,255); - - int theColorIndex = 0; - Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope()); - int theR, theG, theB, theA; - gammaAdjustedTeamColor.getColor(theR, theG, theB, theA); - m_HeaderLabels[i].setFgColor(theR, theG, theB, theA); - - m_HeaderLabels[i].setFont(smallfont); - m_HeaderLabels[i].setContentAlignment(g_ColumnInfo[i].m_Alignment); - - int yres = 12; - if (ScreenHeight() >= 480) - { - yres = YRES(yres); - } - m_HeaderLabels[i].setSize(50, yres); - } - - // Set the width of the last column to be the remaining space. - int ex, ey, ew, eh; - m_HeaderGrid.GetEntryBox(NUM_COLUMNS - 2, 0, ex, ey, ew, eh); - m_HeaderGrid.SetColumnWidth(NUM_COLUMNS - 1, (wide - X_BORDER) - (ex + ew)); - - m_HeaderGrid.AutoSetRowHeights(); - m_HeaderGrid.setBounds(X_BORDER, SBOARD_TITLE_SIZE_Y, wide - X_BORDER*2, m_HeaderGrid.GetRowHeight(0)); - m_HeaderGrid.setParent(this); - m_HeaderGrid.setBgColor(0,0,0,255); - - - // Now setup the listbox with the actual player data in it. - int headerX, headerY, headerWidth, headerHeight; - m_HeaderGrid.getBounds(headerX, headerY, headerWidth, headerHeight); - m_PlayerList.setBounds(headerX, headerY+headerHeight, headerWidth, tall - headerY - headerHeight - 6); - m_PlayerList.setBgColor(0,0,0,255); - m_PlayerList.setParent(this); - - for(int row=0; row < NUM_ROWS; row++) - { - CGrid *pGridRow = &m_PlayerGrids[row]; - - pGridRow->SetDimensions(NUM_COLUMNS, 1); - - for(int col=0; col < NUM_COLUMNS; col++) - { - m_PlayerEntries[col][row].setContentFitted(false); - m_PlayerEntries[col][row].setRow(row); - m_PlayerEntries[col][row].addInputSignal(this); - pGridRow->SetEntry(col, 0, &m_PlayerEntries[col][row]); - } - - pGridRow->setBgColor(0,0,0,255); -// pGridRow->SetSpacing(2, 0);f - pGridRow->SetSpacing(0, 0); - pGridRow->CopyColumnWidths(&m_HeaderGrid); - pGridRow->AutoSetRowHeights(); - pGridRow->setSize(PanelWidth(pGridRow), pGridRow->CalcDrawHeight()); - pGridRow->RepositionContents(); - - m_PlayerList.AddItem(pGridRow); - } - - - // Add the hit test panel. It is invisible and traps mouse clicks so we can go into squelch mode. - m_HitTestPanel.setBgColor(0,0,0,255); - m_HitTestPanel.setParent(this); - m_HitTestPanel.setBounds(0, 0, wide, tall); - m_HitTestPanel.addInputSignal(this); - - m_pCloseButton = new CommandButton( "x", wide-XRES(12 + 4), YRES(2), XRES( 12 ) , YRES( 12 ) ); - m_pCloseButton->setParent( this ); - m_pCloseButton->addActionSignal( new CMenuHandler_StringCommandWatch( "-showscores", true ) ); - m_pCloseButton->setBgColor(0,0,0,255); - m_pCloseButton->setFgColor( 255, 255, 255, 0 ); - m_pCloseButton->setFont(tfont); - m_pCloseButton->setBoundKey( (char)255 ); - m_pCloseButton->setContentAlignment(Label::a_center); - Initialize(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Called each time a new level is started. -//----------------------------------------------------------------------------- -void ScorePanel::Initialize( void ) -{ - // Clear out scoreboard data - m_iLastKilledBy = 0; - m_fLastKillTime = 0; - m_iPlayerNum = 0; - m_iNumTeams = 0; - // puzl: 0001073 -// for( int counter = 0; counter < MAX_PLAYERS+1; counter++ ) -// { -// delete g_PlayerExtraInfo[counter].icon; -// } - - memset( g_PlayerExtraInfo, 0, sizeof g_PlayerExtraInfo ); - memset( g_TeamInfo, 0, sizeof g_TeamInfo ); -} - -bool HACK_GetPlayerUniqueID( int iPlayer, char playerID[16] ) -{ - return !!gEngfuncs.GetPlayerUniqueID( iPlayer, playerID ); -} -//----------------------------------------------------------------------------- -// Purpose: Recalculate the internal scoreboard data -//----------------------------------------------------------------------------- -void ScorePanel::Update() -{ - // Set the title - string theTitleName; - - if (gViewPort->m_szServerName) - { - int iServerNameLength = max((int)strlen(gViewPort->m_szServerName),MAX_SERVERNAME_LENGTH); - theTitleName += string(gViewPort->m_szServerName,iServerNameLength); - } - - string theMapName = gHUD.GetMapName(); - if(theMapName != "") - { - if(theTitleName != "") - { - theTitleName += " "; - } - - theTitleName += "("; - theTitleName += theMapName; - theTitleName += ")"; - } - - m_TitleLabel.setText(theTitleName.c_str()); - - int theColorIndex = 0; - - // Set gamma-correct title color - Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope()); - - int theR, theG, theB, theA; - gammaAdjustedTeamColor.getColor(theR, theG, theB, theA); - - m_TitleLabel.setFgColor(theR, theG, theB, theA); - - m_iRows = 0; - gViewPort->GetAllPlayersInfo(); - - // Clear out sorts - int i = 0; - for (i = 0; i < NUM_ROWS; i++) - { - m_iSortedRows[i] = 0; - m_iIsATeam[i] = TEAM_IND; - } - - // Fix for memory overrun bug - for (i = 0; i < MAX_PLAYERS; i++) - { - m_bHasBeenSorted[i] = false; - } - - SortTeams(); - - // set scrollbar range - m_PlayerList.SetScrollRange(m_iRows); - - FillGrid(); - if ( gViewPort->m_pSpectatorPanel->m_menuVisible ) - { - m_pCloseButton->setVisible ( true ); - } - else - { - m_pCloseButton->setVisible ( false ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sort all the teams -//----------------------------------------------------------------------------- -void ScorePanel::SortTeams() -{ - // clear out team scores - float theCurrentTime = gHUD.GetTimeOfLastUpdate(); - - for ( int i = 1; i <= m_iNumTeams; i++ ) - { - if ( !g_TeamInfo[i].scores_overriden ) - g_TeamInfo[i].score = g_TeamInfo[i].frags = g_TeamInfo[i].deaths = 0; - g_TeamInfo[i].ping = g_TeamInfo[i].packetloss = 0; - } - - // recalc the team scores, then draw them - for ( i = 1; i <= MAX_PLAYERS; i++ ) - { - if ( g_PlayerInfoList[i].name == NULL ) - continue; // empty player slot, skip - - if ( g_PlayerExtraInfo[i].teamname[0] == 0 ) - continue; // skip over players who are not in a team - - // find what team this player is in - for ( int j = 1; j <= m_iNumTeams; j++ ) - { - if ( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) ) - break; - } - if ( j > m_iNumTeams ) // player is not in a team, skip to the next guy - continue; - - if ( !g_TeamInfo[j].scores_overriden ) - { - g_TeamInfo[j].score += g_PlayerExtraInfo[i].score; - g_TeamInfo[j].frags += g_PlayerExtraInfo[i].frags; - g_TeamInfo[j].deaths += g_PlayerExtraInfo[i].deaths; - } - - g_TeamInfo[j].ping += g_PlayerInfoList[i].ping; - g_TeamInfo[j].packetloss += g_PlayerInfoList[i].packetloss; - - if ( g_PlayerInfoList[i].thisplayer ) - g_TeamInfo[j].ownteam = TRUE; - else - g_TeamInfo[j].ownteam = FALSE; - - // Set the team's number (used for team colors) - g_TeamInfo[j].teamnumber = g_PlayerExtraInfo[i].teamnumber; - } - - // find team ping/packetloss averages - for ( i = 1; i <= m_iNumTeams; i++ ) - { - g_TeamInfo[i].already_drawn = FALSE; - - if ( g_TeamInfo[i].players > 0 ) - { - g_TeamInfo[i].ping /= g_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping - g_TeamInfo[i].packetloss /= g_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping - } - } - - SortActivePlayers(kMarine1Team); - SortActivePlayers(kAlien1Team); - SortActivePlayers(kMarine2Team); - SortActivePlayers(kAlien2Team); - SortActivePlayers(kSpectatorTeam); - SortActivePlayers(kUndefinedTeam); -} - -void ScorePanel::SortActivePlayers(char* inTeam, bool inSortByEntityIndex) -{ - for(int i = 1; i <= m_iNumTeams; i++) - { - if(!strcmp(g_TeamInfo[i].name, inTeam)) - { - int best_team = i; - - // Put this team in the sorted list - m_iSortedRows[ m_iRows ] = best_team; - m_iIsATeam[ m_iRows ] = TEAM_YES; - g_TeamInfo[best_team].already_drawn = TRUE; // set the already_drawn to be TRUE, so this team won't get sorted again - m_iRows++; - - // Now sort all the players on this team - SortPlayers(0, g_TeamInfo[best_team].name, inSortByEntityIndex); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sort a list of players -//----------------------------------------------------------------------------- -void ScorePanel::SortPlayers( int iTeam, char *team, bool inSortByEntityIndex) -{ - bool bCreatedTeam = false; - - // draw the players, in order, and restricted to team if set - while ( 1 ) - { - // Find the top ranking player - int theBestTotalScore = -99999; - int theBestDeaths = 0; - int theBestPlayer = 0; - - for ( int i = 1; i <= MAX_PLAYERS; i++ ) - { - if ( m_bHasBeenSorted[i] == false && g_PlayerInfoList[i].name ) - { - cl_entity_t *ent = gEngfuncs.GetEntityByIndex( i ); - - if ( ent && !(team && stricmp(g_PlayerExtraInfo[i].teamname, team)) ) - { - extra_player_info_t *pl_info = &g_PlayerExtraInfo[i]; - - // Sort by player index to mask marine status - if(inSortByEntityIndex) - { - if((theBestPlayer == 0) || (i < theBestPlayer)) - { - theBestPlayer = i; - } - } - else - { - // overall rank = score + kills (with least deaths breaking ties) - int thePlayerScore = pl_info->score; - int thePlayerDeaths = pl_info->deaths; - if((thePlayerScore > theBestTotalScore) || ((thePlayerScore == theBestTotalScore) && (pl_info->deaths < theBestDeaths))) - { - theBestPlayer = i; - theBestTotalScore = thePlayerScore; - theBestDeaths = thePlayerDeaths; - } - } - } - } - } - - if ( !theBestPlayer ) - break; - - // If we haven't created the Team yet, do it first - if (!bCreatedTeam && iTeam) - { - m_iIsATeam[ m_iRows ] = iTeam; - m_iRows++; - - bCreatedTeam = true; - } - - // Put this player in the sorted list - m_iSortedRows[ m_iRows ] = theBestPlayer; - m_bHasBeenSorted[ theBestPlayer ] = true; - m_iRows++; - } - - if (team) - { - m_iIsATeam[m_iRows++] = TEAM_BLANK; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Recalculate the existing teams in the match -//----------------------------------------------------------------------------- -void ScorePanel::RebuildTeams() -{ - // clear out player counts from teams - for ( int i = 1; i <= m_iNumTeams; i++ ) - { - g_TeamInfo[i].players = 0; - } - - // rebuild the team list - gViewPort->GetAllPlayersInfo(); - m_iNumTeams = 0; - for ( i = 1; i <= MAX_PLAYERS; i++ ) - { - if ( g_PlayerInfoList[i].name == NULL ) - continue; - - if ( g_PlayerExtraInfo[i].teamname[0] == 0 ) - continue; // skip over players who are not in a team - - // is this player in an existing team? - for ( int j = 1; j <= m_iNumTeams; j++ ) - { - if ( g_TeamInfo[j].name[0] == '\0' ) - break; - - if ( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) ) - break; - } - - if ( j > m_iNumTeams ) - { // they aren't in a listed team, so make a new one - // search through for an empty team slot - for ( int j = 1; j <= m_iNumTeams; j++ ) - { - if ( g_TeamInfo[j].name[0] == '\0' ) - break; - } - m_iNumTeams = max( j, m_iNumTeams ); - - strncpy( g_TeamInfo[j].name, g_PlayerExtraInfo[i].teamname, MAX_TEAM_NAME ); - g_TeamInfo[j].players = 0; - } - - g_TeamInfo[j].players++; - } - - // clear out any empty teams - for ( i = 1; i <= m_iNumTeams; i++ ) - { - if ( g_TeamInfo[i].players < 1 ) - memset( &g_TeamInfo[i], 0, sizeof(team_info_t) ); - } - - // Update the scoreboard - Update(); -} - -//----------------------------------------------------------------------------- - -int ScorePanel::GetIconFrame(void) -{ - const static int kIconFrameDuration = 0.25; - - int current_time = gHUD.GetTimeOfLastUpdate(); - if( (m_iLastFrameIncrementTime - current_time) > kIconFrameDuration ) - { - m_iLastFrameIncrementTime = current_time; - m_iIconFrame++; - if( m_iIconFrame >= 1000 ) - { - m_iIconFrame = 0; - } - } - return m_iIconFrame; -} - -//----------------------------------------------------------------------------- - -void ScorePanel::FillGrid() -{ - CSchemeManager *pSchemes = gViewPort->GetSchemeManager(); - SchemeHandle_t hScheme = pSchemes->getSchemeHandle("Scoreboard Text"); - SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle("Scoreboard Title Text"); - SchemeHandle_t hSmallScheme = pSchemes->getSchemeHandle("Scoreboard Small Text"); - SchemeHandle_t hTinyScheme = pSchemes->getSchemeHandle("Scoreboard Tiny Text"); - - Font *sfont = pSchemes->getFont(hScheme); - Font *tfont = pSchemes->getFont(hTitleScheme); - Font *smallfont = pSchemes->getFont(hSmallScheme); - Font *tinyfont = pSchemes->getFont(hTinyScheme); - - // update highlight position - int x, y; - getApp()->getCursorPos(x, y); - cursorMoved(x, y, this); - - // remove highlight row if we're not in squelch mode - if (!GetClientVoiceMgr()->IsInSquelchMode()) - { - m_iHighlightRow = -1; - } - - bool bNextRowIsGap = false; - - for(int row=0; row < NUM_ROWS; row++) - { - CGrid *pGridRow = &m_PlayerGrids[row]; - pGridRow->SetRowUnderline(0, false, 0, 0, 0, 0, 0); - - if(row >= m_iRows) - { - for(int col=0; col < NUM_COLUMNS; col++) - m_PlayerEntries[col][row].setVisible(false); - - continue; - } - - bool bRowIsGap = false; - if (bNextRowIsGap) - { - bNextRowIsGap = false; - bRowIsGap = true; - } - - // Get the player's data - int theSortedRow = m_iSortedRows[row]; - hud_player_info_t* pl_info = &g_PlayerInfoList[theSortedRow]; - extra_player_info_t* theExtraPlayerInfo = &g_PlayerExtraInfo[theSortedRow]; - int thePlayerClass = theExtraPlayerInfo->playerclass; - short theTeamNumber = theExtraPlayerInfo->teamnumber; - string theCustomIcon = (string)theExtraPlayerInfo->customicon; -// puzl: 0001073 - short thePlayerAuthentication = theExtraPlayerInfo->auth; - bool thePlayerIsDead = false; - switch( thePlayerClass ) - { - case PLAYERCLASS_DEAD_MARINE: - case PLAYERCLASS_DEAD_ALIEN: - case PLAYERCLASS_REINFORCING: - thePlayerIsDead = true; - break; - } - - // Code to test DEBUG -#if 0 - #ifdef DEBUG - extern int gGlobalDebugAuth; - thePlayerAuthentication = 1; - thePlayerAuthentication <<= gGlobalDebugAuth; - #endif -#endif - - team_info_t* team_info = &g_TeamInfo[m_iSortedRows[row]]; - int theColorIndex = theTeamNumber % iNumberOfTeamColors; - - int theLocalPlayerTeam = 0; - if(gEngfuncs.GetLocalPlayer()) - { - theLocalPlayerTeam = gEngfuncs.GetLocalPlayer()->curstate.team; - } - - for(int col=0; col < NUM_COLUMNS; col++) - { - CLabelHeader *pLabel = &m_PlayerEntries[col][row]; - - pLabel->setVisible(true); - pLabel->setText2(""); - pLabel->setImage(NULL); - pLabel->setFont(sfont); - pLabel->setTextOffset(0, 0); - - int rowheight = 13; - if (ScreenHeight() > 480) - { - rowheight = YRES(rowheight); - } - else - { - // more tweaking, make sure icons fit at low res - rowheight = 15; - } - pLabel->setSize(pLabel->getWide(), rowheight); - pLabel->setBgColor(0, 0, 0, 255); - - char sz[128]; - - Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope()); - pLabel->setFgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 0); - - if (m_iIsATeam[row] == TEAM_BLANK) - { - pLabel->setText(" "); - continue; - } - else if ( m_iIsATeam[row] == TEAM_YES ) - { - theColorIndex = team_info->teamnumber % iNumberOfTeamColors; - - // team color text for team names - - - - // different height for team header rows - rowheight = 20; - if (ScreenHeight() >= 480) - { - rowheight = YRES(rowheight); - } - pLabel->setSize(pLabel->getWide(), rowheight); - pLabel->setFont(tfont); - - pGridRow->SetRowUnderline( 0, - true, - YRES(3), - gammaAdjustedTeamColor[0], - gammaAdjustedTeamColor[1], - gammaAdjustedTeamColor[2], - 0 ); - } - else if ( m_iIsATeam[row] == TEAM_SPECTATORS ) - { - // grey text for spectators - pLabel->setFgColor(100, 100, 100, 0); - - // different height for team header rows - rowheight = 20; - if (ScreenHeight() >= 480) - { - rowheight = YRES(rowheight); - } - pLabel->setSize(pLabel->getWide(), rowheight); - pLabel->setFont(tfont); - - pGridRow->SetRowUnderline(0, true, YRES(3), 100, 100, 100, 0); - } - else - { - if(thePlayerIsDead) - { - pLabel->setFgColor(255, 0, 0, 0); - } - else - { - // team color text for player names - pLabel->setFgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 0); - } - - // Set background color - if ( pl_info && pl_info->thisplayer ) // if it is their name, draw it a different color - { - // Highlight this player - pLabel->setFgColor(Scheme::sc_white); - pLabel->setBgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 196 ); - } - else if ( theSortedRow == m_iLastKilledBy && m_fLastKillTime && m_fLastKillTime > gHUD.m_flTime ) - { - // Killer's name - pLabel->setBgColor( 255,0,0, 255 - ((float)15 * (float)(m_fLastKillTime - gHUD.m_flTime)) ); - } - } - - // Align - switch(col) - { - case COLUMN_NAME: - case COLUMN_CLASS: - pLabel->setContentAlignment( vgui::Label::a_west ); - break; - - case COLUMN_TRACKER: - case COLUMN_RANK_ICON: - case COLUMN_VOICE: - pLabel->setContentAlignment( vgui::Label::a_center ); - break; - - case COLUMN_SCORE: - case COLUMN_KILLS: - case COLUMN_DEATHS: - case COLUMN_LATENCY: - default: - pLabel->setContentAlignment( vgui::Label::a_east ); - break; - } - - // Fill out with the correct data - strcpy(sz, ""); - if ( m_iIsATeam[row] ) - { - char sz2[128]; - - switch (col) - { - case COLUMN_NAME: - if ( m_iIsATeam[row] == TEAM_SPECTATORS ) - { - sprintf( sz2, CHudTextMessage::BufferedLocaliseTextString( "#Spectators" ) ); - } - else - { - if(team_info) - { - sprintf( sz2, gViewPort->GetTeamName(team_info->teamnumber) ); - } - } - - strcpy(sz, sz2); - - // Append the number of players - if ( m_iIsATeam[row] == TEAM_YES && team_info) - { - if (team_info->players == 1) - { - sprintf(sz2, "(%d %s)", team_info->players, CHudTextMessage::BufferedLocaliseTextString( "#Player" ) ); - } - else - { - sprintf(sz2, "(%d %s)", team_info->players, CHudTextMessage::BufferedLocaliseTextString( "#Player_plural" ) ); - } - - pLabel->setText2(sz2); - pLabel->setFont2(smallfont); - } - break; - case COLUMN_VOICE: - break; - case COLUMN_CLASS: - break; - case COLUMN_SCORE: - // Don't show score for enemies unless spectating or in RR - if ((m_iIsATeam[row] == TEAM_YES) && team_info && ((theLocalPlayerTeam == 0) || (theLocalPlayerTeam == team_info->teamnumber))) - sprintf(sz, "%d", team_info->score); - break; - case COLUMN_KILLS: - if ((m_iIsATeam[row] == TEAM_YES) && team_info) - sprintf(sz, "%d", team_info->frags ); - break; - case COLUMN_DEATHS: - if ((m_iIsATeam[row] == TEAM_YES) && team_info) - sprintf(sz, "%d", team_info->deaths ); - break; - case COLUMN_LATENCY: - if ((m_iIsATeam[row] == TEAM_YES) && team_info) - sprintf(sz, "%d", team_info->ping ); - break; - default: - break; - } - } - else - { - // Are these stats for an enemy? Score and other stats shouldn't be drawn for enemies. - bool theIsForEnemy = false; - - int theLocalPlayerTeam = 0; - if(gEngfuncs.GetLocalPlayer()) - { - theLocalPlayerTeam = gEngfuncs.GetLocalPlayer()->curstate.team; - } - - if((theLocalPlayerTeam != 0) && (theExtraPlayerInfo->teamnumber != theLocalPlayerTeam)) - { - theIsForEnemy = true; - } - - switch (col) - { - case COLUMN_NAME: - - if (g_pTrackerUser) - { - int playerSlot = m_iSortedRows[row]; - int trackerID = gEngfuncs.GetTrackerIDForPlayer(playerSlot); - const char *trackerName = g_pTrackerUser->GetUserName(trackerID); - if (trackerName && *trackerName) - { - sprintf(sz, " (%s)", trackerName); - pLabel->setText2(sz); - } - } - - if(pl_info) - { - sprintf(sz, "%s ", pl_info->name); - } - break; - case COLUMN_VOICE: - sz[0] = 0; - // in HLTV mode allow spectator to turn on/off commentator voice - if (pl_info && (!pl_info->thisplayer || gEngfuncs.IsSpectateOnly())) - { - GetClientVoiceMgr()->UpdateSpeakerImage(pLabel, theSortedRow); - } - break; - case COLUMN_CLASS: - // No class for other team's members (unless allied or spectator, and make sure player is on our team) - strcpy(sz, ""); - - if(team_info && ((theLocalPlayerTeam == theTeamNumber) || (gHUD.GetPlayMode() == PLAYMODE_OBSERVER))) - { - switch(thePlayerClass) - { - case (int)(PLAYERCLASS_DEAD_MARINE): - case (int)(PLAYERCLASS_DEAD_ALIEN): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassDead)); - break; - case (int)(PLAYERCLASS_REINFORCING): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassReinforcing)); - break; - case (int)(PLAYERCLASS_REINFORCINGCOMPLETE): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassReinforcingComplete)); - break; - case (int)(PLAYERCLASS_ALIVE_JETPACK_MARINE): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassJetpackMarine)); - break; - case (int)(PLAYERCLASS_ALIVE_HEAVY_MARINE): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassHeavyMarine)); - break; - case (int)(PLAYERCLASS_COMMANDER): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassCommander)); - break; - case (int)(PLAYERCLASS_ALIVE_LEVEL1): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel1)); - break; - case (int)(PLAYERCLASS_ALIVE_LEVEL2): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel2)); - break; - case (int)(PLAYERCLASS_ALIVE_LEVEL3): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel3)); - break; - case (int)(PLAYERCLASS_ALIVE_LEVEL4): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel4)); - break; - case (int)(PLAYERCLASS_ALIVE_LEVEL5): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel5)); - break; - case (int)(PLAYERCLASS_ALIVE_DIGESTING): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassDigesting)); - break; - case (int)(PLAYERCLASS_ALIVE_GESTATING): - sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassGestating)); - break; - default: - break; - } - } - break; - - case COLUMN_RANK_ICON: -// puzl: 0001073 -#ifdef USE_OLDAUTH - // Check if we have authority. Right now these override the tracker icons. Listed in increasing order of "importance". - if(thePlayerAuthentication & PLAYERAUTH_CHEATINGDEATH) - { - // Red - pLabel->setImage(m_pCheatingDeathIcon); - pLabel->setFgColorAsImageColor(false); - m_pCheatingDeathIcon->setColor(BuildColor(255, 69, 9, gHUD.GetGammaSlope())); - } - if(thePlayerAuthentication & PLAYERAUTH_VETERAN) - { - // Yellow - pLabel->setImage(m_pVeteranIcon); - pLabel->setFgColorAsImageColor(false); - m_pVeteranIcon->setColor(BuildColor(248, 252, 0, gHUD.GetGammaSlope())); - } - if(thePlayerAuthentication & PLAYERAUTH_BETASERVEROP) - { - // Whitish - pLabel->setImage(m_pServerOpIcon); - pLabel->setFgColorAsImageColor(false); - m_pServerOpIcon->setColor(BuildColor(220, 220, 220, gHUD.GetGammaSlope())); - } - if(thePlayerAuthentication & PLAYERAUTH_CONTRIBUTOR) - { - // Light blue - pLabel->setImage(m_pContribIcon); - pLabel->setFgColorAsImageColor(false); - m_pContribIcon->setColor(BuildColor(117, 214, 241, gHUD.GetGammaSlope())); - } - if(thePlayerAuthentication & PLAYERAUTH_GUIDE) - { - // Magenta - pLabel->setImage(m_pGuideIcon); - pLabel->setFgColorAsImageColor(false); - m_pGuideIcon->setColor(BuildColor(208, 16, 190, gHUD.GetGammaSlope())); - } - if(thePlayerAuthentication & PLAYERAUTH_PLAYTESTER) - { - // Orange - pLabel->setImage(m_pPTIcon); - pLabel->setFgColorAsImageColor(false); - m_pPTIcon->setColor(BuildColor(255, 167, 54, gHUD.GetGammaSlope())); - } - if(thePlayerAuthentication & PLAYERAUTH_DEVELOPER) - { - // TSA blue - pLabel->setImage(m_pDevIcon); - pLabel->setFgColorAsImageColor(false); - m_pDevIcon->setColor(BuildColor(100, 215, 255, gHUD.GetGammaSlope())); - } - - if(thePlayerAuthentication & PLAYERAUTH_SERVEROP) - { - // Bright green - pLabel->setImage(m_pServerOpIcon); - pLabel->setFgColorAsImageColor(false); - m_pServerOpIcon->setColor(BuildColor(0, 255, 0, gHUD.GetGammaSlope())); - } - - // Allow custom icons to override other general icons - if(thePlayerAuthentication & PLAYERAUTH_CUSTOM) - { - if(theCustomIcon != "") - { - string theIconName = theCustomIcon.substr(0, strlen(theCustomIcon.c_str()) - 3); - string theFullCustomIconString = string("gfx/vgui/640_") + theIconName + string(".tga"); - - vgui::BitmapTGA *pIcon = GetIconPointer(theCustomIcon); - - //Icon hasnt been loaded, load it now and add it to list of icons. - if(pIcon == NULL) - { - pIcon = vgui_LoadTGANoInvertAlpha(theFullCustomIconString.c_str()); - - if(pIcon) - m_CustomIconList.push_back( make_pair(pIcon, theCustomIcon) ); - } - - if(pIcon) - { - pLabel->setImage(pIcon); - pLabel->setFgColorAsImageColor(false); - - // Parse color (last 3 bytes are the RGB values 1-9) - string theColor = theCustomIcon.substr( strlen(theCustomIcon.c_str())-3, 3); - int theRed = (MakeIntFromString(theColor.substr(0, 1))/9.0f)*255; - int theGreen = (MakeIntFromString(theColor.substr(1, 1))/9.0f)*255; - int theBlue = (MakeIntFromString(theColor.substr(2, 1))/9.0f)*255; - - pIcon->setColor(BuildColor(theRed, theGreen, theBlue, gHUD.GetGammaSlope())); - } - } - } - - if(g_pTrackerUser) - { - int playerSlot = theSortedRow; - int trackerID = gEngfuncs.GetTrackerIDForPlayer(playerSlot); - - if (g_pTrackerUser->IsFriend(trackerID) && trackerID != g_pTrackerUser->GetTrackerID()) - { - pLabel->setImage(m_pTrackerIcon); - pLabel->setFgColorAsImageColor(false); - m_pTrackerIcon->setColor(Color(255, 255, 255, 0)); - } - } -#else - if( theExtraPlayerInfo->icon ) - { - vgui::Bitmap* image = theExtraPlayerInfo->icon->getImage( this->GetIconFrame() ); - if( image ) { pLabel->setImage( image ); } - } -#endif - break; - case COLUMN_SCORE: - if(!theIsForEnemy) - { - const float kDeltaDisplayTime = 3.0f; - float theTimeSinceChange = gHUD.GetTimeOfLastUpdate() - theExtraPlayerInfo->timeOfLastScoreChange; - if((theExtraPlayerInfo->score > theExtraPlayerInfo->lastScore) && (theTimeSinceChange > 0) && (theTimeSinceChange < kDeltaDisplayTime) && (theExtraPlayerInfo->teamnumber != 0)) - { - // draw score with change - int theDelta = (theExtraPlayerInfo->score - theExtraPlayerInfo->lastScore); - sprintf(sz, "(+%d) %d", theDelta, theExtraPlayerInfo->score); - } - else - { - sprintf(sz, "%d", theExtraPlayerInfo->score); - } - - } - break; - - case COLUMN_KILLS: - sprintf(sz, "%d", theExtraPlayerInfo->frags); - break; - - case COLUMN_DEATHS: - sprintf(sz, "%d", theExtraPlayerInfo->deaths); - break; - case COLUMN_LATENCY: - if(pl_info) - { - sprintf(sz, "%d", pl_info->ping ); - } - break; - default: - break; - } - } - - pLabel->setText(sz); - } - } - - for(row=0; row < NUM_ROWS; row++) - { - CGrid *pGridRow = &m_PlayerGrids[row]; - - pGridRow->AutoSetRowHeights(); - pGridRow->setSize(PanelWidth(pGridRow), pGridRow->CalcDrawHeight()); - pGridRow->RepositionContents(); - } - - // hack, for the thing to resize - m_PlayerList.getSize(x, y); - m_PlayerList.setSize(x, y); -} - - -//----------------------------------------------------------------------------- -// Purpose: Setup highlights for player names in scoreboard -//----------------------------------------------------------------------------- -void ScorePanel::DeathMsg( int killer, int victim ) -{ - // if we were the one killed, or the world killed us, set the scoreboard to indicate suicide - if ( victim == m_iPlayerNum || killer == 0 ) - { - m_iLastKilledBy = killer ? killer : m_iPlayerNum; - m_fLastKillTime = gHUD.m_flTime + 10; // display who we were killed by for 10 seconds - - if ( killer == m_iPlayerNum ) - m_iLastKilledBy = m_iPlayerNum; - } -} - - -void ScorePanel::Open( void ) -{ - RebuildTeams(); - setVisible(true); - m_HitTestPanel.setVisible(true); -} - -bool ScorePanel::SetSquelchMode(bool inMode) -{ - bool theSuccess = false; - - if(inMode && !GetClientVoiceMgr()->IsInSquelchMode()) - { - GetClientVoiceMgr()->StartSquelchMode(); - m_HitTestPanel.setVisible(false); - theSuccess = true; - } - else if(!inMode && GetClientVoiceMgr()->IsInSquelchMode()) - { - GetClientVoiceMgr()->StopSquelchMode(); - theSuccess = true; - } - - return theSuccess; -} - -void ScorePanel::mousePressed(MouseCode code, Panel* panel) -{ - if(gHUD.m_iIntermission) - return; - - if (!GetClientVoiceMgr()->IsInSquelchMode()) - { - //GetClientVoiceMgr()->StartSquelchMode(); - //m_HitTestPanel.setVisible(false); - this->SetSquelchMode(true); - } - else if (m_iHighlightRow >= 0) - { - // mouse has been pressed, toggle mute state - int iPlayer = m_iSortedRows[m_iHighlightRow]; - if (iPlayer > 0) - { - // print text message - hud_player_info_t *pl_info = &g_PlayerInfoList[iPlayer]; - - if (pl_info && pl_info->name && pl_info->name[0]) - { - char string[256]; - if (GetClientVoiceMgr()->IsPlayerBlocked(iPlayer)) - { - char string1[1024]; - - // remove mute - GetClientVoiceMgr()->SetPlayerBlockedState(iPlayer, false); - - sprintf( string1, CHudTextMessage::BufferedLocaliseTextString( "#Unmuted" ), pl_info->name ); - sprintf( string, "%c** %s\n", HUD_PRINTTALK, string1 ); - - gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, (int)strlen(string)+1, string ); - } - else - { - char string1[1024]; - char string2[1024]; - - // mute the player - GetClientVoiceMgr()->SetPlayerBlockedState(iPlayer, true); - - sprintf( string1, CHudTextMessage::BufferedLocaliseTextString( "#Muted" ), pl_info->name ); - sprintf( string2, CHudTextMessage::BufferedLocaliseTextString( "#No_longer_hear_that_player" ) ); - sprintf( string, "%c** %s %s\n", HUD_PRINTTALK, string1, string2 ); - - gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, (int)strlen(string)+1, string ); - } - } - } - } -} - -void ScorePanel::cursorMoved(int x, int y, Panel *panel) -{ - if (GetClientVoiceMgr()->IsInSquelchMode()) - { - // look for which cell the mouse is currently over - for (int i = 0; i < NUM_ROWS; i++) - { - int row, col; - if (m_PlayerGrids[i].getCellAtPoint(x, y, row, col)) - { - MouseOverCell(i, col); - return; - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handles mouse movement over a cell -// Input : row - -// col - -//----------------------------------------------------------------------------- -void ScorePanel::MouseOverCell(int row, int col) -{ - CLabelHeader *label = &m_PlayerEntries[col][row]; - - // clear the previously highlighted label - if (m_pCurrentHighlightLabel != label) - { - m_pCurrentHighlightLabel = NULL; - m_iHighlightRow = -1; - } - if (!label) - return; - - // don't act on teams - if (m_iIsATeam[row] != TEAM_IND) - return; - - // don't act on disconnected players or ourselves - hud_player_info_t *pl_info = &g_PlayerInfoList[ m_iSortedRows[row] ]; - if (!pl_info->name || !pl_info->name[0]) - return; - - if (pl_info->thisplayer && !gEngfuncs.IsSpectateOnly() ) - return; - - // only act on audible players - if (!GetClientVoiceMgr()->IsPlayerAudible(m_iSortedRows[row])) - return; - - // setup the new highlight - m_pCurrentHighlightLabel = label; - m_iHighlightRow = row; -} - -//----------------------------------------------------------------------------- -// Purpose: Label paint functions - take into account current highligh status -//----------------------------------------------------------------------------- -void CLabelHeader::paintBackground() -{ - Color oldBg; - getBgColor(oldBg); - - if (gViewPort->GetScoreBoard()->m_iHighlightRow == _row) - { - setBgColor(134, 91, 19, 0); - } - - Panel::paintBackground(); - - setBgColor(oldBg); -} - - -//----------------------------------------------------------------------------- -// Purpose: Label paint functions - take into account current highligh status -//----------------------------------------------------------------------------- -void CLabelHeader::paint() -{ - Color oldFg; - getFgColor(oldFg); - - if (gViewPort->GetScoreBoard()->m_iHighlightRow == _row) - { - setFgColor(255, 255, 255, 0); - } - - // draw text - int x, y, iwide, itall; - getTextSize(iwide, itall); - calcAlignment(iwide, itall, x, y); - _dualImage->setPos(x, y); - - int x1, y1; - _dualImage->GetImage(1)->getPos(x1, y1); - _dualImage->GetImage(1)->setPos(_gap, y1); - - _dualImage->doPaint(this); - - // get size of the panel and the image - if (_image) - { - Color imgColor; - getFgColor( imgColor ); - if( _useFgColorAsImageColor ) - { - _image->setColor( imgColor ); - } - _image->getSize(iwide, itall); - calcAlignment(iwide, itall, x, y); - _image->setPos(x, y); - _image->doPaint(this); - } - - setFgColor(oldFg[0], oldFg[1], oldFg[2], oldFg[3]); -} - - -void CLabelHeader::calcAlignment(int iwide, int itall, int &x, int &y) -{ - // calculate alignment ourselves, since vgui is so broken - int wide, tall; - getSize(wide, tall); - - x = 0, y = 0; - - // align left/right - switch (_contentAlignment) - { - // left - case Label::a_northwest: - case Label::a_west: - case Label::a_southwest: - { - x = 0; - break; - } - - // center - case Label::a_north: - case Label::a_center: - case Label::a_south: - { - x = (wide - iwide) / 2; - break; - } - - // right - case Label::a_northeast: - case Label::a_east: - case Label::a_southeast: - { - x = wide - iwide; - break; - } - } - - // top/down - switch (_contentAlignment) - { - // top - case Label::a_northwest: - case Label::a_north: - case Label::a_northeast: - { - y = 0; - break; - } - - // center - case Label::a_west: - case Label::a_center: - case Label::a_east: - { - y = (tall - itall) / 2; - break; - } - - // south - case Label::a_southwest: - case Label::a_south: - case Label::a_southeast: - { - y = tall - itall; - break; - } - } - -// don't clip to Y -// if (y < 0) -// { -// y = 0; -// } - if (x < 0) - { - x = 0; - } - - x += _offset[0]; - y += _offset[1]; -} +//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: VGUI scoreboard +// +// $Workfile: $ +// $Date: 2002/10/24 21:13:15 $ +// +//----------------------------------------------------------------------------- +// $Log: vgui_ScorePanel.cpp,v $ +// Revision 1.15 2002/10/24 21:13:15 Flayra +// - Fixed gamma correction for auth icons +// +// Revision 1.14 2002/10/18 22:13:48 Flayra +// - Gamma-correction for auth icons +// +// Revision 1.13 2002/10/16 00:37:33 Flayra +// - Added support for authentication in scoreboard +// +// Revision 1.12 2002/09/09 19:42:55 Flayra +// - Fixed problem where scores were sometimes displayed for marines +// +// Revision 1.11 2002/08/31 18:02:11 Flayra +// - Work at VALVe +// +// Revision 1.10 2002/08/09 00:11:33 Flayra +// - Fixed scoreboard +// +// Revision 1.9 2002/07/08 16:15:13 Flayra +// - Refactored team color management +// +// Revision 1.8 2002/06/25 17:06:48 Flayra +// - Removed memory overwrite from hlcoder list +// +// Revision 1.7 2002/06/03 16:12:21 Flayra +// - Show player status for players on your team (LEV1, GEST, COMM, etc.) +// +// Revision 1.6 2002/04/16 19:30:21 Charlie +// - Fixed crash when holding tab right when joining server, added "REIN" status +// +// Revision 1.5 2002/03/27 21:17:18 Charlie +// - Scoreboard now shows alien scores, and doesn't show marine scores. Also draws DEAD and COMM indicators, and scoring is handled properly for aliens (points for kills, buildings) +// +// Revision 1.4 2002/03/08 02:37:52 Charlie +// - Refactored crappy-ass score panel. It's not perfect, but it's better. Removed TFC code, added DEAD and COMM tags to scoreboard. +// +// Revision 1.3 2001/11/13 17:51:02 Charlie +// - Increased max teams, changed team colors (allow aliens vs. aliens and fronts vs. fronts), general scoreboard support +// +// Revision 1.2 2001/09/13 22:28:01 Charlie +// - Updated NS with new Half-life 1108 patch in preparation for voice support and spectator mode +// +// Revision 1.1.1.1.2.1 2001/09/13 14:42:30 Charlie +// - HL1108 +// +// +// $NoKeywords: $ +//============================================================================= + + +#include + +#include "hud.h" +#include "cl_util.h" +#include "common/const.h" +#include "common/entity_state.h" +#include "common/cl_entity.h" +#include "vgui_TeamFortressViewport.h" +#include "vgui_ScorePanel.h" +#include "..\game_shared\vgui_helpers.h" +#include "..\game_shared\vgui_loadtga.h" +#include "mod/AvHConstants.h" +#include "mod/AvHTitles.h" +#include "mod/AvHBasePlayerWeaponConstants.h" +#include "vgui_SpectatorPanel.h" +#include "cl_dll/demo.h" +#include "mod/AvHServerVariables.h" +#include "util\STLUtil.h" +#include "ui/ScoreboardIcon.h" + +#include "common/ITrackerUser.h" +extern ITrackerUser *g_pTrackerUser; + +hud_player_info_t g_PlayerInfoList[MAX_PLAYERS+1]; // player info from the engine +extra_player_info_t g_PlayerExtraInfo[MAX_PLAYERS+1]; // additional player info sent directly to the client dll +team_info_t g_TeamInfo[MAX_TEAMS+1]; +int g_IsSpectator[MAX_PLAYERS+1]; + +int HUD_IsGame( const char *game ); +int EV_TFC_IsAllyTeam( int iTeam1, int iTeam2 ); + +// Scoreboard dimensions +#define SBOARD_TITLE_SIZE_Y YRES(22) + +#define X_BORDER XRES(4) + +void LoadData(void* inBuffer, const unsigned char* inData, int inSizeToCopy, int& inSizeVariable); +void SaveData(unsigned char* inBuffer, const void* inData, int inSizeToCopy, int& inSizeVariable); + +int ScorePanel_InitializeDemoPlayback(int inSize, unsigned char* inBuffer) +{ + int theBytesRead = 0; + + LoadData(&g_PlayerInfoList, inBuffer, (MAX_PLAYERS+1)*sizeof(hud_player_info_t), theBytesRead); + LoadData(&g_PlayerExtraInfo, inBuffer, (MAX_PLAYERS+1)*sizeof(extra_player_info_t), theBytesRead); + LoadData(&g_TeamInfo, inBuffer, (MAX_PLAYERS+1)*sizeof(team_info_t), theBytesRead); + LoadData(&g_IsSpectator, inBuffer, (MAX_PLAYERS+1)*sizeof(int), theBytesRead); + + return theBytesRead; +} + +void ScorePanel_InitializeDemoRecording() +{ + // Now save out team info + int theTotalSize = (MAX_PLAYERS + 1)*(sizeof(hud_player_info_t) + sizeof(extra_player_info_t) + sizeof(team_info_t) + sizeof(int)); + unsigned char* theCharArray = new unsigned char[theTotalSize]; + if(theCharArray) + { + int theCounter = 0; + SaveData(theCharArray, &g_PlayerInfoList, (MAX_PLAYERS+1)*sizeof(hud_player_info_t), theCounter); + SaveData(theCharArray, &g_PlayerExtraInfo, (MAX_PLAYERS+1)*sizeof(extra_player_info_t), theCounter); + SaveData(theCharArray, &g_TeamInfo, (MAX_PLAYERS+1)*sizeof(team_info_t), theCounter); + SaveData(theCharArray, &g_IsSpectator, (MAX_PLAYERS+1)*sizeof(int), theCounter); + + Demo_WriteBuffer(TYPE_PLAYERINFO, theTotalSize, theCharArray); + } +} + +// Column sizes +class SBColumnInfo +{ +public: + char *m_pTitle; // If null, ignore, if starts with #, it's localized, otherwise use the string directly. + int m_Width; // Based on 640 width. Scaled to fit other resolutions. + Label::Alignment m_Alignment; +}; + +// grid size is marked out for 640x480 screen + +SBColumnInfo g_ColumnInfo[NUM_COLUMNS] = +{ + {NULL, 24, Label::a_center}, // tracker column + {NULL, 24, Label::a_center}, // status icons + {NULL, 110, Label::a_center}, // name + {NULL, 56, Label::a_center}, // class + {NULL, 40, Label::a_center}, // resources + {NULL, 18, Label::a_center}, // weld + {NULL, 18, Label::a_center}, // weld + {"#SCORE", 35, Label::a_center}, // score + {"#KILLS", 35, Label::a_center}, // kills + {"#DEATHS", 35, Label::a_center}, // deaths + {"#LATENCY", 35, Label::a_center}, // ping + {"#VOICE", 40, Label::a_center}, + {NULL, 2, Label::a_center}, // blank column to take up the slack +}; + + +#define TEAM_NO 0 +#define TEAM_YES 1 +#define TEAM_SPECTATORS 2 +#define TEAM_BLANK 3 + +//----------------------------------------------------------------------------- +// ScorePanel::HitTestPanel. +//----------------------------------------------------------------------------- + +void ScorePanel::HitTestPanel::internalMousePressed(MouseCode code) +{ + for(int i=0;i<_inputSignalDar.getCount();i++) + { + _inputSignalDar[i]->mousePressed(code,this); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +vgui::Color BuildColor( int R, int G, int B, float gamma ) +{ + ASSERT( gamma != 0 ); + return vgui::Color( R/gamma, G/gamma, B/gamma, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Create the ScoreBoard panel +//----------------------------------------------------------------------------- +ScorePanel::ScorePanel(int x,int y,int wide,int tall) : Panel(x,y,wide,tall) +{ + CSchemeManager *pSchemes = gViewPort->GetSchemeManager(); + SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle("Scoreboard Title Text"); + SchemeHandle_t hSmallScheme = pSchemes->getSchemeHandle("Scoreboard Small Text"); + SchemeHandle_t hTinyScheme = pSchemes->getSchemeHandle("Scoreboard Tiny Text"); + Font *tfont = pSchemes->getFont(hTitleScheme); + Font *smallfont = pSchemes->getFont(hSmallScheme); + Font *tinyfont = pSchemes->getFont(hTinyScheme); + + setBgColor(0, 0, 0, 96); + m_pCurrentHighlightLabel = NULL; + m_iHighlightRow = -1; + // : 0001073 + m_pTrackerIcon = NULL; + m_pDevIcon = NULL; + m_pPTIcon = NULL; + m_pGuideIcon = NULL; + m_pServerOpIcon = NULL; + m_pContribIcon = NULL; + m_pCheatingDeathIcon = NULL; + m_pVeteranIcon = NULL; + + m_pHMG = NULL; + m_pMine = NULL; + m_pWeld = NULL; + m_pGL = NULL; + m_pSG = NULL; + + + m_pTrackerIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardtracker.tga"); + m_pDevIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboarddev.tga"); + m_pPTIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardpt.tga"); + m_pGuideIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardguide.tga"); + m_pServerOpIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardserverop.tga"); + m_pContribIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardcontrib.tga"); + m_pCheatingDeathIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardcd.tga"); + m_pVeteranIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardveteran.tga"); + + + + m_pHMG = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardhmg.tga"); + m_pMine = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardmine.tga"); + m_pWeld = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardwelder.tga"); + m_pGL = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardgl.tga"); + m_pSG = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardsg.tga"); + + m_iIconFrame = 0; + m_iLastFrameIncrementTime = gHUD.GetTimeOfLastUpdate(); + + // Initialize the top title. + m_TitleLabel.setFont(tfont); + m_TitleLabel.setText(""); + m_TitleLabel.setBgColor( 0, 0, 0, 255 ); + m_TitleLabel.setFgColor( Scheme::sc_primary1 ); + m_TitleLabel.setContentAlignment( vgui::Label::a_center ); + + LineBorder *border = new LineBorder(Color(60, 60, 60, 128)); + setBorder(border); + setPaintBorderEnabled(true); + + int xpos = g_ColumnInfo[0].m_Width + 3; + if (ScreenWidth() >= 640) + { + // only expand column size for res greater than 640 + xpos = XRES(xpos); + } + m_TitleLabel.setBounds(xpos, 4, wide, SBOARD_TITLE_SIZE_Y); + m_TitleLabel.setContentFitted(false); + m_TitleLabel.setParent(this); + + // Setup the header (labels like "name", "class", etc..). + m_HeaderGrid.SetDimensions(NUM_COLUMNS, 1); + m_HeaderGrid.SetSpacing(0, 0); + + for(int i=0; i < NUM_COLUMNS; i++) + { + if (g_ColumnInfo[i].m_pTitle && g_ColumnInfo[i].m_pTitle[0] == '#') + m_HeaderLabels[i].setText(CHudTextMessage::BufferedLocaliseTextString(g_ColumnInfo[i].m_pTitle)); + else if(g_ColumnInfo[i].m_pTitle) + m_HeaderLabels[i].setText(g_ColumnInfo[i].m_pTitle); + + int xwide = g_ColumnInfo[i].m_Width; + if (ScreenWidth() >= 640) + { + xwide = XRES(xwide); + } + else if (ScreenWidth() == 400) + { + // hack to make 400x300 resolution scoreboard fit + if (i == 1) + { + // reduces size of player name cell + xwide -= 28; + } + else if (i == 0) + { + // tracker icon cell + xwide -= 8; + } + } + + m_HeaderGrid.SetColumnWidth(i, xwide); + m_HeaderGrid.SetEntry(i, 0, &m_HeaderLabels[i]); + + m_HeaderLabels[i].setBgColor(0,0,0,255); + m_HeaderLabels[i].setBgColor(0,0,0,255); + + int theColorIndex = 0; + Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope()); + int theR, theG, theB, theA; + gammaAdjustedTeamColor.getColor(theR, theG, theB, theA); + m_HeaderLabels[i].setFgColor(theR, theG, theB, theA); + + m_HeaderLabels[i].setFont(smallfont); + m_HeaderLabels[i].setContentAlignment(g_ColumnInfo[i].m_Alignment); + + int yres = 12; + if (ScreenHeight() >= 480) + { + yres = YRES(yres); + } + m_HeaderLabels[i].setSize(50, yres); + } + + // Set the width of the last column to be the remaining space. + int ex, ey, ew, eh; + m_HeaderGrid.GetEntryBox(NUM_COLUMNS - 2, 0, ex, ey, ew, eh); + m_HeaderGrid.SetColumnWidth(NUM_COLUMNS - 1, (wide - X_BORDER) - (ex + ew)); + + m_HeaderGrid.AutoSetRowHeights(); + m_HeaderGrid.setBounds(X_BORDER, SBOARD_TITLE_SIZE_Y, wide - X_BORDER*2, m_HeaderGrid.GetRowHeight(0)); + m_HeaderGrid.setParent(this); + m_HeaderGrid.setBgColor(0,0,0,255); + + + // Now setup the listbox with the actual player data in it. + int headerX, headerY, headerWidth, headerHeight; + m_HeaderGrid.getBounds(headerX, headerY, headerWidth, headerHeight); + m_PlayerList.setBounds(headerX, headerY+headerHeight, headerWidth, tall - headerY - headerHeight - 6); + m_PlayerList.setBgColor(0,0,0,255); + m_PlayerList.setParent(this); + + for(int row=0; row < NUM_ROWS; row++) + { + CGrid *pGridRow = &m_PlayerGrids[row]; + + pGridRow->SetDimensions(NUM_COLUMNS, 1); + + for(int col=0; col < NUM_COLUMNS; col++) + { + m_PlayerEntries[col][row].setContentFitted(false); + m_PlayerEntries[col][row].setRow(row); + m_PlayerEntries[col][row].addInputSignal(this); + pGridRow->SetEntry(col, 0, &m_PlayerEntries[col][row]); + } + + pGridRow->setBgColor(0,0,0,255); +// pGridRow->SetSpacing(2, 0);f + pGridRow->SetSpacing(0, 0); + pGridRow->CopyColumnWidths(&m_HeaderGrid); + pGridRow->AutoSetRowHeights(); + pGridRow->setSize(PanelWidth(pGridRow), pGridRow->CalcDrawHeight()); + pGridRow->RepositionContents(); + + m_PlayerList.AddItem(pGridRow); + } + + + // Add the hit test panel. It is invisible and traps mouse clicks so we can go into squelch mode. + m_HitTestPanel.setBgColor(0,0,0,255); + m_HitTestPanel.setParent(this); + m_HitTestPanel.setBounds(0, 0, wide, tall); + m_HitTestPanel.addInputSignal(this); + + m_pCloseButton = new CommandButton( "x", wide-XRES(12 + 4), YRES(2), XRES( 12 ) , YRES( 12 ) ); + m_pCloseButton->setParent( this ); + m_pCloseButton->addActionSignal( new CMenuHandler_StringCommandWatch( "-showscores", true ) ); + m_pCloseButton->setBgColor(0,0,0,255); + m_pCloseButton->setFgColor( 255, 255, 255, 0 ); + m_pCloseButton->setFont(tfont); + m_pCloseButton->setBoundKey( (char)255 ); + m_pCloseButton->setContentAlignment(Label::a_center); + Initialize(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called each time a new level is started. +//----------------------------------------------------------------------------- +void ScorePanel::Initialize( void ) +{ + // Clear out scoreboard data + m_iLastKilledBy = 0; + m_fLastKillTime = 0; + m_iPlayerNum = 0; + m_iNumTeams = 0; + // : 0001073 +// for( int counter = 0; counter < MAX_PLAYERS+1; counter++ ) +// { +// delete g_PlayerExtraInfo[counter].icon; +// } + + memset( g_PlayerExtraInfo, 0, sizeof g_PlayerExtraInfo ); + memset( g_TeamInfo, 0, sizeof g_TeamInfo ); +} + +bool HACK_GetPlayerUniqueID( int iPlayer, char playerID[16] ) +{ + return !!gEngfuncs.GetPlayerUniqueID( iPlayer, playerID ); +} +//----------------------------------------------------------------------------- +// Purpose: Recalculate the internal scoreboard data +//----------------------------------------------------------------------------- +void ScorePanel::Update() +{ + // Set the title + char title[128]; + + char theServerName[MAX_SERVERNAME_LENGTH+1]; + if (gViewPort->m_szServerName) + { + memset(theServerName, 0, MAX_SERVERNAME_LENGTH+1); + int iServerNameLength = max((int)strlen(gViewPort->m_szServerName),MAX_SERVERNAME_LENGTH); + strncat(theServerName, gViewPort->m_szServerName, iServerNameLength); + } + theServerName[MAX_SERVERNAME_LENGTH]=0; + char theMapName[MAX_MAPNAME_LENGTH+1]; + sprintf(theMapName, "%s", gHUD.GetMapName().c_str()); + + int theTimeElapsed = gHUD.GetGameTime(); + char elapsedString[64]; + if ( theTimeElapsed > 0 ) { + int theMinutesElapsed = theTimeElapsed/60; + int theSecondsElapsed = theTimeElapsed%60; + sprintf(elapsedString, "Game time: %d:%02d", theMinutesElapsed, theSecondsElapsed); + } + else { + sprintf(elapsedString, ""); + } + + sprintf(title, "%32s Map: %s %s", theServerName, theMapName, elapsedString); + + m_TitleLabel.setText(title); + + int theColorIndex = 0; + + // Set gamma-correct title color + Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope()); + + int theR, theG, theB, theA; + gammaAdjustedTeamColor.getColor(theR, theG, theB, theA); + + m_TitleLabel.setFgColor(theR, theG, theB, theA); + + m_iRows = 0; + gViewPort->GetAllPlayersInfo(); + + // Clear out sorts + int i = 0; + for (i = 0; i < NUM_ROWS; i++) + { + m_iSortedRows[i] = 0; + m_iIsATeam[i] = TEAM_IND; + } + + // Fix for memory overrun bug + for (i = 0; i < MAX_PLAYERS; i++) + { + m_bHasBeenSorted[i] = false; + } + + SortTeams(); + + // set scrollbar range + m_PlayerList.SetScrollRange(m_iRows); + + FillGrid(); + if ( gViewPort->m_pSpectatorPanel->m_menuVisible ) + { + m_pCloseButton->setVisible ( true ); + } + else + { + m_pCloseButton->setVisible ( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sort all the teams +//----------------------------------------------------------------------------- +void ScorePanel::SortTeams() +{ + // clear out team scores + float theCurrentTime = gHUD.GetTimeOfLastUpdate(); + + for ( int i = 1; i <= m_iNumTeams; i++ ) + { + if ( !g_TeamInfo[i].scores_overriden ) + g_TeamInfo[i].score =0; + g_TeamInfo[i].frags = g_TeamInfo[i].deaths = g_TeamInfo[i].ping = g_TeamInfo[i].packetloss = 0; + } + + // recalc the team scores, then draw them + for ( i = 1; i <= MAX_PLAYERS; i++ ) + { + if ( g_PlayerInfoList[i].name == NULL ) + continue; // empty player slot, skip + + if ( g_PlayerExtraInfo[i].teamname[0] == 0 ) + continue; // skip over players who are not in a team + + // find what team this player is in + for ( int j = 1; j <= m_iNumTeams; j++ ) + { + if ( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) ) + break; + } + if ( j > m_iNumTeams ) // player is not in a team, skip to the next guy + continue; + + if ( !g_TeamInfo[j].scores_overriden ) + { + g_TeamInfo[j].score += g_PlayerExtraInfo[i].score; + } + + g_TeamInfo[j].deaths += g_PlayerExtraInfo[i].deaths; + g_TeamInfo[j].frags += g_PlayerExtraInfo[i].frags; + g_TeamInfo[j].ping += g_PlayerInfoList[i].ping; + g_TeamInfo[j].packetloss += g_PlayerInfoList[i].packetloss; + + if ( g_PlayerInfoList[i].thisplayer ) + g_TeamInfo[j].ownteam = TRUE; + else + g_TeamInfo[j].ownteam = FALSE; + + // Set the team's number (used for team colors) + g_TeamInfo[j].teamnumber = g_PlayerExtraInfo[i].teamnumber; + } + + // find team ping/packetloss averages + for ( i = 1; i <= m_iNumTeams; i++ ) + { + g_TeamInfo[i].already_drawn = FALSE; + + if ( g_TeamInfo[i].players > 0 ) + { + g_TeamInfo[i].ping /= g_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping + g_TeamInfo[i].packetloss /= g_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping + } + } + vector teams; + if ( gHUD.GetHUDTeam() == TEAM_TWO || gHUD.GetHUDTeam() == TEAM_FOUR ) { + SortActivePlayers(kAlien1Team); + SortActivePlayers(kMarine1Team); + SortActivePlayers(kAlien2Team); + SortActivePlayers(kMarine2Team); + } + else { + SortActivePlayers(kMarine1Team); + SortActivePlayers(kAlien1Team); + SortActivePlayers(kMarine2Team); + SortActivePlayers(kAlien2Team); + } + + SortActivePlayers(kSpectatorTeam); + SortActivePlayers(kUndefinedTeam); +} + +void ScorePanel::SortActivePlayers(char* inTeam, bool inSortByEntityIndex) +{ + for(int i = 1; i <= m_iNumTeams; i++) + { + if(!strcmp(g_TeamInfo[i].name, inTeam)) + { + int best_team = i; + + // Put this team in the sorted list + m_iSortedRows[ m_iRows ] = best_team; + m_iIsATeam[ m_iRows ] = TEAM_YES; + g_TeamInfo[best_team].already_drawn = TRUE; // set the already_drawn to be TRUE, so this team won't get sorted again + m_iRows++; + + // Now sort all the players on this team + SortPlayers(0, g_TeamInfo[best_team].name, inSortByEntityIndex); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sort a list of players +//----------------------------------------------------------------------------- +void ScorePanel::SortPlayers( int iTeam, char *team, bool inSortByEntityIndex) +{ + bool bCreatedTeam = false; + + // draw the players, in order, and restricted to team if set + while ( 1 ) + { + // Find the top ranking player + int theBestTotalScore = -99999; + int theBestDeaths = 0; + int theBestPlayer = 0; + + for ( int i = 1; i <= MAX_PLAYERS; i++ ) + { + if ( m_bHasBeenSorted[i] == false && g_PlayerInfoList[i].name ) + { + cl_entity_t *ent = gEngfuncs.GetEntityByIndex( i ); + + if ( ent && !(team && stricmp(g_PlayerExtraInfo[i].teamname, team)) ) + { + extra_player_info_t *pl_info = &g_PlayerExtraInfo[i]; + + // Sort by player index to mask marine status + if(inSortByEntityIndex) + { + if((theBestPlayer == 0) || (i < theBestPlayer)) + { + theBestPlayer = i; + } + } + else + { + // overall rank = score + kills (with least deaths breaking ties) + int thePlayerScore = pl_info->score; + int thePlayerDeaths = pl_info->deaths; + if((thePlayerScore > theBestTotalScore) || ((thePlayerScore == theBestTotalScore) && (pl_info->deaths < theBestDeaths))) + { + theBestPlayer = i; + theBestTotalScore = thePlayerScore; + theBestDeaths = thePlayerDeaths; + } + } + } + } + } + + if ( !theBestPlayer ) + break; + + // If we haven't created the Team yet, do it first + if (!bCreatedTeam && iTeam) + { + m_iIsATeam[ m_iRows ] = iTeam; + m_iRows++; + + bCreatedTeam = true; + } + + // Put this player in the sorted list + m_iSortedRows[ m_iRows ] = theBestPlayer; + m_bHasBeenSorted[ theBestPlayer ] = true; + m_iRows++; + } + + if (team) + { + m_iIsATeam[m_iRows++] = TEAM_BLANK; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Recalculate the existing teams in the match +//----------------------------------------------------------------------------- +void ScorePanel::RebuildTeams() +{ + // clear out player counts from teams + for ( int i = 1; i <= m_iNumTeams; i++ ) + { + g_TeamInfo[i].players = 0; + } + + // rebuild the team list + gViewPort->GetAllPlayersInfo(); + m_iNumTeams = 0; + for ( i = 1; i <= MAX_PLAYERS; i++ ) + { + if ( g_PlayerInfoList[i].name == NULL ) + continue; + + if ( g_PlayerExtraInfo[i].teamname[0] == 0 ) + continue; // skip over players who are not in a team + + // is this player in an existing team? + for ( int j = 1; j <= m_iNumTeams; j++ ) + { + if ( g_TeamInfo[j].name[0] == '\0' ) + break; + + if ( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) ) + break; + } + + if ( j > m_iNumTeams ) + { // they aren't in a listed team, so make a new one + // search through for an empty team slot + for ( int j = 1; j <= m_iNumTeams; j++ ) + { + if ( g_TeamInfo[j].name[0] == '\0' ) + break; + } + m_iNumTeams = max( j, m_iNumTeams ); + + strncpy( g_TeamInfo[j].name, g_PlayerExtraInfo[i].teamname, MAX_TEAM_NAME ); + g_TeamInfo[j].players = 0; + } + + g_TeamInfo[j].players++; + } + + // clear out any empty teams + for ( i = 1; i <= m_iNumTeams; i++ ) + { + if ( g_TeamInfo[i].players < 1 ) + memset( &g_TeamInfo[i], 0, sizeof(team_info_t) ); + } + + // Update the scoreboard + Update(); +} + +//----------------------------------------------------------------------------- + +int ScorePanel::GetIconFrame(void) +{ + const static int kIconFrameDuration = 0.25; + + int current_time = gHUD.GetTimeOfLastUpdate(); + if( (m_iLastFrameIncrementTime - current_time) > kIconFrameDuration ) + { + m_iLastFrameIncrementTime = current_time; + m_iIconFrame++; + if( m_iIconFrame >= 1000 ) + { + m_iIconFrame = 0; + } + } + return m_iIconFrame; +} + +//----------------------------------------------------------------------------- + +void ScorePanel::FillGrid() +{ + bool isNsMode=( strnicmp(gHUD.GetMapName().c_str(), "ns_", 3) == 0 ); + + CSchemeManager *pSchemes = gViewPort->GetSchemeManager(); + SchemeHandle_t hScheme = pSchemes->getSchemeHandle("Scoreboard Text"); + SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle("Scoreboard Title Text"); + SchemeHandle_t hSmallScheme = pSchemes->getSchemeHandle("Scoreboard Small Text"); + SchemeHandle_t hTinyScheme = pSchemes->getSchemeHandle("Scoreboard Tiny Text"); + + Font *sfont = pSchemes->getFont(hScheme); + Font *tfont = pSchemes->getFont(hTitleScheme); + Font *smallfont = pSchemes->getFont(hSmallScheme); + Font *tinyfont = pSchemes->getFont(hTinyScheme); + + // update highlight position + int x, y; + getApp()->getCursorPos(x, y); + cursorMoved(x, y, this); + + // remove highlight row if we're not in squelch mode + if (!GetClientVoiceMgr()->IsInSquelchMode()) + { + m_iHighlightRow = -1; + } + + bool bNextRowIsGap = false; + m_HeaderLabels[COLUMN_EXTRA].setText(""); + m_HeaderLabels[COLUMN_MINE].setText(""); + m_HeaderLabels[COLUMN_WELD].setText(""); + if ( isNsMode ) { + if ( gHUD.GetHUDTeam() == TEAM_ONE || gHUD.GetHUDTeam() == TEAM_THREE ) { + m_HeaderLabels[COLUMN_EXTRA].setText(CHudTextMessage::BufferedLocaliseTextString("#COLWEAP")); + } + else if ( gHUD.GetHUDTeam() == TEAM_TWO || gHUD.GetHUDTeam() == TEAM_FOUR ) { + m_HeaderLabels[COLUMN_EXTRA].setText(CHudTextMessage::BufferedLocaliseTextString("#COLRES")); + } + } + else { + if ( gHUD.GetHUDTeam() != TEAM_IND && gHUD.GetHUDTeam() != TEAM_SPECT ) + m_HeaderLabels[COLUMN_EXTRA].setText(CHudTextMessage::BufferedLocaliseTextString("#COLLEVEL")); + } + + for(int row=0; row < NUM_ROWS; row++) + { + CGrid *pGridRow = &m_PlayerGrids[row]; + pGridRow->SetRowUnderline(0, false, 0, 0, 0, 0, 0); + + if(row >= m_iRows) + { + for(int col=0; col < NUM_COLUMNS; col++) + m_PlayerEntries[col][row].setVisible(false); + + continue; + } + + bool bRowIsGap = false; + if (bNextRowIsGap) + { + bNextRowIsGap = false; + bRowIsGap = true; + } + + // Get the player's data + int theSortedRow = m_iSortedRows[row]; + hud_player_info_t* pl_info = &g_PlayerInfoList[theSortedRow]; + extra_player_info_t* theExtraPlayerInfo = &g_PlayerExtraInfo[theSortedRow]; + int thePlayerClass = theExtraPlayerInfo->playerclass; + short theTeamNumber = theExtraPlayerInfo->teamnumber; + string theCustomIcon = (string)theExtraPlayerInfo->customicon; +// : 0001073 + short thePlayerAuthentication = theExtraPlayerInfo->auth; + bool thePlayerIsDead = false; + switch( thePlayerClass ) + { + case PLAYERCLASS_DEAD_MARINE: + case PLAYERCLASS_DEAD_ALIEN: + case PLAYERCLASS_REINFORCING: + thePlayerIsDead = true; + break; + } + + // Code to test DEBUG +#if 0 + #ifdef DEBUG + extern int gGlobalDebugAuth; + thePlayerAuthentication = 1; + thePlayerAuthentication <<= gGlobalDebugAuth; + #endif +#endif + + team_info_t* team_info = &g_TeamInfo[m_iSortedRows[row]]; + int theColorIndex = theTeamNumber % iNumberOfTeamColors; + + int theLocalPlayerTeam = 0; + if(gEngfuncs.GetLocalPlayer()) + { + theLocalPlayerTeam = gEngfuncs.GetLocalPlayer()->curstate.team; + } + + for(int col=0; col < NUM_COLUMNS; col++) + { + CLabelHeader *pLabel = &m_PlayerEntries[col][row]; + + pLabel->setVisible(true); + pLabel->setText2(""); + pLabel->setImage(NULL); + pLabel->setFont(sfont); + pLabel->setTextOffset(0, 0); + + int rowheight = 13; + if (ScreenHeight() > 480) + { + rowheight = YRES(rowheight); + } + else + { + // more tweaking, make sure icons fit at low res + rowheight = 15; + } + pLabel->setSize(pLabel->getWide(), rowheight); + pLabel->setBgColor(0, 0, 0, 255); + + char sz[128]; + + Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope()); + pLabel->setFgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 0); + + if (m_iIsATeam[row] == TEAM_BLANK) + { + pLabel->setText(" "); + continue; + } + else if ( m_iIsATeam[row] == TEAM_YES ) + { + theColorIndex = team_info->teamnumber % iNumberOfTeamColors; + + // team color text for team names + + + + // different height for team header rows + rowheight = 20; + if (ScreenHeight() >= 480) + { + rowheight = YRES(rowheight); + } + pLabel->setSize(pLabel->getWide(), rowheight); + pLabel->setFont(tfont); + + pGridRow->SetRowUnderline( 0, + true, + YRES(3), + gammaAdjustedTeamColor[0], + gammaAdjustedTeamColor[1], + gammaAdjustedTeamColor[2], + 0 ); + } + else if ( m_iIsATeam[row] == TEAM_SPECTATORS ) + { + // grey text for spectators + pLabel->setFgColor(100, 100, 100, 0); + + // different height for team header rows + rowheight = 20; + if (ScreenHeight() >= 480) + { + rowheight = YRES(rowheight); + } + pLabel->setSize(pLabel->getWide(), rowheight); + pLabel->setFont(tfont); + + pGridRow->SetRowUnderline(0, true, YRES(3), 100, 100, 100, 0); + } + else + { + if(thePlayerIsDead) + { + pLabel->setFgColor(255, 0, 0, 0); + } + else + { + // team color text for player names + pLabel->setFgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 0); + } + + // Set background color + if ( pl_info && pl_info->thisplayer ) // if it is their name, draw it a different color + { + // Highlight this player + pLabel->setFgColor(Scheme::sc_white); + pLabel->setBgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 196 ); + } + else if ( theSortedRow == m_iLastKilledBy && m_fLastKillTime && m_fLastKillTime > gHUD.m_flTime ) + { + // Killer's name + pLabel->setBgColor( 255,0,0, 255 - ((float)15 * (float)(m_fLastKillTime - gHUD.m_flTime)) ); + } + } + + // Align + switch(col) + { + case COLUMN_NAME: + case COLUMN_CLASS: + pLabel->setContentAlignment( vgui::Label::a_west ); + break; + + case COLUMN_TRACKER: + case COLUMN_RANK_ICON: + case COLUMN_VOICE: + pLabel->setContentAlignment( vgui::Label::a_center ); + break; + + case COLUMN_SCORE: + case COLUMN_KILLS: + case COLUMN_EXTRA: + case COLUMN_MINE: + case COLUMN_WELD: + case COLUMN_DEATHS: + case COLUMN_LATENCY: + default: + pLabel->setContentAlignment( vgui::Label::a_center ); + break; + } + + // Fill out with the correct data + strcpy(sz, ""); + if ( m_iIsATeam[row] ) + { + char sz2[128]; + + switch (col) + { + case COLUMN_NAME: + if ( m_iIsATeam[row] == TEAM_SPECTATORS ) + { + sprintf( sz2, CHudTextMessage::BufferedLocaliseTextString( "#Spectators" ) ); + } + else + { + if(team_info) + { + sprintf( sz2, gViewPort->GetTeamName(team_info->teamnumber) ); + } + } + + strcpy(sz, sz2); + + // Append the number of players + if ( m_iIsATeam[row] == TEAM_YES && team_info) + { + if (team_info->players == 1) + { + sprintf(sz2, "(%d %s)", team_info->players, CHudTextMessage::BufferedLocaliseTextString( "#Player" ) ); + } + else + { + sprintf(sz2, "(%d %s)", team_info->players, CHudTextMessage::BufferedLocaliseTextString( "#Player_plural" ) ); + } + + pLabel->setText2(sz2); + pLabel->setFont2(smallfont); + } + break; + case COLUMN_VOICE: + break; + case COLUMN_CLASS: + break; + case COLUMN_SCORE: + if ((m_iIsATeam[row] == TEAM_YES) && team_info && (theLocalPlayerTeam == team_info->teamnumber || (gHUD.GetPlayMode() == PLAYMODE_OBSERVER))) + sprintf(sz, "%d", team_info->score); + break; + case COLUMN_MINE: + break; + case COLUMN_WELD: + break; + case COLUMN_EXTRA: + break; + case COLUMN_KILLS: + if ((m_iIsATeam[row] == TEAM_YES) && team_info) + sprintf(sz, "%d", team_info->frags ); + break; + case COLUMN_DEATHS: + if ((m_iIsATeam[row] == TEAM_YES) && team_info) + sprintf(sz, "%d", team_info->deaths ); + break; + case COLUMN_LATENCY: + if ((m_iIsATeam[row] == TEAM_YES) && team_info) + sprintf(sz, "%d", team_info->ping ); + break; + default: + break; + } + } + else + { + // Are these stats for an enemy? Score and other stats shouldn't be drawn for enemies. + bool theIsForEnemy = false; + + int theLocalPlayerTeam = 0; + if(gEngfuncs.GetLocalPlayer()) + { + theLocalPlayerTeam = gEngfuncs.GetLocalPlayer()->curstate.team; + } + + if((theLocalPlayerTeam != 0) && (theExtraPlayerInfo->teamnumber != theLocalPlayerTeam)) + { + theIsForEnemy = true; + } + + switch (col) + { + case COLUMN_NAME: + + if (g_pTrackerUser) + { + int playerSlot = m_iSortedRows[row]; + int trackerID = gEngfuncs.GetTrackerIDForPlayer(playerSlot); + const char *trackerName = g_pTrackerUser->GetUserName(trackerID); + if (trackerName && *trackerName) + { + sprintf(sz, " (%s)", trackerName); + pLabel->setText2(sz); + } + } + + if(pl_info) + { + sprintf(sz, "%s ", pl_info->name); + } + break; + case COLUMN_VOICE: + sz[0] = 0; + // in HLTV mode allow spectator to turn on/off commentator voice + if (pl_info && (!pl_info->thisplayer || gEngfuncs.IsSpectateOnly())) + { + GetClientVoiceMgr()->UpdateSpeakerImage(pLabel, theSortedRow); + } + break; + case COLUMN_CLASS: + // No class for other team's members (unless allied or spectator, and make sure player is on our team) + strcpy(sz, ""); + + if(team_info && ((theLocalPlayerTeam == theTeamNumber) || (gHUD.GetPlayMode() == PLAYMODE_OBSERVER))) + { + switch(thePlayerClass) + { + case (int)(PLAYERCLASS_DEAD_MARINE): + case (int)(PLAYERCLASS_DEAD_ALIEN): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassDead)); + break; + case (int)(PLAYERCLASS_REINFORCING): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassReinforcing)); + break; + case (int)(PLAYERCLASS_REINFORCINGCOMPLETE): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassReinforcingComplete)); + break; + case (int)(PLAYERCLASS_ALIVE_JETPACK_MARINE): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassJetpackMarine)); + break; + case (int)(PLAYERCLASS_ALIVE_HEAVY_MARINE): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassHeavyMarine)); + break; + case (int)(PLAYERCLASS_COMMANDER): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassCommander)); + break; + case (int)(PLAYERCLASS_ALIVE_LEVEL1): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel1)); + break; + case (int)(PLAYERCLASS_ALIVE_LEVEL2): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel2)); + break; + case (int)(PLAYERCLASS_ALIVE_LEVEL3): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel3)); + break; + case (int)(PLAYERCLASS_ALIVE_LEVEL4): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel4)); + break; + case (int)(PLAYERCLASS_ALIVE_LEVEL5): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel5)); + break; + case (int)(PLAYERCLASS_ALIVE_DIGESTING): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassDigesting)); + break; + case (int)(PLAYERCLASS_ALIVE_GESTATING): + sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassGestating)); + break; + default: + break; + } + } + break; + + case COLUMN_RANK_ICON: +// : 0001073 +#ifdef USE_OLDAUTH + // Check if we have authority. Right now these override the tracker icons. Listed in increasing order of "importance". + if(thePlayerAuthentication & PLAYERAUTH_CHEATINGDEATH) + { + // Red + pLabel->setImage(m_pCheatingDeathIcon); + pLabel->setFgColorAsImageColor(false); + m_pCheatingDeathIcon->setColor(BuildColor(255, 69, 9, gHUD.GetGammaSlope())); + } + if(thePlayerAuthentication & PLAYERAUTH_VETERAN) + { + // Yellow + pLabel->setImage(m_pVeteranIcon); + pLabel->setFgColorAsImageColor(false); + m_pVeteranIcon->setColor(BuildColor(248, 252, 0, gHUD.GetGammaSlope())); + } + if(thePlayerAuthentication & PLAYERAUTH_BETASERVEROP) + { + // Whitish + pLabel->setImage(m_pServerOpIcon); + pLabel->setFgColorAsImageColor(false); + m_pServerOpIcon->setColor(BuildColor(220, 220, 220, gHUD.GetGammaSlope())); + } + if(thePlayerAuthentication & PLAYERAUTH_CONTRIBUTOR) + { + // Light blue + pLabel->setImage(m_pContribIcon); + pLabel->setFgColorAsImageColor(false); + m_pContribIcon->setColor(BuildColor(117, 214, 241, gHUD.GetGammaSlope())); + } + if(thePlayerAuthentication & PLAYERAUTH_GUIDE) + { + // Magenta + pLabel->setImage(m_pGuideIcon); + pLabel->setFgColorAsImageColor(false); + m_pGuideIcon->setColor(BuildColor(208, 16, 190, gHUD.GetGammaSlope())); + } + if(thePlayerAuthentication & PLAYERAUTH_PLAYTESTER) + { + // Orange + pLabel->setImage(m_pPTIcon); + pLabel->setFgColorAsImageColor(false); + m_pPTIcon->setColor(BuildColor(255, 167, 54, gHUD.GetGammaSlope())); + } + if(thePlayerAuthentication & PLAYERAUTH_DEVELOPER) + { + // TSA blue + pLabel->setImage(m_pDevIcon); + pLabel->setFgColorAsImageColor(false); + m_pDevIcon->setColor(BuildColor(100, 215, 255, gHUD.GetGammaSlope())); + } + + if(thePlayerAuthentication & PLAYERAUTH_SERVEROP) + { + // Bright green + pLabel->setImage(m_pServerOpIcon); + pLabel->setFgColorAsImageColor(false); + m_pServerOpIcon->setColor(BuildColor(0, 255, 0, gHUD.GetGammaSlope())); + } + + // Allow custom icons to override other general icons + if(thePlayerAuthentication & PLAYERAUTH_CUSTOM) + { + if(theCustomIcon != "") + { + string theIconName = theCustomIcon.substr(0, strlen(theCustomIcon.c_str()) - 3); + string theFullCustomIconString = string("gfx/vgui/640_") + theIconName + string(".tga"); + + vgui::BitmapTGA *pIcon = GetIconPointer(theCustomIcon); + + //Icon hasnt been loaded, load it now and add it to list of icons. + if(pIcon == NULL) + { + pIcon = vgui_LoadTGANoInvertAlpha(theFullCustomIconString.c_str()); + + if(pIcon) + m_CustomIconList.push_back( make_pair(pIcon, theCustomIcon) ); + } + + if(pIcon) + { + pLabel->setImage(pIcon); + pLabel->setFgColorAsImageColor(false); + + // Parse color (last 3 bytes are the RGB values 1-9) + string theColor = theCustomIcon.substr( strlen(theCustomIcon.c_str())-3, 3); + int theRed = (MakeIntFromString(theColor.substr(0, 1))/9.0f)*255; + int theGreen = (MakeIntFromString(theColor.substr(1, 1))/9.0f)*255; + int theBlue = (MakeIntFromString(theColor.substr(2, 1))/9.0f)*255; + + pIcon->setColor(BuildColor(theRed, theGreen, theBlue, gHUD.GetGammaSlope())); + } + } + } + + if(g_pTrackerUser) + { + int playerSlot = theSortedRow; + int trackerID = gEngfuncs.GetTrackerIDForPlayer(playerSlot); + + if (g_pTrackerUser->IsFriend(trackerID) && trackerID != g_pTrackerUser->GetTrackerID()) + { + pLabel->setImage(m_pTrackerIcon); + pLabel->setFgColorAsImageColor(false); + m_pTrackerIcon->setColor(Color(255, 255, 255, 0)); + } + } +#else + if( theExtraPlayerInfo->icon ) + { + vgui::Bitmap* image = theExtraPlayerInfo->icon->getImage( this->GetIconFrame() ); + if( image ) { pLabel->setImage( image ); } + } +#endif + break; + case COLUMN_SCORE: + if(!theIsForEnemy && theLocalPlayerTeam != TEAM_IND || (gHUD.GetPlayMode() == PLAYMODE_OBSERVER)) + { + const float kDeltaDisplayTime = 3.0f; + float theTimeSinceChange = gHUD.GetTimeOfLastUpdate() - theExtraPlayerInfo->timeOfLastScoreChange; + if((theExtraPlayerInfo->score > theExtraPlayerInfo->lastScore) && (theTimeSinceChange > 0) && (theTimeSinceChange < kDeltaDisplayTime) && (theExtraPlayerInfo->teamnumber != 0)) + { + // draw score with change + int theDelta = (theExtraPlayerInfo->score - theExtraPlayerInfo->lastScore); + sprintf(sz, "(+%d) %d", theDelta, theExtraPlayerInfo->score); + } + else + { + sprintf(sz, "%d", theExtraPlayerInfo->score); + } + + } + break; + case COLUMN_WELD: + if ((theLocalPlayerTeam == theTeamNumber) || (gHUD.GetPlayMode() == PLAYMODE_OBSERVER)) + { + if ( isNsMode ) { + if ( theExtraPlayerInfo->teamnumber == TEAM_ONE || theExtraPlayerInfo->teamnumber == TEAM_THREE ) { + if ( theExtraPlayerInfo->extra & WEAPON_WELDER ) { + pLabel->setFgColorAsImageColor(false); + pLabel->setImage(m_pWeld); + m_pWeld->setColor(BuildColor(0, 149, 221, gHUD.GetGammaSlope())); + } + } + } + } + break; + + case COLUMN_MINE: + if ((theLocalPlayerTeam == theTeamNumber) || (gHUD.GetPlayMode() == PLAYMODE_OBSERVER)) + { + if ( isNsMode ) { + if ( theExtraPlayerInfo->teamnumber == TEAM_ONE || theExtraPlayerInfo->teamnumber == TEAM_THREE ) { + if ( theExtraPlayerInfo->extra & WEAPON_MINE ) { + pLabel->setFgColorAsImageColor(false); + pLabel->setImage(m_pMine); + m_pMine->setColor(BuildColor(0, 149, 221, gHUD.GetGammaSlope())); + } + } + } + } + break; + + case COLUMN_EXTRA: + if ((theLocalPlayerTeam == theTeamNumber) || (gHUD.GetPlayMode() == PLAYMODE_OBSERVER)) + { + if ( isNsMode ) { + if ( theExtraPlayerInfo->teamnumber == TEAM_ONE || theExtraPlayerInfo->teamnumber == TEAM_THREE ) { + int r=0, g=149, b=221; + if ( theExtraPlayerInfo->extra & WEAPON_HMG ) { + pLabel->setFgColorAsImageColor(false); + pLabel->setImage(m_pHMG); + m_pHMG->setColor(BuildColor(r, g, b, gHUD.GetGammaSlope())); + } + if ( theExtraPlayerInfo->extra & WEAPON_SG ) { + pLabel->setFgColorAsImageColor(false); + pLabel->setImage(m_pSG); + m_pSG->setColor(BuildColor(r, g, b, gHUD.GetGammaSlope())); + } + if ( theExtraPlayerInfo->extra & WEAPON_GL ) { + pLabel->setFgColorAsImageColor(false); + pLabel->setImage(m_pGL); + m_pGL->setColor(BuildColor(r, g, b, gHUD.GetGammaSlope())); + } + } + else if ( theExtraPlayerInfo->teamnumber == TEAM_TWO || theExtraPlayerInfo->teamnumber == TEAM_FOUR ) { + sprintf(sz, "%d", theExtraPlayerInfo->extra); + } + } + else if ( theExtraPlayerInfo->teamnumber == TEAM_ONE || theExtraPlayerInfo->teamnumber == TEAM_TWO || + theExtraPlayerInfo->teamnumber == TEAM_THREE || theExtraPlayerInfo->teamnumber == TEAM_FOUR ) { + sprintf(sz, "%d", theExtraPlayerInfo->extra); + } + } + break; + case COLUMN_KILLS: + sprintf(sz, "%d", theExtraPlayerInfo->frags); + break; + + case COLUMN_DEATHS: + sprintf(sz, "%d", theExtraPlayerInfo->deaths); + break; + case COLUMN_LATENCY: + if(pl_info) + { + sprintf(sz, "%d", pl_info->ping ); + } + break; + default: + break; + } + } + + pLabel->setText(sz); + } + } + + for(row=0; row < NUM_ROWS; row++) + { + CGrid *pGridRow = &m_PlayerGrids[row]; + + pGridRow->AutoSetRowHeights(); + pGridRow->setSize(PanelWidth(pGridRow), pGridRow->CalcDrawHeight()); + pGridRow->RepositionContents(); + } + + // hack, for the thing to resize + m_PlayerList.getSize(x, y); + m_PlayerList.setSize(x, y); +} + + +//----------------------------------------------------------------------------- +// Purpose: Setup highlights for player names in scoreboard +//----------------------------------------------------------------------------- +void ScorePanel::DeathMsg( int killer, int victim ) +{ + // if we were the one killed, or the world killed us, set the scoreboard to indicate suicide + if ( victim == m_iPlayerNum || killer == 0 ) + { + m_iLastKilledBy = killer ? killer : m_iPlayerNum; + m_fLastKillTime = gHUD.m_flTime + 10; // display who we were killed by for 10 seconds + + if ( killer == m_iPlayerNum ) + m_iLastKilledBy = m_iPlayerNum; + } +} + + +void ScorePanel::Open( void ) +{ + RebuildTeams(); + setVisible(true); + m_HitTestPanel.setVisible(true); +} + +bool ScorePanel::SetSquelchMode(bool inMode) +{ + bool theSuccess = false; + + if(inMode && !GetClientVoiceMgr()->IsInSquelchMode()) + { + GetClientVoiceMgr()->StartSquelchMode(); + m_HitTestPanel.setVisible(false); + theSuccess = true; + } + else if(!inMode && GetClientVoiceMgr()->IsInSquelchMode()) + { + GetClientVoiceMgr()->StopSquelchMode(); + theSuccess = true; + } + + return theSuccess; +} + +void ScorePanel::mousePressed(MouseCode code, Panel* panel) +{ + if(gHUD.m_iIntermission) + return; + + if (!GetClientVoiceMgr()->IsInSquelchMode()) + { + //GetClientVoiceMgr()->StartSquelchMode(); + //m_HitTestPanel.setVisible(false); + this->SetSquelchMode(true); + } + else if (m_iHighlightRow >= 0) + { + // mouse has been pressed, toggle mute state + int iPlayer = m_iSortedRows[m_iHighlightRow]; + if (iPlayer > 0) + { + // print text message + hud_player_info_t *pl_info = &g_PlayerInfoList[iPlayer]; + + if (pl_info && pl_info->name && pl_info->name[0]) + { + char string[256]; + if (GetClientVoiceMgr()->IsPlayerBlocked(iPlayer)) + { + char string1[1024]; + + // remove mute + GetClientVoiceMgr()->SetPlayerBlockedState(iPlayer, false); + + sprintf( string1, CHudTextMessage::BufferedLocaliseTextString( "#Unmuted" ), pl_info->name ); + sprintf( string, "%c** %s\n", HUD_PRINTTALK, string1 ); + + gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, (int)strlen(string)+1, string ); + } + else + { + char string1[1024]; + char string2[1024]; + + // mute the player + GetClientVoiceMgr()->SetPlayerBlockedState(iPlayer, true); + + sprintf( string1, CHudTextMessage::BufferedLocaliseTextString( "#Muted" ), pl_info->name ); + sprintf( string2, CHudTextMessage::BufferedLocaliseTextString( "#No_longer_hear_that_player" ) ); + sprintf( string, "%c** %s %s\n", HUD_PRINTTALK, string1, string2 ); + + gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, (int)strlen(string)+1, string ); + } + } + } + } +} + +void ScorePanel::cursorMoved(int x, int y, Panel *panel) +{ + if (GetClientVoiceMgr()->IsInSquelchMode()) + { + // look for which cell the mouse is currently over + for (int i = 0; i < NUM_ROWS; i++) + { + int row, col; + if (m_PlayerGrids[i].getCellAtPoint(x, y, row, col)) + { + MouseOverCell(i, col); + return; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles mouse movement over a cell +// Input : row - +// col - +//----------------------------------------------------------------------------- +void ScorePanel::MouseOverCell(int row, int col) +{ + CLabelHeader *label = &m_PlayerEntries[col][row]; + + // clear the previously highlighted label + if (m_pCurrentHighlightLabel != label) + { + m_pCurrentHighlightLabel = NULL; + m_iHighlightRow = -1; + } + if (!label) + return; + + // don't act on teams + if (m_iIsATeam[row] != TEAM_IND) + return; + + // don't act on disconnected players or ourselves + hud_player_info_t *pl_info = &g_PlayerInfoList[ m_iSortedRows[row] ]; + if (!pl_info->name || !pl_info->name[0]) + return; + + if (pl_info->thisplayer && !gEngfuncs.IsSpectateOnly() ) + return; + + // only act on audible players + //if (!GetClientVoiceMgr()->IsPlayerAudible(m_iSortedRows[row])) + // return; + + // setup the new highlight + m_pCurrentHighlightLabel = label; + m_iHighlightRow = row; +} + +//----------------------------------------------------------------------------- +// Purpose: Label paint functions - take into account current highligh status +//----------------------------------------------------------------------------- +void CLabelHeader::paintBackground() +{ + Color oldBg; + getBgColor(oldBg); + + if (gViewPort->GetScoreBoard()->m_iHighlightRow == _row) + { + setBgColor(134, 91, 19, 0); + } + + Panel::paintBackground(); + + setBgColor(oldBg); +} + + +//----------------------------------------------------------------------------- +// Purpose: Label paint functions - take into account current highligh status +//----------------------------------------------------------------------------- +void CLabelHeader::paint() +{ + Color oldFg; + getFgColor(oldFg); + + if (gViewPort->GetScoreBoard()->m_iHighlightRow == _row) + { + setFgColor(255, 255, 255, 0); + } + + // draw text + int x, y, iwide, itall; + getTextSize(iwide, itall); + calcAlignment(iwide, itall, x, y); + _dualImage->setPos(x, y); + + int x1, y1; + _dualImage->GetImage(1)->getPos(x1, y1); + _dualImage->GetImage(1)->setPos(_gap, y1); + + _dualImage->doPaint(this); + + // get size of the panel and the image + if (_image) + { + Color imgColor; + getFgColor( imgColor ); + if( _useFgColorAsImageColor ) + { + _image->setColor( imgColor ); + } + _image->getSize(iwide, itall); + calcAlignment(iwide, itall, x, y); + _image->setPos(x, y); + _image->doPaint(this); + } + + setFgColor(oldFg[0], oldFg[1], oldFg[2], oldFg[3]); +} + + +void CLabelHeader::calcAlignment(int iwide, int itall, int &x, int &y) +{ + // calculate alignment ourselves, since vgui is so broken + int wide, tall; + getSize(wide, tall); + + x = 0, y = 0; + + // align left/right + switch (_contentAlignment) + { + // left + case Label::a_northwest: + case Label::a_west: + case Label::a_southwest: + { + x = 0; + break; + } + + // center + case Label::a_north: + case Label::a_center: + case Label::a_south: + { + x = (wide - iwide) / 2; + break; + } + + // right + case Label::a_northeast: + case Label::a_east: + case Label::a_southeast: + { + x = wide - iwide; + break; + } + } + + // top/down + switch (_contentAlignment) + { + // top + case Label::a_northwest: + case Label::a_north: + case Label::a_northeast: + { + y = 0; + break; + } + + // center + case Label::a_west: + case Label::a_center: + case Label::a_east: + { + y = (tall - itall) / 2; + break; + } + + // south + case Label::a_southwest: + case Label::a_south: + case Label::a_southeast: + { + y = tall - itall; + break; + } + } + +// don't clip to Y +// if (y < 0) +// { +// y = 0; +// } + if (x < 0) + { + x = 0; + } + + x += _offset[0]; + y += _offset[1]; +} diff --git a/main/source/cl_dll/vgui_ScorePanel.h b/main/source/cl_dll/vgui_ScorePanel.h index 3b74bc3..1bc8715 100644 --- a/main/source/cl_dll/vgui_ScorePanel.h +++ b/main/source/cl_dll/vgui_ScorePanel.h @@ -20,13 +20,16 @@ #define COLUMN_RANK_ICON 1 #define COLUMN_NAME 2 #define COLUMN_CLASS 3 -#define COLUMN_SCORE 4 -#define COLUMN_KILLS 5 -#define COLUMN_DEATHS 6 -#define COLUMN_LATENCY 7 -#define COLUMN_VOICE 8 -#define COLUMN_BLANK 9 -#define NUM_COLUMNS 10 +#define COLUMN_EXTRA 4 +#define COLUMN_WELD 5 +#define COLUMN_MINE 6 +#define COLUMN_SCORE 7 +#define COLUMN_KILLS 8 +#define COLUMN_DEATHS 9 +#define COLUMN_LATENCY 10 +#define COLUMN_VOICE 11 +#define COLUMN_BLANK 12 +#define NUM_COLUMNS 13 #define NUM_ROWS (MAX_PLAYERS + (MAX_SCOREBOARD_TEAMS * 2)) using namespace vgui; @@ -86,6 +89,13 @@ private: vgui::BitmapTGA *m_pContribIcon; vgui::BitmapTGA *m_pCheatingDeathIcon; vgui::BitmapTGA *m_pVeteranIcon; + + vgui::BitmapTGA *m_pHMG; + vgui::BitmapTGA *m_pMine; + vgui::BitmapTGA *m_pWeld; + vgui::BitmapTGA *m_pGL; + vgui::BitmapTGA *m_pSG; + vector< pair > m_CustomIconList; unsigned int m_iIconFrame; diff --git a/main/source/cl_dll/vgui_SpectatorPanel.cpp b/main/source/cl_dll/vgui_SpectatorPanel.cpp index f2fb88d..eacf762 100644 --- a/main/source/cl_dll/vgui_SpectatorPanel.cpp +++ b/main/source/cl_dll/vgui_SpectatorPanel.cpp @@ -89,15 +89,15 @@ void SpectatorPanel::StateChanged(CCheckButton2* pButton) gHUD.m_Spectator.SetMode(theMode); - if (m_autoDirectorButton != NULL) + if (m_autoDirectorButton != NULL && pButton == m_autoDirectorButton ) { if (m_autoDirectorButton->IsChecked()) { - gEngfuncs.Cvar_SetValue("spec_autodirector", 1); + gEngfuncs.Cvar_SetValue("spec_autodirector", 0); } else { - gEngfuncs.Cvar_SetValue("spec_autodirector", 0); + gEngfuncs.Cvar_SetValue("spec_autodirector", 1); } } diff --git a/main/source/cl_dll/vgui_TeamFortressViewport.cpp b/main/source/cl_dll/vgui_TeamFortressViewport.cpp index c4665c5..15311db 100644 --- a/main/source/cl_dll/vgui_TeamFortressViewport.cpp +++ b/main/source/cl_dll/vgui_TeamFortressViewport.cpp @@ -1,2631 +1,2641 @@ -//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: Client DLL VGUI Viewport -// -// $Workfile: $ -// $Date: 2002/10/28 20:32:17 $ -// -//----------------------------------------------------------------------------- -// $Log: vgui_TeamFortressViewport.cpp,v $ -// Revision 1.12 2002/10/28 20:32:17 Flayra -// - Don't display disconcerting error message -// -// Revision 1.11 2002/10/16 00:37:33 Flayra -// - Added support for authentication in scoreboard -// -// Revision 1.10 2002/07/08 16:15:13 Flayra -// - Refactored team color management -// -// Revision 1.9 2002/04/16 19:32:07 Charlie -// - Removed crappy way of handling pop-up menu changes, removed pressing enter to go back to RR (was getting in way of chat, now just use "readyroom" - F4 -) -// -// Revision 1.8 2002/02/25 20:35:41 Charlie -// - Added hotgrouping of units and buildings, moving to units or buildings when double-tapping, and camera tracking -// -// Revision 1.7 2002/01/30 18:34:32 Charlie -// - Fixed doubled-cursor problem with scoreboard (only show sprite cursor, not Windows cursor) -// -// Revision 1.6 2001/11/13 17:51:02 Charlie -// - Increased max teams, changed team colors (allow aliens vs. aliens and fronts vs. fronts), general scoreboard support -// -// Revision 1.5 2001/10/22 19:26:32 Charlie -// - Changes to make scoreboard work in commander mode -// -// Revision 1.4 2001/09/13 15:01:02 Charlie -// - Merging with 1108 -// -// Revision 1.3 2001/06/02 14:26:47 charlie -// - Commented out UpdateCursorState because it was causing problems (look into this) -// Revision 1.1.1.1.2.1 2001/09/13 14:42:30 Charlie -// - HL1108 -// -// Revision 1.2 2001/04/09 19:31:35 charlie -// - Quick hacky tests to try out menus for team/class picking...yuck -// -// Revision 1.1.1.1 2000/06/17 14:12:45 charlie -// Final version of new HL SDK. May not compile. -// Previous versions of my MSVC project files and utility .bat files. -// This is my starting point; there is no mod-specific code in here. -// -// -// $NoKeywords: $ -//============================================================================= -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hud.h" -#include "cl_util.h" -#include "camera.h" -#include "kbutton.h" -#include "common/cvardef.h" -#include "common/usercmd.h" -#include "common/const.h" -#include "camera.h" -#include "in_defs.h" -#include "pm_shared/pm_shared.h" -#include "../engine/keydefs.h" -#include "demo.h" -#include "common/demo_api.h" - -#include "vgui_int.h" -#include "vgui_TeamFortressViewport.h" -#include "vgui_ServerBrowser.h" -#include "vgui_ScorePanel.h" -#include "vgui_SpectatorPanel.h" -#include "game_shared\vgui_loadtga.h" -#include "mod/AvHConstants.h" -#include "mod/AvHTitles.h" -#include "mod/AvHPieMenuHandler.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHCommandConstants.h" -#include "ui/ChatPanel.h" -#include "mod/AvHNetworkMessages.h" -#include "util/STLUtil.h" - -extern int g_iVisibleMouse; -class CCommandMenu; -int g_iPlayerClass; -int g_iTeamNumber; -int g_iUser1; -int g_iUser2; -int g_iUser3; - -// Scoreboard positions -#define SBOARD_INDENT_X XRES(75) -#define SBOARD_INDENT_Y YRES(40) - -// low-res scoreboard indents -#define SBOARD_INDENT_X_512 30 -#define SBOARD_INDENT_Y_512 30 - -#define SBOARD_INDENT_X_400 0 -#define SBOARD_INDENT_Y_400 20 - - -const int kPlayerMenuWidth = 200; - -void IN_ResetMouse( void ); -extern CMenuPanel *CMessageWindowPanel_Create( const char *szMOTD, const char *szTitle, int iShadeFullscreen, int iRemoveMe, int x, int y, int wide, int tall ); -extern float * GetClientColor( int clientIndex ); - -using namespace vgui; - - -class PlayerButton : public CommandButton -{ - -public: - - PlayerButton(int inPlayerNumber, const char* text,int x,int y,int wide,int tall, bool bNoHighlight, bool bFlat ) - : CommandButton(text, x, y, wide, tall, bNoHighlight, bFlat) - { - mPlayerNumber = inPlayerNumber; - } - - void paint() - { - - // Set the color of the button based on the team color (or red if the player is dead). - - int theTeamNumber = g_PlayerExtraInfo[mPlayerNumber].teamnumber % iNumberOfTeamColors; - int r, g, b; - - switch (g_PlayerExtraInfo[mPlayerNumber].playerclass) - { - - case PLAYERCLASS_DEAD_MARINE: - case PLAYERCLASS_DEAD_ALIEN: - case PLAYERCLASS_REINFORCING: - r = 255 / gHUD.GetGammaSlope(); - g = 0 / gHUD.GetGammaSlope(); - b = 0 / gHUD.GetGammaSlope(); - break; - - default: - r = kTeamColors[theTeamNumber][0] / gHUD.GetGammaSlope(); - g = kTeamColors[theTeamNumber][1] / gHUD.GetGammaSlope(); - b = kTeamColors[theTeamNumber][2] / gHUD.GetGammaSlope(); - break; - - } - - setFgColor(r, g, b, 0); - - Button::paint(); - - } - - void paintBackground() - { - if ( isArmed() ) - { - // Orange Border - drawSetColor(255, 255, 255, 0); - drawOutlinedRect(0,0,_size[0],_size[1]); - } - } - -private: - - int mPlayerNumber; - -}; - - -// Used for Class specific buttons -char *sTFClasses[] = -{ - "", - "SCOUT", - "SNIPER", - "SOLDIER", - "DEMOMAN", - "MEDIC", - "HWGUY", - "PYRO", - "SPY", - "ENGINEER", - "CIVILIAN", -}; - -char *sLocalisedClasses[] = -{ - "#Civilian", - "#Scout", - "#Sniper", - "#Soldier", - "#Demoman", - "#Medic", - "#HWGuy", - "#Pyro", - "#Spy", - "#Engineer", - "#Random", - "#Civilian", -}; - -char *sTFClassSelection[] = -{ - "civilian", - "scout", - "sniper", - "soldier", - "demoman", - "medic", - "hwguy", - "pyro", - "spy", - "engineer", - "randompc", - "civilian", -}; - -const int kNumOptionsButtons = 4; - -char* kOptionsButtons[kNumOptionsButtons*2] = -{ - "#Menu_Marine1Team", "jointeamone", - "#Menu_Alien1Team", "jointeamtwo", - "#Menu_ReadyRoom", "readyroom", - "#Menu_LeaveGame", "escape", -}; - -int iBuildingCosts[] = -{ - BUILD_COST_DISPENSER, - BUILD_COST_SENTRYGUN -}; - -// This maps class numbers to the Invalid Class bit. -// This is needed for backwards compatability in maps that were finished before -// all the classes were in TF. Hence the wacky sequence. -int sTFValidClassInts[] = -{ - 0, - TF_ILL_SCOUT, - TF_ILL_SNIPER, - TF_ILL_SOLDIER, - TF_ILL_DEMOMAN, - TF_ILL_MEDIC, - TF_ILL_HVYWEP, - TF_ILL_PYRO, - TF_ILL_SPY, - TF_ILL_ENGINEER, - TF_ILL_RANDOMPC, -}; - -// Get the name of TGA file, based on GameDir -char* GetVGUITGAName(const char *pszName) -{ - int i; - char sz[256]; - static char gd[256]; - const char *gamedir; - - if (ScreenWidth() < 640) - i = 320; - else - i = 640; - sprintf(sz, pszName, i); - - gamedir = gEngfuncs.pfnGetGameDirectory(); - sprintf(gd, "%s/gfx/vgui/%s.tga",gamedir,sz); - - return gd; -} - -//================================================================ -// COMMAND MENU -//================================================================ -void CCommandMenu::AddButton( CommandButton *pButton ) -{ - if (m_iButtons >= MAX_BUTTONS) - return; - - m_aButtons[m_iButtons] = pButton; - m_iButtons++; - pButton->setParent( this ); - pButton->setFont( Scheme::sf_primary3 ); - - // give the button a default key binding - if ( m_iButtons < 10 ) - { - pButton->setBoundKey( m_iButtons + '0' ); - } - else if ( m_iButtons == 10 ) - { - pButton->setBoundKey( '0' ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Tries to find a button that has a key bound to the input, and -// presses the button if found -// Input : keyNum - the character number of the input key -// Output : Returns true if the command menu should close, false otherwise -//----------------------------------------------------------------------------- -bool CCommandMenu::KeyInput( int keyNum ) -{ - // loop through all our buttons looking for one bound to keyNum - for ( int i = 0; i < m_iButtons; i++ ) - { - if ( !m_aButtons[i]->IsNotValid() ) - { - if ( m_aButtons[i]->getBoundKey() == keyNum ) - { - // hit the button - if ( m_aButtons[i]->GetSubMenu() ) - { - // open the sub menu - gViewPort->SetCurrentCommandMenu( m_aButtons[i]->GetSubMenu() ); - return false; - } - else - { - // run the bound command - m_aButtons[i]->fireActionSignal(); - return true; - } - } - } - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: clears the current menus buttons of any armed (highlighted) -// state, and all their sub buttons -//----------------------------------------------------------------------------- -void CCommandMenu::ClearButtonsOfArmedState( void ) -{ - for ( int i = 0; i < GetNumButtons(); i++ ) - { - m_aButtons[i]->setArmed( false ); - - if ( m_aButtons[i]->GetSubMenu() ) - { - m_aButtons[i]->GetSubMenu()->ClearButtonsOfArmedState(); - } - } -} - -void CCommandMenu::RemoveAllButtons() -{ - - for ( int i = 0; i < GetNumButtons(); i++ ) - { - - if ( m_aButtons[i]->GetSubMenu() ) - { - m_aButtons[i]->GetSubMenu()->RemoveAllButtons(); - } - - removeChild(m_aButtons[i]); - - delete m_aButtons[i]; - m_aButtons[i] = NULL; - - } - - m_iButtons = 0; - -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pSubMenu - -// Output : CommandButton -//----------------------------------------------------------------------------- -CommandButton *CCommandMenu::FindButtonWithSubmenu( CCommandMenu *pSubMenu ) -{ - for ( int i = 0; i < GetNumButtons(); i++ ) - { - if ( m_aButtons[i]->GetSubMenu() == pSubMenu ) - return m_aButtons[i]; - } - - return NULL; -} - -// Recalculate the visible buttons -bool CCommandMenu::RecalculateVisibles( int iYOffset, bool bHideAll ) -{ - int i, iCurrentY = 0; - int iVisibleButtons = 0; - - // Cycle through all the buttons in this menu, and see which will be visible - for (i = 0; i < m_iButtons; i++) - { - int iClass = m_aButtons[i]->GetPlayerClass(); - if ( (iClass && iClass != g_iPlayerClass ) || ( m_aButtons[i]->IsNotValid() ) || bHideAll ) - { - m_aButtons[i]->setVisible( false ); - if ( m_aButtons[i]->GetSubMenu() != NULL ) - { - (m_aButtons[i]->GetSubMenu())->RecalculateVisibles( 0, true ); - } - } - else - { - // If it's got a submenu, force it to check visibilities - if ( m_aButtons[i]->GetSubMenu() != NULL ) - { - if ( !(m_aButtons[i]->GetSubMenu())->RecalculateVisibles( 0 , false ) ) - { - // The submenu had no visible buttons, so don't display this button - m_aButtons[i]->setVisible( false ); - continue; - } - } - - m_aButtons[i]->setVisible( true ); - iVisibleButtons++; - } - } - - // Set Size - setSize( _size[0], (iVisibleButtons * (m_flButtonSizeY-1)) + 1 ); - - if ( iYOffset ) - { - m_iYOffset = iYOffset; - } - - for (i = 0; i < m_iButtons; i++) - { - if ( m_aButtons[i]->isVisible() ) - { - if ( m_aButtons[i]->GetSubMenu() != NULL ) - (m_aButtons[i]->GetSubMenu())->RecalculateVisibles( iCurrentY + m_iYOffset, false ); - - - // Make sure it's at the right Y position - // m_aButtons[i]->getPos( iXPos, iYPos ); - - if ( m_iDirection ) - { - m_aButtons[i]->setPos( 0, (iVisibleButtons-1) * (m_flButtonSizeY-1) - iCurrentY ); - } - else - { - m_aButtons[i]->setPos( 0, iCurrentY ); - } - - iCurrentY += (m_flButtonSizeY-1); - } - } - - return iVisibleButtons?true:false; -} - -// Make sure all submenus can fit on the screen -void CCommandMenu::RecalculatePositions( int iYOffset ) -{ - int iTop; - int iAdjust = 0; - - m_iYOffset+= iYOffset; - if ( m_iDirection ) - iTop = ScreenHeight() - (m_iYOffset + _size[1] ); - else - iTop = m_iYOffset; - - if ( iTop < 0 ) - iTop = 0; - // Calculate if this is going to fit onscreen, and shuffle it up if it won't - int iBottom = iTop + _size[1]; - if ( iBottom > ScreenHeight() ) - { - // Move in increments of button sizes - while (iAdjust < (iBottom - ScreenHeight())) - { - iAdjust += m_flButtonSizeY - 1; - } - iTop -= iAdjust; - - // Make sure it doesn't move off the top of the screen (the menu's too big to fit it all) - if ( iTop < 0 ) - { - iAdjust -= (0 - iTop); - iTop = 0; - } - } - - setPos( _pos[0], iTop ); - // We need to force all menus below this one to update their positions now, because they - // might have submenus riding off buttons in this menu that have just shifted. - for (int i = 0; i < m_iButtons; i++) - m_aButtons[i]->UpdateSubMenus( iAdjust ); -} - - -// Make this menu and all menus above it in the chain visible -void CCommandMenu::MakeVisible( CCommandMenu *pChildMenu ) -{ - setVisible(true); - if (m_pParentMenu) - m_pParentMenu->MakeVisible( this ); -} - -//================================================================ -// CreateSubMenu -CCommandMenu *TeamFortressViewport::CreateSubMenu( CommandButton *pButton, CCommandMenu *pParentMenu, int iYOffset, int iXOffset ) -{ - int iXPos = 0; - int iYPos = 0; - int iWide = CMENU_SIZE_X; - int iTall = 0; - int iDirection = 0; - - if (pParentMenu) - { - iXPos = m_pCurrentCommandMenu->GetXOffset() + (CMENU_SIZE_X - 1) + iXOffset; - iYPos = m_pCurrentCommandMenu->GetYOffset() + iYOffset; - iDirection = pParentMenu->GetDirection(); - } - - CCommandMenu *pMenu = new CCommandMenu(pParentMenu, iDirection, iXPos, iYPos, iWide, iTall ); - pMenu->setParent(this); - pButton->AddSubMenu( pMenu ); - pButton->setFont( Scheme::sf_primary3 ); - pMenu->m_flButtonSizeY = m_pCurrentCommandMenu->m_flButtonSizeY; - - // Create the Submenu-open signal - InputSignal *pISignal = new CMenuHandler_PopupSubMenuInput(pButton, pMenu); - pButton->addInputSignal(pISignal); - - // Put a > to show it's a submenu - CImageLabel *pLabel = new CImageLabel( "arrow", CMENU_SIZE_X - SUBMENU_SIZE_X, SUBMENU_SIZE_Y ); - pLabel->setParent(pButton); - pLabel->addInputSignal(pISignal); - - // Reposition - pLabel->getPos( iXPos, iYPos ); - pLabel->setPos( CMENU_SIZE_X - pLabel->getImageWide(), (BUTTON_SIZE_Y - pLabel->getImageTall()) / 2 ); - - // Create the mouse off signal for the Label too - if (!pButton->m_bNoHighlight) - pLabel->addInputSignal( new CHandler_CommandButtonHighlight(pButton) ); - - return pMenu; -} - -//----------------------------------------------------------------------------- -// Purpose: Makes sure the memory allocated for TeamFortressViewport is nulled out -// Input : stAllocateBlock - -// Output : void * -//----------------------------------------------------------------------------- -void *TeamFortressViewport::operator new( size_t stAllocateBlock ) -{ -// void *mem = Panel::operator new( stAllocateBlock ); - void *mem = ::operator new( stAllocateBlock ); - memset( mem, 0, stAllocateBlock ); - return mem; -} - -//----------------------------------------------------------------------------- -// Purpose: InputSignal handler for the main viewport -//----------------------------------------------------------------------------- -class CViewPortInputHandler : public InputSignal -{ -public: - bool bPressed; - - CViewPortInputHandler() - { - } - - virtual void cursorMoved(int x,int y,Panel* panel) {} - virtual void cursorEntered(Panel* panel) {} - virtual void cursorExited(Panel* panel) {} - virtual void mousePressed(MouseCode code,Panel* panel) - { - if ( code != MOUSE_LEFT ) - { - // send a message to close the command menu - // this needs to be a message, since a direct call screws the timing - gEngfuncs.pfnClientCmd( "ForceCloseCommandMenu\n" ); - } - } - virtual void mouseReleased(MouseCode code,Panel* panel) - { - } - - virtual void mouseDoublePressed(MouseCode code,Panel* panel) {} - virtual void mouseWheeled(int delta,Panel* panel) {} - virtual void keyPressed(KeyCode code,Panel* panel) {} - virtual void keyTyped(KeyCode code,Panel* panel) {} - virtual void keyReleased(KeyCode code,Panel* panel) {} - virtual void keyFocusTicked(Panel* panel) {} -}; - - -//================================================================ -TeamFortressViewport::TeamFortressViewport(int x,int y,int wide,int tall) : Panel(x,y,wide,tall), m_SchemeManager(wide,tall) -{ - gViewPort = this; - m_iInitialized = false; - m_pTeamMenu = NULL; - m_pClassMenu = NULL; - m_pScoreBoard = NULL; - mOptionsScreen = NULL; - mOptionsButtons = new CommandButton*[kNumOptionsButtons]; - - m_pSpectatorPanel = NULL; - m_pCurrentMenu = NULL; - m_pCurrentCommandMenu = NULL; - - Initialize(); - addInputSignal( new CViewPortInputHandler ); - - int r, g, b, a; - - Scheme* pScheme = App::getInstance()->getScheme(); - - // primary text color - // Get the colors - //!! two different types of scheme here, need to integrate - SchemeHandle_t hPrimaryScheme = m_SchemeManager.getSchemeHandle( "Primary Button Text" ); - { - // font - pScheme->setFont( Scheme::sf_primary1, m_SchemeManager.getFont(hPrimaryScheme) ); - - // text color - m_SchemeManager.getFgColor( hPrimaryScheme, r, g, b, a ); - pScheme->setColor(Scheme::sc_primary1, r, g, b, a ); // sc_primary1 is non-transparent orange - - // background color (transparent black) - m_SchemeManager.getBgColor( hPrimaryScheme, r, g, b, a ); - pScheme->setColor(Scheme::sc_primary3, r, g, b, a ); - - // armed foreground color - m_SchemeManager.getFgArmedColor( hPrimaryScheme, r, g, b, a ); - pScheme->setColor(Scheme::sc_secondary2, r, g, b, a ); - - // armed background color - m_SchemeManager.getBgArmedColor( hPrimaryScheme, r, g, b, a ); - pScheme->setColor(Scheme::sc_primary2, r, g, b, a ); - - //!! need to get this color from scheme file - // used for orange borders around buttons - m_SchemeManager.getBorderColor( hPrimaryScheme, r, g, b, a ); - // pScheme->setColor(Scheme::sc_secondary1, r, g, b, a ); - pScheme->setColor(Scheme::sc_secondary1, 255*0.7, 170*0.7, 0, 0); - } - - // Change the second primary font (used in the scoreboard) - SchemeHandle_t hScoreboardScheme = m_SchemeManager.getSchemeHandle( "Scoreboard Text" ); - { - pScheme->setFont(Scheme::sf_primary2, m_SchemeManager.getFont(hScoreboardScheme) ); - } - - // Change the third primary font (used in command menu) - SchemeHandle_t hCommandMenuScheme = m_SchemeManager.getSchemeHandle( "CommandMenu Text" ); - { - pScheme->setFont(Scheme::sf_primary3, m_SchemeManager.getFont(hCommandMenuScheme) ); - } - - App::getInstance()->setScheme(pScheme); - - // VGUI MENUS - CreateTeamMenu(); - CreateClassMenu(); - CreateSpectatorMenu(); - CreateScoreBoard(); - CreateOptionsMenu(); - // Init command menus - m_iNumMenus = 0; - m_iCurrentTeamNumber = m_iUser1 = m_iUser2 = m_iUser3 = 0; - - m_StandardMenu = CreateCommandMenu("commandmenu.txt", 0, CMENU_TOP, false, CMENU_SIZE_X, BUTTON_SIZE_Y, 0 ); - m_SpectatorOptionsMenu = CreateCommandMenu("spectatormenu.txt", 1, YRES(32), true, CMENU_SIZE_X, BUTTON_SIZE_Y / 2, 0 ); // above bottom bar, flat design - m_SpectatorCameraMenu = CreateCommandMenu("spectcammenu.txt", 1, YRES(32), true, XRES( 200 ), BUTTON_SIZE_Y / 2, ScreenWidth() - ( XRES ( 200 ) + 15 ) ); // above bottom bar, flat design - - CreatePlayerMenu(); - - CreateServerBrowser(); - - // tankefugl: 0000989: - // m_chatPanel = new ChatPanel(10, (ScreenHeight() * 0.75 - 30) / 2, ScreenWidth() - 20, 30); - m_chatPanel = new ChatPanel(10, ScreenHeight() * 0.57f - 30, ScreenWidth() - 20, 30); - // :tankefugl - m_chatPanel->setParent(this); - m_chatPanel->setVisible(false); - -} - -//----------------------------------------------------------------------------- -// Purpose: Called everytime a new level is started. Viewport clears out it's data. -//----------------------------------------------------------------------------- -void TeamFortressViewport::Initialize( void ) -{ - // Force each menu to Initialize - if (m_pTeamMenu) - { - m_pTeamMenu->Initialize(); - } - if (m_pClassMenu) - { - m_pClassMenu->Initialize(); - } - if (m_pScoreBoard) - { - m_pScoreBoard->Initialize(); - HideScoreBoard(); - } - if (m_pSpectatorPanel) - { - // Spectator menu doesn't need initializing - m_pSpectatorPanel->setVisible( false ); - } - - // Make sure all menus are hidden - HideVGUIMenu(); - HideCommandMenu(); - - // Clear out some data - m_iGotAllMOTD = true; - m_iRandomPC = false; - m_flScoreBoardLastUpdated = 0; - m_flSpectatorPanelLastUpdated = 0; - - // reset player info - g_iPlayerClass = 0; - g_iTeamNumber = 0; - - memset(this->m_sMapName, 0, MAX_MAPNAME_LENGTH); - memset(this->m_szServerName, 0, MAX_SERVERNAME_LENGTH); - for (int i = 0; i < 5; i++) - { - m_iValidClasses[i] = 0; - strcpy(m_sTeamNames[i], ""); - } - - App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(Scheme::SchemeCursor::scu_none) ); -} - -class CException; -//----------------------------------------------------------------------------- -// Purpose: Read the Command Menu structure from the txt file and create the menu. -// Returns Index of menu in m_pCommandMenus -//----------------------------------------------------------------------------- -int TeamFortressViewport::CreateCommandMenu( char * menuFile, int direction, int yOffset, bool flatDesign, float flButtonSizeX, float flButtonSizeY, int xOffset ) -{ - // COMMAND MENU - // Create the root of this new Command Menu - - int newIndex = m_iNumMenus; - - m_pCommandMenus[newIndex] = new CCommandMenu(NULL, direction, xOffset, yOffset, flButtonSizeX, 300); // This will be resized once we know how many items are in it - m_pCommandMenus[newIndex]->setParent(this); - m_pCommandMenus[newIndex]->setVisible(false); - m_pCommandMenus[newIndex]->m_flButtonSizeY = flButtonSizeY; - m_pCommandMenus[newIndex]->m_iSpectCmdMenu = direction; - - m_iNumMenus++; - - // Read Command Menu from the txt file - char token[1024]; - char *pfile = (char*)gEngfuncs.COM_LoadFile( menuFile, 5, NULL); - if (!pfile) - { - gEngfuncs.Con_DPrintf( "Unable to open %s\n", menuFile); - SetCurrentCommandMenu( NULL ); - return newIndex; - } - -try -{ - // First, read in the localisation strings - - // Detpack strings - gHUD.m_TextMessage.LocaliseTextString( "#DetpackSet_For5Seconds", m_sDetpackStrings[0], MAX_BUTTON_SIZE ); - gHUD.m_TextMessage.LocaliseTextString( "#DetpackSet_For20Seconds", m_sDetpackStrings[1], MAX_BUTTON_SIZE ); - gHUD.m_TextMessage.LocaliseTextString( "#DetpackSet_For50Seconds", m_sDetpackStrings[2], MAX_BUTTON_SIZE ); - - // Now start parsing the menu structure - m_pCurrentCommandMenu = m_pCommandMenus[newIndex]; - char szLastButtonText[32] = "file start"; - pfile = gEngfuncs.COM_ParseFile(pfile, token); - while ( ( strlen ( token ) > 0 ) && ( m_iNumMenus < MAX_MENUS ) ) - { - // Keep looping until we hit the end of this menu - while ( token[0] != '}' && ( strlen( token ) > 0 ) ) - { - char cText[32] = ""; - char cBoundKey[32] = ""; - char cCustom[32] = ""; - static const int cCommandLength = 128; - char cCommand[cCommandLength] = ""; - char szMap[MAX_MAPNAME] = ""; - int iPlayerClass = 0; - int iCustom = false; - int iTeamOnly = -1; - int iToggle = 0; - int iButtonY; - bool bGetExtraToken = true; - CommandButton *pButton = NULL; - - // We should never be here without a Command Menu - if (!m_pCurrentCommandMenu) - { - gEngfuncs.Con_Printf("Error in %s file after '%s'.\n",menuFile, szLastButtonText ); - m_iInitialized = false; - return newIndex; - } - - // token should already be the bound key, or the custom name - strncpy( cCustom, token, 32 ); - cCustom[31] = '\0'; - - // See if it's a custom button - if (!strcmp(cCustom, "CUSTOM") ) - { - iCustom = true; - - // Get the next token - pfile = gEngfuncs.COM_ParseFile(pfile, token); - } - // See if it's a map - else if (!strcmp(cCustom, "MAP") ) - { - // Get the mapname - pfile = gEngfuncs.COM_ParseFile(pfile, token); - strncpy( szMap, token, MAX_MAPNAME ); - szMap[MAX_MAPNAME-1] = '\0'; - - // Get the next token - pfile = gEngfuncs.COM_ParseFile(pfile, token); - } - else if ( !strncmp(cCustom, "TEAM", 4) ) // TEAM1, TEAM2, TEAM3, TEAM4 - { - // make it a team only button - iTeamOnly = atoi( cCustom + 4 ); - - // Get the next token - pfile = gEngfuncs.COM_ParseFile(pfile, token); - } - else if ( !strncmp(cCustom, "TOGGLE", 6) ) - { - iToggle = true; - // Get the next token - pfile = gEngfuncs.COM_ParseFile(pfile, token); - } - else - { - // See if it's a Class - for (int i = 1; i <= PC_ENGINEER; i++) - { - if ( !strcmp(token, sTFClasses[i]) ) - { - // Save it off - iPlayerClass = i; - - // Get the button text - pfile = gEngfuncs.COM_ParseFile(pfile, token); - break; - } - } - } - - // Get the button bound key - strncpy( cBoundKey, token, 32 ); - cText[31] = '\0'; - - // Get the button text - pfile = gEngfuncs.COM_ParseFile(pfile, token); - strncpy( cText, token, 32 ); - cText[31] = '\0'; - - // save off the last button text we've come across (for error reporting) - strcpy( szLastButtonText, cText ); - - // Get the button command - pfile = gEngfuncs.COM_ParseFile(pfile, token); - strncpy( cCommand, token, cCommandLength ); - cCommand[cCommandLength - 1] = '\0'; - - iButtonY = (BUTTON_SIZE_Y-1) * m_pCurrentCommandMenu->GetNumButtons(); - // Custom button handling - if ( iCustom ) - { - pButton = CreateCustomButton( cText, cCommand, iButtonY ); - - // Get the next token to see if we're a menu - pfile = gEngfuncs.COM_ParseFile(pfile, token); - - if ( token[0] == '{' ) - { - strcpy( cCommand, token ); - } - else - { - bGetExtraToken = false; - } - } - else if ( szMap[0] != '\0' ) - { - // create a map button - pButton = new MapButton(szMap, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY ); - } - else if ( iTeamOnly != -1) - { - // button that only shows up if the player is on team iTeamOnly - pButton = new TeamOnlyCommandButton( iTeamOnly, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY, flatDesign ); - } - else if ( iToggle && direction == 0 ) - { - pButton = new ToggleCommandButton( cCommand, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY, flatDesign ); - } - else if ( direction == 1 ) - { - if ( iToggle ) - pButton = new SpectToggleButton( cCommand, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY, flatDesign ); - else - pButton = new SpectButton( iPlayerClass, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY ); - } - else - { - // normal button - pButton = new CommandButton( iPlayerClass, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY, flatDesign ); - } - - // add the button into the command menu - if ( pButton ) - { - m_pCurrentCommandMenu->AddButton( pButton ); - pButton->setBoundKey( cBoundKey[0] ); - pButton->setParentMenu( m_pCurrentCommandMenu ); - - // Override font in CommandMenu - pButton->setFont( Scheme::sf_primary3 ); - } - - // Find out if it's a submenu or a button we're dealing with - if ( cCommand[0] == '{' ) - { - if ( m_iNumMenus >= MAX_MENUS ) - { - gEngfuncs.Con_Printf( "Too many menus in %s past '%s'\n",menuFile, szLastButtonText ); - } - else - { - // Create the menu - m_pCommandMenus[m_iNumMenus] = CreateSubMenu(pButton, m_pCurrentCommandMenu, iButtonY ); - m_pCurrentCommandMenu = m_pCommandMenus[m_iNumMenus]; - m_iNumMenus++; - } - } - else if ( !iCustom ) - { - // Create the button and attach it to the current menu - if ( iToggle ) - pButton->addActionSignal(new CMenuHandler_ToggleCvar(cCommand)); - else - pButton->addActionSignal(new CMenuHandler_StringCommand(cCommand)); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - - // Get the next token - if ( bGetExtraToken ) - { - pfile = gEngfuncs.COM_ParseFile(pfile, token); - } - } - - // Move back up a menu - m_pCurrentCommandMenu = m_pCurrentCommandMenu->GetParentMenu(); - - pfile = gEngfuncs.COM_ParseFile(pfile, token); - } -} -catch( CException *e ) -{ - e; - //e->Delete(); - e = NULL; - m_iInitialized = false; - return newIndex; -} - - SetCurrentMenu( NULL ); - SetCurrentCommandMenu( NULL ); - gEngfuncs.COM_FreeFile( pfile ); - - m_iInitialized = true; - return newIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: Creates all the class choices under a spy's disguise menus, and -// maps a command to them -// Output : CCommandMenu -//----------------------------------------------------------------------------- -CCommandMenu *TeamFortressViewport::CreateDisguiseSubmenu( CommandButton *pButton, CCommandMenu *pParentMenu, const char *commandText, int iYOffset, int iXOffset ) -{ - // create the submenu, under which the class choices will be listed - CCommandMenu *pMenu = CreateSubMenu( pButton, pParentMenu, iYOffset, iXOffset ); - m_pCommandMenus[m_iNumMenus] = pMenu; - m_iNumMenus++; - - // create the class choice buttons - for ( int i = PC_SCOUT; i <= PC_ENGINEER; i++ ) - { - CommandButton *pDisguiseButton = new CommandButton( CHudTextMessage::BufferedLocaliseTextString( sLocalisedClasses[i] ), 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y ); - - char sz[256]; - sprintf(sz, "%s %d", commandText, i ); - pDisguiseButton->addActionSignal(new CMenuHandler_StringCommand(sz)); - - pMenu->AddButton( pDisguiseButton ); - } - - return pMenu; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pButtonText - -// *pButtonName - -// Output : CommandButton -//----------------------------------------------------------------------------- -CommandButton *TeamFortressViewport::CreateCustomButton( char *pButtonText, char *pButtonName, int iYOffset ) -{ - CommandButton *pButton = NULL; - CCommandMenu *pMenu = NULL; - - // ChangeTeam - if ( !strcmp( pButtonName, "!CHANGETEAM" ) ) - { - // ChangeTeam Submenu - pButton = new CommandButton(pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - - // Create the submenu - pMenu = CreateSubMenu(pButton, m_pCurrentCommandMenu, iYOffset ); - m_pCommandMenus[m_iNumMenus] = pMenu; - m_iNumMenus++; - - // ChangeTeam buttons - for (int i = 0; i < 4; i++) - { - char sz[256]; - sprintf(sz, "jointeam %d", i+1); - m_pTeamButtons[i] = new TeamButton(i+1, "teamname", 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - m_pTeamButtons[i]->addActionSignal(new CMenuHandler_StringCommandWatch( sz )); - pMenu->AddButton( m_pTeamButtons[i] ); - } - - // Auto Assign button - m_pTeamButtons[4] = new TeamButton(5, gHUD.m_TextMessage.BufferedLocaliseTextString( "#Team_AutoAssign" ), 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - m_pTeamButtons[4]->addActionSignal(new CMenuHandler_StringCommand( "jointeam 5" )); - pMenu->AddButton( m_pTeamButtons[4] ); - - // Spectate button - m_pTeamButtons[5] = new SpectateButton( CHudTextMessage::BufferedLocaliseTextString( "#Menu_Spectate" ), 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y, false); - m_pTeamButtons[5]->addActionSignal(new CMenuHandler_StringCommand( "spectate" )); - pMenu->AddButton( m_pTeamButtons[5] ); - } - // ChangeClass - else if ( !strcmp( pButtonName, "!CHANGECLASS" ) ) - { - // Create the Change class menu - pButton = new ClassButton(-1, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y, false); - - // ChangeClass Submenu - pMenu = CreateSubMenu(pButton, m_pCurrentCommandMenu, iYOffset ); - m_pCommandMenus[m_iNumMenus] = pMenu; - m_iNumMenus++; - - for (int i = PC_SCOUT; i <= PC_RANDOM; i++ ) - { - char sz[256]; - - // ChangeClass buttons - CHudTextMessage::LocaliseTextString( sLocalisedClasses[i], sz, 256 ); - ClassButton *pClassButton = new ClassButton( i, sz, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y, false); - - sprintf(sz, "%s", sTFClassSelection[i]); - pClassButton->addActionSignal(new CMenuHandler_StringCommandClassSelect(sz)); - pMenu->AddButton( pClassButton ); - } - } - // Map Briefing - else if ( !strcmp( pButtonName, "!MAPBRIEFING" ) ) - { - pButton = new CommandButton(pButtonText, 0, BUTTON_SIZE_Y * m_pCurrentCommandMenu->GetNumButtons(), CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_TextWindow(MENU_MAPBRIEFING)); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - // Class Descriptions - else if ( !strcmp( pButtonName, "!CLASSDESC" ) ) - { - pButton = new ClassButton(0, pButtonText, 0, BUTTON_SIZE_Y * m_pCurrentCommandMenu->GetNumButtons(), CMENU_SIZE_X, BUTTON_SIZE_Y, false); - pButton->addActionSignal(new CMenuHandler_TextWindow(MENU_CLASSHELP)); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - else if ( !strcmp( pButtonName, "!SERVERINFO" ) ) - { - pButton = new ClassButton(0, pButtonText, 0, BUTTON_SIZE_Y * m_pCurrentCommandMenu->GetNumButtons(), CMENU_SIZE_X, BUTTON_SIZE_Y, false); - pButton->addActionSignal(new CMenuHandler_TextWindow(MENU_INTRO)); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - // Spy abilities - else if ( !strcmp( pButtonName, "!SPY" ) ) - { - pButton = new DisguiseButton( 0, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y ); - } - // Feign - else if ( !strcmp( pButtonName, "!FEIGN" ) ) - { - pButton = new FeignButton(FALSE, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand( "feign" )); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - // Feign Silently - else if ( !strcmp( pButtonName, "!FEIGNSILENT" ) ) - { - pButton = new FeignButton(FALSE, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand( "sfeign" )); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - // Stop Feigning - else if ( !strcmp( pButtonName, "!FEIGNSTOP" ) ) - { - pButton = new FeignButton(TRUE, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand( "feign" )); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - // Disguise - else if ( !strcmp( pButtonName, "!DISGUISEENEMY" ) ) - { - // Create the disguise enemy button, which active only if there are 2 teams - pButton = new DisguiseButton(DISGUISE_TEAM2, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - CreateDisguiseSubmenu( pButton, m_pCurrentCommandMenu, "disguise_enemy", iYOffset); - } - else if ( !strcmp( pButtonName, "!DISGUISEFRIENDLY" ) ) - { - // Create the disguise friendly button, which active only if there are 1 or 2 teams - pButton = new DisguiseButton(DISGUISE_TEAM1 | DISGUISE_TEAM2, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - CreateDisguiseSubmenu( pButton, m_pCurrentCommandMenu, "disguise_friendly", iYOffset ); - } - else if ( !strcmp( pButtonName, "!DISGUISE" ) ) - { - // Create the Disguise button - pButton = new DisguiseButton( DISGUISE_TEAM3 | DISGUISE_TEAM4, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - CCommandMenu *pDisguiseMenu = CreateSubMenu( pButton, m_pCurrentCommandMenu, iYOffset ); - m_pCommandMenus[m_iNumMenus] = pDisguiseMenu; - m_iNumMenus++; - - // Disguise Enemy submenu buttons - for ( int i = 1; i <= 4; i++ ) - { - // only show the 4th disguise button if we have 4 teams - m_pDisguiseButtons[i] = new DisguiseButton( ((i < 4) ? DISGUISE_TEAM3 : 0) | DISGUISE_TEAM4, "Disguise", 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - - pDisguiseMenu->AddButton( m_pDisguiseButtons[i] ); - m_pDisguiseButtons[i]->setParentMenu( pDisguiseMenu ); - - char sz[256]; - sprintf( sz, "disguise %d", i ); - CreateDisguiseSubmenu( m_pDisguiseButtons[i], pDisguiseMenu, sz, iYOffset, CMENU_SIZE_X - 1 ); - } - } - // Start setting a Detpack - else if ( !strcmp( pButtonName, "!DETPACKSTART" ) ) - { - // Detpack Submenu - pButton = new DetpackButton(2, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - - // Create the submenu - pMenu = CreateSubMenu(pButton, m_pCurrentCommandMenu, iYOffset ); - m_pCommandMenus[m_iNumMenus] = pMenu; - m_iNumMenus++; - - // Set detpack buttons - CommandButton *pDetButton; - pDetButton = new CommandButton(m_sDetpackStrings[0], 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - pDetButton->addActionSignal(new CMenuHandler_StringCommand("detstart 5")); - pMenu->AddButton( pDetButton ); - pDetButton = new CommandButton(m_sDetpackStrings[1], 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - pDetButton->addActionSignal(new CMenuHandler_StringCommand("detstart 20")); - pMenu->AddButton( pDetButton ); - pDetButton = new CommandButton(m_sDetpackStrings[2], 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - pDetButton->addActionSignal(new CMenuHandler_StringCommand("detstart 50")); - pMenu->AddButton( pDetButton ); - } - // Stop setting a Detpack - else if ( !strcmp( pButtonName, "!DETPACKSTOP" ) ) - { - pButton = new DetpackButton(1, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand( "detstop" )); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - // Engineer building - else if ( !strcmp( pButtonName, "!BUILD" ) ) - { - // only appears if the player is an engineer, and either they have built something or have enough metal to build - pButton = new BuildButton( BUILDSTATE_BASE, 0, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - } - else if ( !strcmp( pButtonName, "!BUILDSENTRY" ) ) - { - pButton = new BuildButton( BUILDSTATE_CANBUILD, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("build 2")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - else if ( !strcmp( pButtonName, "!BUILDDISPENSER" ) ) - { - pButton = new BuildButton( BUILDSTATE_CANBUILD, BuildButton::DISPENSER, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("build 1")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - else if ( !strcmp( pButtonName, "!ROTATESENTRY180" ) ) - { - pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("rotatesentry180")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - else if ( !strcmp( pButtonName, "!ROTATESENTRY" ) ) - { - pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("rotatesentry")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - else if ( !strcmp( pButtonName, "!DISMANTLEDISPENSER" ) ) - { - pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::DISPENSER, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("dismantle 1")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - else if ( !strcmp( pButtonName, "!DISMANTLESENTRY" ) ) - { - pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("dismantle 2")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - else if ( !strcmp( pButtonName, "!DETONATEDISPENSER" ) ) - { - pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::DISPENSER, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("detdispenser")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - else if ( !strcmp( pButtonName, "!DETONATESENTRY" ) ) - { - pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("detsentry")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - // Stop building - else if ( !strcmp( pButtonName, "!BUILDSTOP" ) ) - { - pButton = new BuildButton( BUILDSTATE_BUILDING, 0, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); - pButton->addActionSignal(new CMenuHandler_StringCommand("build")); - // Create an input signal that'll popup the current menu - pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); - } - - return pButton; -} - -void TeamFortressViewport::ToggleServerBrowser() -{ - if (!m_iInitialized) - return; - - if ( !m_pServerBrowser ) - return; - - if ( m_pServerBrowser->isVisible() ) - { - m_pServerBrowser->setVisible( false ); - } - else - { - m_pServerBrowser->setVisible( true ); - } - - UpdateCursorState(); -} - -//======================================================================= -void TeamFortressViewport::ShowCommandMenu(int menuIndex) -{ - if (!m_iInitialized) - return; - - //Already have a menu open. - if ( m_pCurrentMenu ) - return; - - // is the command menu open? - if ( m_pCurrentCommandMenu == m_pCommandMenus[menuIndex] ) - { - HideCommandMenu(); - return; - } - - // Not visible while in intermission - if ( gHUD.m_iIntermission ) - return; - - // Recalculate visible menus - UpdateCommandMenu( menuIndex ); - HideVGUIMenu(); - - SetCurrentCommandMenu( m_pCommandMenus[menuIndex] ); - m_flMenuOpenTime = gHUD.m_flTime; - UpdateCursorState(); - - // get command menu parameters - for ( int i = 2; i < gEngfuncs.Cmd_Argc(); i++ ) - { - const char *param = gEngfuncs.Cmd_Argv( i - 1 ); - if ( param ) - { - if ( m_pCurrentCommandMenu->KeyInput(param[0]) ) - { - // kill the menu open time, since the key input is final - HideCommandMenu(); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handles the key input of "-commandmenu" -// Input : -//----------------------------------------------------------------------------- -void TeamFortressViewport::InputSignalHideCommandMenu() -{ - if (!m_iInitialized) - return; - - // if they've just tapped the command menu key, leave it open - if ( (m_flMenuOpenTime + 0.3) > gHUD.m_flTime ) - return; - - HideCommandMenu(); -} - -//----------------------------------------------------------------------------- -// Purpose: Hides the command menu -//----------------------------------------------------------------------------- -void TeamFortressViewport::HideCommandMenu() -{ - if (!m_iInitialized) - return; - - if ( m_pCommandMenus[m_StandardMenu] ) - { - m_pCommandMenus[m_StandardMenu]->ClearButtonsOfArmedState(); - } - - if ( m_pCommandMenus[m_SpectatorOptionsMenu] ) - { - m_pCommandMenus[m_SpectatorOptionsMenu]->ClearButtonsOfArmedState(); - } - - if ( m_pCommandMenus[m_SpectatorCameraMenu] ) - { - m_pCommandMenus[m_SpectatorCameraMenu]->ClearButtonsOfArmedState(); - } - - m_flMenuOpenTime = 0.0f; - SetCurrentCommandMenu( NULL ); - UpdateCursorState(); -} - -//----------------------------------------------------------------------------- -// Purpose: Bring up the scoreboard -//----------------------------------------------------------------------------- -void TeamFortressViewport::ShowScoreBoard( void ) -{ - if (m_pScoreBoard) - { - // No Scoreboard in single-player - if ( gEngfuncs.GetMaxClients() > 1 ) - { - if(gHUD.SwitchUIMode(SCOREBOARD_MODE)) - { - m_pScoreBoard->Open(); - - // TODO: HLSDK 2.3 requires this? - //UpdateCursorState(); - - // If cursor is visible, set squelch mode automatically (used for commander) - if(gHUD.GetIsInTopDownMode()) - { - m_pScoreBoard->SetSquelchMode(true); - } - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Returns true if the scoreboard is up -//----------------------------------------------------------------------------- -bool TeamFortressViewport::IsScoreBoardVisible( void ) -{ - if (m_pScoreBoard) - return m_pScoreBoard->isVisible(); - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Hide the scoreboard -//----------------------------------------------------------------------------- -const float kButtonWidth = .15f; -const float kButtonHeight = .08f; -const float kButtonHorizontalSpacing = .15f; -const float kButtonVerticalSpacing = .05f; -const float xInset = .05f; -const float yInset = .07f; - -void TeamFortressViewport::HideScoreBoard( void ) -{ - // Prevent removal of scoreboard during intermission - if ( gHUD.m_iIntermission ) - return; - - if (m_pScoreBoard && m_pScoreBoard->isVisible()) - { - // Hide other components - if(gHUD.SwitchUIMode(MAIN_MODE)) - { - m_pScoreBoard->setVisible(false); - - //GetClientVoiceMgr()->StopSquelchMode(); - m_pScoreBoard->SetSquelchMode(false); - - //UpdateCursorState(); - } - } -} - -void TeamFortressViewport::ShowOptionsMenu() -{ - if(this->mOptionsScreen) - { - const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_ON); - gHUD.PlayHUDSound(theSound, kHUDSoundVolume); - - this->mOptionsScreen->setVisible(true); - - for(int i = 0; i < kNumOptionsButtons; i++) - { - if(this->mOptionsButtons[i]) - { - this->mOptionsButtons[i]->setVisible(true); - } - } - - gHUD.GetManager().SetMouseVisibility(true); - } -} - -void TeamFortressViewport::HideOptionsMenu() -{ - if(this->mOptionsScreen) - { - const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_OFF); - gHUD.PlayHUDSound(theSound, kHUDSoundVolume); - - this->mOptionsScreen->setVisible(false); - for(int i = 0; i < kNumOptionsButtons; i++) - { - if(this->mOptionsButtons[i]) - { - this->mOptionsButtons[i]->setVisible(false); - } - } - - gHUD.GetManager().SetMouseVisibility(false); - - gEngfuncs.pfnSetMousePos(gEngfuncs.GetWindowCenterX(), gEngfuncs.GetWindowCenterY()); - } -} - -bool TeamFortressViewport::IsOptionsMenuVisible() -{ - bool theIsVisible = false; - - if(this->mOptionsScreen) - { - theIsVisible = this->mOptionsScreen->isVisible(); - } - - return theIsVisible; -} - -// Set the submenu of the Command Menu -void TeamFortressViewport::SetCurrentCommandMenu( CCommandMenu *pNewMenu ) -{ - for (int i = 0; i < m_iNumMenus; i++) - m_pCommandMenus[i]->setVisible(false); - - m_pCurrentCommandMenu = pNewMenu; - - if (m_pCurrentCommandMenu) - m_pCurrentCommandMenu->MakeVisible( NULL ); -} - -void TeamFortressViewport::UpdateCommandMenu(int menuIndex) -{ - m_pCommandMenus[menuIndex]->RecalculateVisibles( 0, false ); - m_pCommandMenus[menuIndex]->RecalculatePositions( 0 ); -} - -void COM_FileBase ( const char *in, char *out); - -void TeamFortressViewport::UpdateSpectatorPanel() -{ - m_iUser1 = g_iUser1; - m_iUser2 = g_iUser2; - m_iUser3 = g_iUser3; - - if (!m_pSpectatorPanel) - return; - - if ( g_iUser1 && gHUD.m_pCvarDraw->value && !gHUD.m_iIntermission) // don't draw in dev_overview mode - { - char bottomText[128]; - char helpString2[128]; - char * name; - int player = 0; - - // check if spectator combinations are still valid - gHUD.m_Spectator.CheckSettings(); - - if ( !m_pSpectatorPanel->isVisible() ) - { - m_pSpectatorPanel->setVisible( true ); // show spectator panel, but - m_pSpectatorPanel->ShowMenu( true ); - - // Removed by mmcguire. - // This text isn't applicable anymore. - - /* - char tempString[128]; - - _snprintf( tempString, sizeof( tempString ) - 1, "%c%s", HUD_PRINTCENTER, CHudTextMessage::BufferedLocaliseTextString( "#Spec_Duck" ) ); - tempString[ sizeof( tempString ) - 1 ] = '\0'; - - gHUD.m_TextMessage.MsgFunc_TextMsg( NULL, strlen( tempString ) + 1, tempString ); - */ - } - - sprintf(bottomText,"#Spec_Mode%d", g_iUser1 ); - sprintf(helpString2,"#Spec_Mode%d", g_iUser1 ); - - if ( gEngfuncs.IsSpectateOnly() ) - strcat(helpString2, " - HLTV"); - - // check if we're locked onto a target, show the player's name - if ( (g_iUser2 > 0) && (g_iUser2 <= gEngfuncs.GetMaxClients()) && (g_iUser1 != OBS_ROAMING) ) - { - player = g_iUser2; - } - - name = g_PlayerInfoList[player].name; - - // create player & health string - if ( player && name ) - { - strcpy( bottomText, name ); - } -/* - // in first person mode colorize player names - if ( (g_iUser1 == OBS_IN_EYE) && player ) - { - float * color = GetClientColor( player ); - int r = color[0]*255; - int g = color[1]*255; - int b = color[2]*255; - - // set team color, a bit transparent - m_pSpectatorPanel->m_BottomMainLabel->setFgColor(r,g,b,0); - } - else - { // restore GUI color - m_pSpectatorPanel->m_BottomMainLabel->setFgColor( 143, 143, 54, 0 ); - } -*/ - // add sting auto if we are in auto directed mode - if ( CVAR_GET_FLOAT("spec_autodirector") != 0 ) - { - char tempString[128]; - sprintf(tempString, "#Spec_Auto %s", helpString2); - strcpy( helpString2, tempString ); - } - - m_pSpectatorPanel->m_BottomMainLabel->setText( CHudTextMessage::BufferedLocaliseTextString( bottomText ) ); - - - // update extra info field - char szText[64]; - - if ( gEngfuncs.IsSpectateOnly() ) - { - // in HLTV mode show number of spectators - _snprintf( szText, 63, "%s: %d", CHudTextMessage::BufferedLocaliseTextString( "#Spectators" ), gHUD.m_Spectator.m_iSpectatorNumber ); - } - else - { - // otherwise show map name - char szMapName[64]; - COM_FileBase( gEngfuncs.pfnGetLevelName(), szMapName ); - - _snprintf ( szText, 63, "%s: %s",CHudTextMessage::BufferedLocaliseTextString( "#Spec_Map" ), szMapName ); - } - - szText[63] = 0; - - m_pSpectatorPanel->m_ExtraInfo->setText ( szText ); - - /* - int timer = (int)( gHUD.m_roundTimer.m_flTimeEnd - gHUD.m_flTime ); - - if ( timer < 0 ) - timer = 0; - - _snprintf ( szText, 63, "%d:%02d\n", (timer / 60), (timer % 60) ); - - szText[63] = 0; - - m_pSpectatorPanel->m_CurrentTime->setText( szText ); */ - int theTimeToDisplay = gHUD.GetGameTime(); - _snprintf ( szText, 63, "%d:%02d\n", (theTimeToDisplay / 60), (theTimeToDisplay % 60) ); - szText[63] = 0; - m_pSpectatorPanel->m_CurrentTime->setText( szText ); - - // update spectator panel - gViewPort->m_pSpectatorPanel->Update(); - } - else - { - if ( m_pSpectatorPanel->isVisible() ) - { - m_pSpectatorPanel->setVisible( false ); - } - if (m_pSpectatorPanel->m_menuVisible) - { - m_pSpectatorPanel->ShowMenu( false ); // dsiable all menus/buttons - } - - } - - m_flSpectatorPanelLastUpdated = gHUD.m_flTime + 1.0; // update every seconds -} - -//====================================================================== -void TeamFortressViewport::CreateScoreBoard( void ) -{ - int xdent = SBOARD_INDENT_X; - int ydent = SBOARD_INDENT_Y; - if (ScreenWidth() == 512) - { - xdent = SBOARD_INDENT_X_512; - ydent = SBOARD_INDENT_Y_512; - } - else if (ScreenWidth() == 400) - { - xdent = SBOARD_INDENT_X_400; - ydent = SBOARD_INDENT_Y_400; - } - - m_pScoreBoard = new ScorePanel(xdent, ydent, ScreenWidth() - (xdent * 2), ScreenHeight() - (ydent * 2)); - m_pScoreBoard->setParent(this); - m_pScoreBoard->setVisible(false); -} - -void TeamFortressViewport::CreateOptionsMenu() -{ - //this->mOptionsScreen = new ColoredPanel(0, 0, ScreenWidth, ScreenHeight); - this->mOptionsScreen = new CTransparentPanel(128, 0, 0, ScreenWidth(), ScreenHeight()); - this->mOptionsScreen->setBorder( new LineBorder( Color(255*0.7,170*0.7,0,0 )) ); - this->mOptionsScreen->setParent(this); - this->mOptionsScreen->setBgColor(128, 128, 128, 40); - this->mOptionsScreen->setVisible(false); - - int theScreenWidth = ScreenWidth(); - int theScreenHeight = ScreenHeight(); -} - -void TeamFortressViewport::CreateServerBrowser( void ) -{ - m_pServerBrowser = new ServerBrowser( 0, 0, ScreenWidth(), ScreenHeight() ); - m_pServerBrowser->setParent(this); - m_pServerBrowser->setVisible(false); -} - -void TeamFortressViewport::CreatePlayerMenu() -{ - - int newIndex = m_iNumMenus; - - m_pCommandMenus[newIndex] = new CCommandMenu(NULL, 1, XRES(390), YRES(32), XRES(kPlayerMenuWidth), YRES(300)); // This will be resized once we know how many items are in it - - m_pCommandMenus[newIndex]->setParent(this); - m_pCommandMenus[newIndex]->setVisible(false); - m_pCommandMenus[newIndex]->m_flButtonSizeY = BUTTON_SIZE_Y / 2; - m_pCommandMenus[newIndex]->m_iSpectCmdMenu = 1; - - m_iNumMenus++; - - m_SpectatorPlayerMenu = newIndex; - -} - -void TeamFortressViewport::UpdatePlayerMenu() -{ - - CCommandMenu* pMenu = m_pCommandMenus[m_SpectatorPlayerMenu]; - - int xOffset = 0; - - float flButtonSizeX = XRES(kPlayerMenuWidth); - float flButtonSizeY = BUTTON_SIZE_Y / 2; - - // Create a menu item for each of the players. - - pMenu->ClearButtonsOfArmedState(); - pMenu->RemoveAllButtons(); - - // Make sure we have player info - gViewPort->GetAllPlayersInfo(); - - for (int team = 0; team < 4; ++team) - { - - if (gHUD.GetPlayMode() != PLAYMODE_OBSERVER) - { - // Filter out players who aren't on our team. - - int theLocalPlayerTeam = 0; - - if (gEngfuncs.GetLocalPlayer()) - { - theLocalPlayerTeam = gEngfuncs.GetLocalPlayer()->curstate.team; - } - - if (theLocalPlayerTeam != team) - { - continue; - } - - } - - for (int i = 1; i <= MAX_PLAYERS; ++i) - { - - if ( g_PlayerInfoList[i].name == NULL ) - continue; // empty player slot, skip - - if ( g_PlayerExtraInfo[i].teamname[0] == 0 ) - continue; // skip over players who are not in a team - - if ( g_PlayerExtraInfo[i].teamnumber != team) - continue; - - switch(g_PlayerExtraInfo[i].playerclass) - { - case PLAYERCLASS_SPECTATOR: - case PLAYERCLASS_COMMANDER: - case PLAYERCLASS_REINFORCING: - continue; - } - - int iButtonY = (BUTTON_SIZE_Y-1) * pMenu->GetNumButtons(); - - PlayerButton* pButton = new PlayerButton(i, g_PlayerInfoList[i].name, 0, iButtonY, flButtonSizeX, flButtonSizeY, false, true); - - //pButton->setArmedColor(teamR, teamG, teamB, 0); - //pButton->setUnArmedColor(teamR, teamG, teamB, 0); - //pButton->setArmedBorderColor(255 / gHUD.GetGammaSlope(), 255 / gHUD.GetGammaSlope(), 255 / gHUD.GetGammaSlope(), 0); - - if ( pButton ) - { - pMenu->AddButton( pButton ); - pButton->setParentMenu( pMenu ); - pButton->setFont( Scheme::sf_primary3 ); - - char command[MAX_COMMAND_SIZE]; - sprintf(command, "follow %d", i); - - pButton->addActionSignal(new CMenuHandler_StringCommand(command)); - - } - - } - } - -} - -//====================================================================== -// Set the VGUI Menu -void TeamFortressViewport::SetCurrentMenu( CMenuPanel *pMenu ) -{ - m_pCurrentMenu = pMenu; - if ( m_pCurrentMenu ) - { - // Don't open menus in demo playback - if ( gEngfuncs.pDemoAPI->IsPlayingback() ) - return; - - m_pCurrentMenu->Open(); - } -} - -//================================================================ -// Text Window -CMenuPanel* TeamFortressViewport::CreateTextWindow( int iTextToShow ) -{ - char sz[256]; - char *cText; - char *pfile = NULL; - static const int MAX_TITLE_LENGTH = 32; - char cTitle[MAX_TITLE_LENGTH]; - - if ( iTextToShow == SHOW_MOTD ) - { - if (!m_szServerName || !m_szServerName[0]) - strcpy( cTitle, kAvHGameName ); - else - strncpy( cTitle, m_szServerName, MAX_TITLE_LENGTH ); - cTitle[MAX_TITLE_LENGTH-1] = 0; - cText = m_szMOTD; - } - else if ( iTextToShow == SHOW_MAPBRIEFING ) - { - // Get the current mapname, and open it's map briefing text - if (m_sMapName && m_sMapName[0]) - { - strcpy( sz, "maps/"); - strcat( sz, m_sMapName ); - strcat( sz, ".txt" ); - } - else - { - const char *level = gEngfuncs.pfnGetLevelName(); - if (!level) - return NULL; - - strcpy( sz, level ); - char *ch = strchr( sz, '.' ); - *ch = '\0'; - strcat( sz, ".txt" ); - - // pull out the map name - strcpy( m_sMapName, level ); - ch = strchr( m_sMapName, '.' ); - if ( ch ) - { - *ch = 0; - } - - ch = strchr( m_sMapName, '/' ); - if ( ch ) - { - // move the string back over the '/' - memmove( m_sMapName, ch+1, strlen(ch)+1 ); - } - } - - pfile = (char*)gEngfuncs.COM_LoadFile( sz, 5, NULL ); - - if (!pfile) - return NULL; - - cText = pfile; - - strncpy( cTitle, m_sMapName, MAX_TITLE_LENGTH ); - cTitle[MAX_TITLE_LENGTH-1] = 0; - } - else if ( iTextToShow == SHOW_CLASSDESC ) - { - switch ( g_iPlayerClass ) - { - case PC_SCOUT: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_scout" ); - CHudTextMessage::LocaliseTextString( "#Title_scout", cTitle, MAX_TITLE_LENGTH ); break; - case PC_SNIPER: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_sniper" ); - CHudTextMessage::LocaliseTextString( "#Title_sniper", cTitle, MAX_TITLE_LENGTH ); break; - case PC_SOLDIER: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_soldier" ); - CHudTextMessage::LocaliseTextString( "#Title_soldier", cTitle, MAX_TITLE_LENGTH ); break; - case PC_DEMOMAN: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_demoman" ); - CHudTextMessage::LocaliseTextString( "#Title_demoman", cTitle, MAX_TITLE_LENGTH ); break; - case PC_MEDIC: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_medic" ); - CHudTextMessage::LocaliseTextString( "#Title_medic", cTitle, MAX_TITLE_LENGTH ); break; - case PC_HVYWEAP: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_hwguy" ); - CHudTextMessage::LocaliseTextString( "#Title_hwguy", cTitle, MAX_TITLE_LENGTH ); break; - case PC_PYRO: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_pyro" ); - CHudTextMessage::LocaliseTextString( "#Title_pyro", cTitle, MAX_TITLE_LENGTH ); break; - case PC_SPY: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_spy" ); - CHudTextMessage::LocaliseTextString( "#Title_spy", cTitle, MAX_TITLE_LENGTH ); break; - case PC_ENGINEER: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_engineer" ); - CHudTextMessage::LocaliseTextString( "#Title_engineer", cTitle, MAX_TITLE_LENGTH ); break; - case PC_CIVILIAN: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_civilian" ); - CHudTextMessage::LocaliseTextString( "#Title_civilian", cTitle, MAX_TITLE_LENGTH ); break; - default: - return NULL; - } - - if ( g_iPlayerClass == PC_CIVILIAN ) - { - sprintf(sz, "classes/long_civilian.txt"); - } - else - { - sprintf(sz, "classes/long_%s.txt", sTFClassSelection[ g_iPlayerClass ]); - } - char *pfile = (char*)gEngfuncs.COM_LoadFile( sz, 5, NULL ); - if (pfile) - { - cText = pfile; - } - } - else if ( iTextToShow == SHOW_SPECHELP ) - { - CHudTextMessage::LocaliseTextString( "#Spec_Help_Title", cTitle, MAX_TITLE_LENGTH ); - cTitle[MAX_TITLE_LENGTH-1] = 0; - - char *pfile = CHudTextMessage::BufferedLocaliseTextString( "#Spec_Help_Text" ); - if ( pfile ) - { - cText = pfile; - } - } - - // if we're in the game (ie. have selected a class), flag the menu to be only grayed in the dialog box, instead of full screen - CMenuPanel *pMOTDPanel = CMessageWindowPanel_Create( cText, cTitle, g_iPlayerClass == PC_UNDEFINED, false, 0, 0, ScreenWidth(), ScreenHeight() ); - pMOTDPanel->setParent( this ); - - if ( pfile ) - gEngfuncs.COM_FreeFile( pfile ); - - return pMOTDPanel; -} - -char* TeamFortressViewport::GetTeamName( int iTeam ) -{ - return m_sTeamNames[iTeam]; -} - -//================================================================ -// VGUI Menus -void TeamFortressViewport::ShowVGUIMenu( int iMenu ) -{ - CMenuPanel *pNewMenu = NULL; - - gHUD.SetUsingVGUI(true); - - // Don't open menus in demo playback - if ( gEngfuncs.pDemoAPI->IsPlayingback() ) - return; - - // Don't open any menus except the MOTD during intermission - // MOTD needs to be accepted because it's sent down to the client - // after map change, before intermission's turned off - if ( gHUD.m_iIntermission && iMenu != MENU_INTRO ) - return; - - // Don't create one if it's already in the list - if (m_pCurrentMenu) - { - CMenuPanel *pMenu = m_pCurrentMenu; - while (pMenu != NULL) - { - if (pMenu->GetMenuID() == iMenu) - return; - pMenu = pMenu->GetNextMenu(); - } - } - - switch ( iMenu ) - { - case MENU_TEAM: - pNewMenu = ShowTeamMenu(); - break; - - // Map Briefing removed now that it appears in the team menu - case MENU_MAPBRIEFING: - pNewMenu = CreateTextWindow( SHOW_MAPBRIEFING ); - break; - - case MENU_INTRO: - pNewMenu = CreateTextWindow( SHOW_MOTD ); - break; - - case MENU_CLASSHELP: - pNewMenu = CreateTextWindow( SHOW_CLASSDESC ); - break; - - case MENU_SPECHELP: - pNewMenu = CreateTextWindow( SHOW_SPECHELP ); - break; - case MENU_CLASS: - pNewMenu = ShowClassMenu(); - break; - - default: - break; - } - - if (!pNewMenu) - return; - - // Close the Command Menu if it's open - HideCommandMenu(); - - pNewMenu->SetMenuID( iMenu ); - pNewMenu->SetActive( true ); - pNewMenu->setParent(this); - - // See if another menu is visible, and if so, cache this one for display once the other one's finished - if (m_pCurrentMenu) - { - if ( m_pCurrentMenu->GetMenuID() == MENU_CLASS && iMenu == MENU_TEAM ) - { - CMenuPanel *temp = m_pCurrentMenu; - m_pCurrentMenu->Close(); - m_pCurrentMenu = pNewMenu; - m_pCurrentMenu->SetNextMenu( temp ); - m_pCurrentMenu->Open(); - UpdateCursorState(); - } - else - { - m_pCurrentMenu->SetNextMenu( pNewMenu ); - } - } - else - { - m_pCurrentMenu = pNewMenu; - m_pCurrentMenu->Open(); - UpdateCursorState(); - } -} - -// Removes all VGUI Menu's onscreen -void TeamFortressViewport::HideVGUIMenu() -{ - while (m_pCurrentMenu) - { - HideTopMenu(); - } - gHUD.SetUsingVGUI(false); -} - -// Remove the top VGUI menu, and bring up the next one -void TeamFortressViewport::HideTopMenu() -{ - if (m_pCurrentMenu) - { - // Close the top one - m_pCurrentMenu->Close(); - - // Bring up the next one - gViewPort->SetCurrentMenu( m_pCurrentMenu->GetNextMenu() ); - } - - UpdateCursorState(); -} - -// Return TRUE if the HUD's allowed to print text messages -bool TeamFortressViewport::AllowedToPrintText( void ) -{ - // Prevent text messages when fullscreen menus are up - if ( m_pCurrentMenu && g_iPlayerClass == 0 ) - { - int iId = m_pCurrentMenu->GetMenuID(); - if ( iId == MENU_TEAM || iId == MENU_CLASS || iId == MENU_INTRO || iId == MENU_CLASSHELP ) - return FALSE; - } - - return TRUE; -} - -//====================================================================================== -// TEAM MENU -//====================================================================================== -// Bring up the Team selection Menu -CMenuPanel* TeamFortressViewport::ShowTeamMenu() -{ - // Don't open menus in demo playback - if ( gEngfuncs.pDemoAPI->IsPlayingback() ) - return NULL; - - m_pTeamMenu->Reset(); - return m_pTeamMenu; -} - -void TeamFortressViewport::CreateTeamMenu() -{ - // Create the panel - m_pTeamMenu = new CTeamMenuPanel(100, false, 0, 0, ScreenWidth(), ScreenHeight()); - m_pTeamMenu->setParent( this ); - m_pTeamMenu->setVisible( false ); -} - -//====================================================================================== -// CLASS MENU -//====================================================================================== -// Bring up the Class selection Menu -CMenuPanel* TeamFortressViewport::ShowClassMenu() -{ - // Don't open menus in demo playback - if ( gEngfuncs.pDemoAPI->IsPlayingback() ) - return NULL; - - m_pClassMenu->Reset(); - return m_pClassMenu; -} - -void TeamFortressViewport::CreateClassMenu() -{ - // Create the panel - m_pClassMenu = new CClassMenuPanel(100, false, 0, 0, ScreenWidth(), ScreenHeight()); - m_pClassMenu->setParent(this); - m_pClassMenu->setVisible( false ); -} - -//====================================================================================== -//====================================================================================== -// SPECTATOR MENU -//====================================================================================== -// Spectator "Menu" explaining the Spectator buttons -void TeamFortressViewport::CreateSpectatorMenu() -{ - // Create the Panel - m_pSpectatorPanel = new SpectatorPanel(0, 0, ScreenWidth(), ScreenHeight()); - m_pSpectatorPanel->setParent(this); - m_pSpectatorPanel->setVisible(false); - m_pSpectatorPanel->Initialize(); -} - -//====================================================================================== -// UPDATE HUD SECTIONS -//====================================================================================== -// We've got an update on player info -// Recalculate any menus that use it. -void TeamFortressViewport::UpdateOnPlayerInfo() -{ - if (m_pTeamMenu) - m_pTeamMenu->Update(); - if (m_pClassMenu) - m_pClassMenu->Update(); - if (m_pScoreBoard) - m_pScoreBoard->Update(); -} - -void TeamFortressViewport::UpdateCursorState() -{ - - if (gHUD.GetInTopDownMode() || m_pSpectatorPanel->isVisible() || GetClientVoiceMgr()->IsInSquelchMode()) - { - gHUD.GetManager().SetMouseVisibility(true); - } - else - { - - // Don't reset mouse in demo playback - if ( !gEngfuncs.pDemoAPI->IsPlayingback() ) - { - IN_ResetMouse(); - } - - gHUD.GetManager().SetMouseVisibility(false); - - } - - /* - if(!gHUD.GetInTopDownMode()) - { - // Need cursor if any VGUI window is up - if ( m_pSpectatorPanel->m_menuVisible || m_pCurrentMenu || m_pTeamMenu->isVisible() || m_pServerBrowser->isVisible() || GetClientVoiceMgr()->IsInSquelchMode() ) - { - gHUD.GetManager().SetMouseVisibility(true); - //g_iVisibleMouse = true; - //App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(Scheme::SchemeCursor::scu_arrow) ); - return; - } - else if ( m_pCurrentCommandMenu ) - { - // commandmenu doesn't have cursor if hud_capturemouse is turned off - if ( gHUD.m_pCvarStealMouse->value != 0.0f ) - { - gHUD.GetManager().SetMouseVisibility(true); - //g_iVisibleMouse = true; - //App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(Scheme::SchemeCursor::scu_arrow) ); - return; - } - } - - // Don't reset mouse in demo playback - if ( !gEngfuncs.pDemoAPI->IsPlayingback() ) - { - IN_ResetMouse(); - } - - gHUD.GetManager().SetMouseVisibility(false); - //g_iVisibleMouse = false; - //App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(Scheme::SchemeCursor::scu_none) ); - } - */ - -} - -void TeamFortressViewport::UpdateHighlights() -{ - if (m_pCurrentCommandMenu) - m_pCurrentCommandMenu->MakeVisible( NULL ); -} - -void TeamFortressViewport::GetAllPlayersInfo( void ) -{ - for ( int i = 1; i <= MAX_PLAYERS; i++ ) - { - GetPlayerInfo( i, &g_PlayerInfoList[i] ); - - if ( g_PlayerInfoList[i].thisplayer ) - m_pScoreBoard->m_iPlayerNum = i; // !!!HACK: this should be initialized elsewhere... maybe gotten from the engine - } -} - -void TeamFortressViewport::paintBackground() -{ - if (m_pScoreBoard) - { - int x, y; - getApp()->getCursorPos(x, y); - m_pScoreBoard->cursorMoved(x, y, m_pScoreBoard); - } - - // See if the command menu is visible and needs recalculating due to some external change - if ( g_iTeamNumber != m_iCurrentTeamNumber ) - { - UpdateCommandMenu( m_StandardMenu ); - - if ( m_pClassMenu ) - { - m_pClassMenu->Update(); - } - - m_iCurrentTeamNumber = g_iTeamNumber; - } - - if ( g_iPlayerClass != m_iCurrentPlayerClass ) - { - UpdateCommandMenu( m_StandardMenu ); - - m_iCurrentPlayerClass = g_iPlayerClass; - } - - // See if the Spectator Menu needs to be update - if ( ( g_iUser1 != m_iUser1 || g_iUser2 != m_iUser2 ) || - ( m_flSpectatorPanelLastUpdated < gHUD.m_flTime ) ) - { - UpdateSpectatorPanel(); - } - - // Update the Scoreboard, if it's visible - if ( m_pScoreBoard->isVisible() && (m_flScoreBoardLastUpdated < gHUD.m_flTime) ) - { - m_pScoreBoard->Update(); - m_flScoreBoardLastUpdated = gHUD.m_flTime + 0.5; - } - - int extents[4]; - getAbsExtents(extents[0],extents[1],extents[2],extents[3]); - VGui_ViewportPaintBackground(extents); -} - -//================================================================ -// Input Handler for Drag N Drop panels -void CDragNDropHandler::cursorMoved(int x,int y,Panel* panel) -{ - if(m_bDragging) - { - App::getInstance()->getCursorPos(x,y); - m_pPanel->setPos(m_iaDragOrgPos[0]+(x-m_iaDragStart[0]),m_iaDragOrgPos[1]+(y-m_iaDragStart[1])); - - if(m_pPanel->getParent()!=null) - { - m_pPanel->getParent()->repaint(); - } - } -} - -void CDragNDropHandler::mousePressed(MouseCode code,Panel* panel) -{ - int x,y; - App::getInstance()->getCursorPos(x,y); - m_bDragging=true; - m_iaDragStart[0]=x; - m_iaDragStart[1]=y; - m_pPanel->getPos(m_iaDragOrgPos[0],m_iaDragOrgPos[1]); - App::getInstance()->setMouseCapture(panel); - - m_pPanel->setDragged(m_bDragging); - m_pPanel->requestFocus(); -} - -void CDragNDropHandler::mouseReleased(MouseCode code,Panel* panel) -{ - m_bDragging=false; - m_pPanel->setDragged(m_bDragging); - App::getInstance()->setMouseCapture(null); -} - -//================================================================ -// Number Key Input -bool TeamFortressViewport::SlotInput( int iSlot ) -{ - // If there's a menu up, give it the input - if ( m_pCurrentMenu ) - return m_pCurrentMenu->SlotInput( iSlot ); - - if(gHUD.SlotInput(iSlot)) - { - return TRUE; - } - - return FALSE; -} - -// Direct Key Input -int TeamFortressViewport::KeyInput( int down, int keynum, const char *pszCurrentBinding ) -{ - - if (m_chatPanel->isVisible()) - { - // Don't let the game handle the input while the user is typing in the - // chat window. - return 0; - } - - if (down && pszCurrentBinding && !strcmp(pszCurrentBinding, kcGlobalChat)) - { - m_chatPanel->setVisible(true); - m_chatPanel->requestFocus(); - m_chatPanel->SetChatMode(ChatPanel::chatModeAll); - return 0; - } - else if (down && pszCurrentBinding && !strcmp(pszCurrentBinding, kcTeamChat)) - { - m_chatPanel->setVisible(true); - m_chatPanel->requestFocus(); - m_chatPanel->SetChatMode(ChatPanel::chatModeTeam); - return 0; - } - - // Open Text Window? - if (m_pCurrentMenu && gEngfuncs.Con_IsVisible() == false) - { - int iMenuID = m_pCurrentMenu->GetMenuID(); - - // Get number keys as Input for Team/Class menus - if (iMenuID == MENU_TEAM || iMenuID == MENU_CLASS) - { - // Escape gets you out of Team/Class menus if the Cancel button is visible - if ( keynum == K_ESCAPE ) - { - if ( (iMenuID == MENU_TEAM && g_iTeamNumber) || (iMenuID == MENU_CLASS && g_iPlayerClass) ) - { - HideTopMenu(); - return 0; - } - } - - for (int i = '0'; i <= '9'; i++) - { - if ( down && (keynum == i) ) - { - SlotInput( i - '0' ); - return 0; - } - } - } - - // Grab enter keys to close TextWindows - if ( down && (keynum == K_ENTER || keynum == K_KP_ENTER || keynum == K_SPACE || keynum == K_ESCAPE) ) - { - if ( iMenuID == MENU_MAPBRIEFING || iMenuID == MENU_INTRO || iMenuID == MENU_CLASSHELP ) - { - HideTopMenu(); - return 0; - } - } - - // Grab jump key on Team Menu as autoassign - if ( pszCurrentBinding && down && !strcmp(pszCurrentBinding, "+jump") ) - { - if (iMenuID == MENU_TEAM) - { - m_pTeamMenu->SlotInput(5); - return 0; - } - } - - } - - // if we're in a command menu, try hit one of it's buttons - if ( down && m_pCurrentCommandMenu ) - { - // Escape hides the command menu - if ( keynum == K_ESCAPE ) - { - HideCommandMenu(); - return 0; - } - - // only trap the number keys - if ( keynum >= '0' && keynum <= '9' ) - { - if ( m_pCurrentCommandMenu->KeyInput(keynum) ) - { - // a final command has been issued, so close the command menu - HideCommandMenu(); - } - - return 0; - } - } - - return 1; -} - -ChatPanel* TeamFortressViewport::GetChatPanel() -{ - return m_chatPanel; -} - -//================================================================ -// Message Handlers -int TeamFortressViewport::MsgFunc_TeamNames(const char *pszName, int iSize, void *pbuf ) -{ - StringList team_names; - NetMsg_TeamNames( pbuf, iSize, team_names ); - - char team_translated[128]; - strcpy(team_translated, gHUD.m_TextMessage.LookupString(kTeamTitle)); - - char team_name_translated[MAX_TEAMNAME_SIZE]; - for( int team_number = 0; team_number < team_names.size(); team_number++ ) - { - gHUD.m_TextMessage.LocaliseTextString( team_names[team_number].c_str(), team_name_translated, MAX_TEAMNAME_SIZE ); - - team_names[team_number] = ""; - if( team_number != 0 && team_number < (m_iNumberOfTeams-1) ) //don't prepend information for spectators or team 0 - { - team_names[team_number] += team_translated; - team_names[team_number] += " "; - team_names[team_number] += MakeStringFromInt( team_number ); - team_names[team_number] += ": "; - } - team_names[team_number] += team_name_translated; - strcpy(m_sTeamNames[team_number], team_names[team_number].c_str()); - } - - return 1; -} - -int TeamFortressViewport::MsgFunc_MOTD( const char *pszName, int iSize, void *pbuf ) -{ - if (m_iGotAllMOTD) - m_szMOTD[0] = 0; - - string MOTD; - bool is_finished; - NetMsg_MOTD( pbuf, iSize, is_finished, MOTD ); - - m_iGotAllMOTD = is_finished ? 1 : 0; - - int roomInArray = (int)max( 0, sizeof(m_szMOTD) - strlen(m_szMOTD) - 1); - - strncat( m_szMOTD, MOTD.c_str(), roomInArray ); - m_szMOTD[ sizeof(m_szMOTD)-1 ] = '\0'; - - return 1; -} - -int TeamFortressViewport::MsgFunc_ServerName( const char *pszName, int iSize, void *pbuf ) -{ - string name; - NetMsg_ServerName( pbuf, iSize, name ); - - memset(this->m_szServerName, 0, MAX_SERVERNAME_LENGTH); - - strncpy( m_szServerName, name.c_str(), MAX_SERVERNAME_LENGTH ); - - return 1; -} - -int TeamFortressViewport::MsgFunc_ScoreInfo( const char *pszName, int iSize, void *pbuf ) -{ - ScoreInfo info; - NetMsg_ScoreInfo( pbuf, iSize, info ); - - if ( info.player_index > 0 && info.player_index <= MAX_PLAYERS ) - { - // Update score, but show + or - indicator on scoreboard when it changes - g_PlayerExtraInfo[info.player_index].lastScore = g_PlayerExtraInfo[info.player_index].score; - g_PlayerExtraInfo[info.player_index].score = info.score; - if(g_PlayerExtraInfo[info.player_index].score != g_PlayerExtraInfo[info.player_index].lastScore) - { - g_PlayerExtraInfo[info.player_index].timeOfLastScoreChange = gHUD.GetTimeOfLastUpdate(); - } - - // Update other info - g_PlayerExtraInfo[info.player_index].frags = info.frags; - g_PlayerExtraInfo[info.player_index].deaths = info.deaths; - g_PlayerExtraInfo[info.player_index].playerclass = info.player_class; - // puzl: 0001073 - g_PlayerExtraInfo[info.player_index].auth = info.auth; - g_PlayerExtraInfo[info.player_index].teamnumber = max( info.team, 0 ); - - // Icon is now handled through the ProfileInfo update - - UpdateOnPlayerInfo(); - } - - return 1; -} - -// Message handler for TeamScore message -// accepts three values: -// string: team name -// short: teams kills -// short: teams deaths -// if this message is never received, then scores will simply be the combined totals of the players. -int TeamFortressViewport::MsgFunc_TeamScore( const char *pszName, int iSize, void *pbuf ) -{ - string team_name; - int score, deaths; - NetMsg_TeamScore( pbuf, iSize, team_name, score, deaths ); - - // find the team matching the name - for ( int i = 1; i <= m_pScoreBoard->m_iNumTeams; i++ ) - { - if ( !stricmp( team_name.c_str(), g_TeamInfo[i].name ) ) - break; - } - - if ( i > m_pScoreBoard->m_iNumTeams ) - return 1; - - // use this new score data instead of combined player scores - g_TeamInfo[i].scores_overriden = TRUE; - g_TeamInfo[i].frags = score; - g_TeamInfo[i].deaths = deaths; - - return 1; -} - -// Message handler for TeamInfo message -// accepts two values: -// byte: client number -// string: client team name -int TeamFortressViewport::MsgFunc_TeamInfo( const char *pszName, int iSize, void *pbuf ) -{ - if (!m_pScoreBoard) - return 1; - - int player_index; - string team_id; - NetMsg_TeamInfo( pbuf, iSize, player_index, team_id ); - - if ( player_index > 0 && player_index <= MAX_PLAYERS ) - { - // set the players team - strncpy( g_PlayerExtraInfo[player_index].teamname, team_id.c_str(), MAX_TEAM_NAME ); - } - - // rebuild the list of teams - m_pScoreBoard->RebuildTeams(); - - return 1; -} - -void TeamFortressViewport::DeathMsg( int killer, int victim ) -{ - m_pScoreBoard->DeathMsg(killer,victim); -} +//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: Client DLL VGUI Viewport +// +// $Workfile: $ +// $Date: 2002/10/28 20:32:17 $ +// +//----------------------------------------------------------------------------- +// $Log: vgui_TeamFortressViewport.cpp,v $ +// Revision 1.12 2002/10/28 20:32:17 Flayra +// - Don't display disconcerting error message +// +// Revision 1.11 2002/10/16 00:37:33 Flayra +// - Added support for authentication in scoreboard +// +// Revision 1.10 2002/07/08 16:15:13 Flayra +// - Refactored team color management +// +// Revision 1.9 2002/04/16 19:32:07 Charlie +// - Removed crappy way of handling pop-up menu changes, removed pressing enter to go back to RR (was getting in way of chat, now just use "readyroom" - F4 -) +// +// Revision 1.8 2002/02/25 20:35:41 Charlie +// - Added hotgrouping of units and buildings, moving to units or buildings when double-tapping, and camera tracking +// +// Revision 1.7 2002/01/30 18:34:32 Charlie +// - Fixed doubled-cursor problem with scoreboard (only show sprite cursor, not Windows cursor) +// +// Revision 1.6 2001/11/13 17:51:02 Charlie +// - Increased max teams, changed team colors (allow aliens vs. aliens and fronts vs. fronts), general scoreboard support +// +// Revision 1.5 2001/10/22 19:26:32 Charlie +// - Changes to make scoreboard work in commander mode +// +// Revision 1.4 2001/09/13 15:01:02 Charlie +// - Merging with 1108 +// +// Revision 1.3 2001/06/02 14:26:47 charlie +// - Commented out UpdateCursorState because it was causing problems (look into this) +// Revision 1.1.1.1.2.1 2001/09/13 14:42:30 Charlie +// - HL1108 +// +// Revision 1.2 2001/04/09 19:31:35 charlie +// - Quick hacky tests to try out menus for team/class picking...yuck +// +// Revision 1.1.1.1 2000/06/17 14:12:45 charlie +// Final version of new HL SDK. May not compile. +// Previous versions of my MSVC project files and utility .bat files. +// This is my starting point; there is no mod-specific code in here. +// +// +// $NoKeywords: $ +//============================================================================= +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hud.h" +#include "cl_util.h" +#include "camera.h" +#include "kbutton.h" +#include "common/cvardef.h" +#include "common/usercmd.h" +#include "common/const.h" +#include "camera.h" +#include "in_defs.h" +#include "pm_shared/pm_shared.h" +#include "../engine/keydefs.h" +#include "demo.h" +#include "common/demo_api.h" + +#include "vgui_int.h" +#include "vgui_TeamFortressViewport.h" +#include "vgui_ServerBrowser.h" +#include "vgui_ScorePanel.h" +#include "vgui_SpectatorPanel.h" +#include "game_shared\vgui_loadtga.h" +#include "mod/AvHConstants.h" +#include "mod/AvHTitles.h" +#include "mod/AvHPieMenuHandler.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHCommandConstants.h" +#include "ui/ChatPanel.h" +#include "mod/AvHNetworkMessages.h" +#include "util/STLUtil.h" + +extern int g_iVisibleMouse; +class CCommandMenu; +int g_iPlayerClass; +int g_iTeamNumber; +int g_iUser1; +int g_iUser2; +int g_iUser3; + +// Scoreboard positions +#define SBOARD_INDENT_X XRES(75) +#define SBOARD_INDENT_Y YRES(40) + +// low-res scoreboard indents +#define SBOARD_INDENT_X_512 30 +#define SBOARD_INDENT_Y_512 30 + +#define SBOARD_INDENT_X_400 0 +#define SBOARD_INDENT_Y_400 20 + + +const int kPlayerMenuWidth = 200; + +void IN_ResetMouse( void ); +extern CMenuPanel *CMessageWindowPanel_Create( const char *szMOTD, const char *szTitle, int iShadeFullscreen, int iRemoveMe, int x, int y, int wide, int tall ); +extern float * GetClientColor( int clientIndex ); + +using namespace vgui; + + +class PlayerButton : public CommandButton +{ + +public: + + PlayerButton(int inPlayerNumber, const char* text,int x,int y,int wide,int tall, bool bNoHighlight, bool bFlat ) + : CommandButton(text, x, y, wide, tall, bNoHighlight, bFlat) + { + mPlayerNumber = inPlayerNumber; + } + + void paint() + { + + // Set the color of the button based on the team color (or red if the player is dead). + + int theTeamNumber = g_PlayerExtraInfo[mPlayerNumber].teamnumber % iNumberOfTeamColors; + int r, g, b; + + switch (g_PlayerExtraInfo[mPlayerNumber].playerclass) + { + + case PLAYERCLASS_DEAD_MARINE: + case PLAYERCLASS_DEAD_ALIEN: + case PLAYERCLASS_REINFORCING: + r = 255 / gHUD.GetGammaSlope(); + g = 0 / gHUD.GetGammaSlope(); + b = 0 / gHUD.GetGammaSlope(); + break; + + default: + r = kTeamColors[theTeamNumber][0] / gHUD.GetGammaSlope(); + g = kTeamColors[theTeamNumber][1] / gHUD.GetGammaSlope(); + b = kTeamColors[theTeamNumber][2] / gHUD.GetGammaSlope(); + break; + + } + + setFgColor(r, g, b, 0); + + Button::paint(); + + } + + void paintBackground() + { + if ( isArmed() ) + { + // Orange Border + drawSetColor(255, 255, 255, 0); + drawOutlinedRect(0,0,_size[0],_size[1]); + } + } + +private: + + int mPlayerNumber; + +}; + + +// Used for Class specific buttons +char *sTFClasses[] = +{ + "", + "SCOUT", + "SNIPER", + "SOLDIER", + "DEMOMAN", + "MEDIC", + "HWGUY", + "PYRO", + "SPY", + "ENGINEER", + "CIVILIAN", +}; + +char *sLocalisedClasses[] = +{ + "#Civilian", + "#Scout", + "#Sniper", + "#Soldier", + "#Demoman", + "#Medic", + "#HWGuy", + "#Pyro", + "#Spy", + "#Engineer", + "#Random", + "#Civilian", +}; + +char *sTFClassSelection[] = +{ + "civilian", + "scout", + "sniper", + "soldier", + "demoman", + "medic", + "hwguy", + "pyro", + "spy", + "engineer", + "randompc", + "civilian", +}; + +const int kNumOptionsButtons = 4; + +char* kOptionsButtons[kNumOptionsButtons*2] = +{ + "#Menu_Marine1Team", "jointeamone", + "#Menu_Alien1Team", "jointeamtwo", + "#Menu_ReadyRoom", "readyroom", + "#Menu_LeaveGame", "escape", +}; + +int iBuildingCosts[] = +{ + BUILD_COST_DISPENSER, + BUILD_COST_SENTRYGUN +}; + +// This maps class numbers to the Invalid Class bit. +// This is needed for backwards compatability in maps that were finished before +// all the classes were in TF. Hence the wacky sequence. +int sTFValidClassInts[] = +{ + 0, + TF_ILL_SCOUT, + TF_ILL_SNIPER, + TF_ILL_SOLDIER, + TF_ILL_DEMOMAN, + TF_ILL_MEDIC, + TF_ILL_HVYWEP, + TF_ILL_PYRO, + TF_ILL_SPY, + TF_ILL_ENGINEER, + TF_ILL_RANDOMPC, +}; + +// Get the name of TGA file, based on GameDir +char* GetVGUITGAName(const char *pszName) +{ + int i; + char sz[256]; + static char gd[256]; + const char *gamedir; + + if (ScreenWidth() < 640) + i = 320; + else + i = 640; + sprintf(sz, pszName, i); + + gamedir = gEngfuncs.pfnGetGameDirectory(); + sprintf(gd, "%s/gfx/vgui/%s.tga",gamedir,sz); + + return gd; +} + +//================================================================ +// COMMAND MENU +//================================================================ +void CCommandMenu::AddButton( CommandButton *pButton ) +{ + if (m_iButtons >= MAX_BUTTONS) + return; + + m_aButtons[m_iButtons] = pButton; + m_iButtons++; + pButton->setParent( this ); + pButton->setFont( Scheme::sf_primary3 ); + + // give the button a default key binding + if ( m_iButtons < 10 ) + { + pButton->setBoundKey( m_iButtons + '0' ); + } + else if ( m_iButtons == 10 ) + { + pButton->setBoundKey( '0' ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Tries to find a button that has a key bound to the input, and +// presses the button if found +// Input : keyNum - the character number of the input key +// Output : Returns true if the command menu should close, false otherwise +//----------------------------------------------------------------------------- +bool CCommandMenu::KeyInput( int keyNum ) +{ + // loop through all our buttons looking for one bound to keyNum + for ( int i = 0; i < m_iButtons; i++ ) + { + if ( !m_aButtons[i]->IsNotValid() ) + { + if ( m_aButtons[i]->getBoundKey() == keyNum ) + { + // hit the button + if ( m_aButtons[i]->GetSubMenu() ) + { + // open the sub menu + gViewPort->SetCurrentCommandMenu( m_aButtons[i]->GetSubMenu() ); + return false; + } + else + { + // run the bound command + m_aButtons[i]->fireActionSignal(); + return true; + } + } + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: clears the current menus buttons of any armed (highlighted) +// state, and all their sub buttons +//----------------------------------------------------------------------------- +void CCommandMenu::ClearButtonsOfArmedState( void ) +{ + for ( int i = 0; i < GetNumButtons(); i++ ) + { + m_aButtons[i]->setArmed( false ); + + if ( m_aButtons[i]->GetSubMenu() ) + { + m_aButtons[i]->GetSubMenu()->ClearButtonsOfArmedState(); + } + } +} + +void CCommandMenu::RemoveAllButtons() +{ + + for ( int i = 0; i < GetNumButtons(); i++ ) + { + + if ( m_aButtons[i]->GetSubMenu() ) + { + m_aButtons[i]->GetSubMenu()->RemoveAllButtons(); + } + + removeChild(m_aButtons[i]); + + delete m_aButtons[i]; + m_aButtons[i] = NULL; + + } + + m_iButtons = 0; + +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pSubMenu - +// Output : CommandButton +//----------------------------------------------------------------------------- +CommandButton *CCommandMenu::FindButtonWithSubmenu( CCommandMenu *pSubMenu ) +{ + for ( int i = 0; i < GetNumButtons(); i++ ) + { + if ( m_aButtons[i]->GetSubMenu() == pSubMenu ) + return m_aButtons[i]; + } + + return NULL; +} + +// Recalculate the visible buttons +bool CCommandMenu::RecalculateVisibles( int iYOffset, bool bHideAll ) +{ + int i, iCurrentY = 0; + int iVisibleButtons = 0; + + // Cycle through all the buttons in this menu, and see which will be visible + for (i = 0; i < m_iButtons; i++) + { + int iClass = m_aButtons[i]->GetPlayerClass(); + if ( (iClass && iClass != g_iPlayerClass ) || ( m_aButtons[i]->IsNotValid() ) || bHideAll ) + { + m_aButtons[i]->setVisible( false ); + if ( m_aButtons[i]->GetSubMenu() != NULL ) + { + (m_aButtons[i]->GetSubMenu())->RecalculateVisibles( 0, true ); + } + } + else + { + // If it's got a submenu, force it to check visibilities + if ( m_aButtons[i]->GetSubMenu() != NULL ) + { + if ( !(m_aButtons[i]->GetSubMenu())->RecalculateVisibles( 0 , false ) ) + { + // The submenu had no visible buttons, so don't display this button + m_aButtons[i]->setVisible( false ); + continue; + } + } + + m_aButtons[i]->setVisible( true ); + iVisibleButtons++; + } + } + + // Set Size + setSize( _size[0], (iVisibleButtons * (m_flButtonSizeY-1)) + 1 ); + + if ( iYOffset ) + { + m_iYOffset = iYOffset; + } + + for (i = 0; i < m_iButtons; i++) + { + if ( m_aButtons[i]->isVisible() ) + { + if ( m_aButtons[i]->GetSubMenu() != NULL ) + (m_aButtons[i]->GetSubMenu())->RecalculateVisibles( iCurrentY + m_iYOffset, false ); + + + // Make sure it's at the right Y position + // m_aButtons[i]->getPos( iXPos, iYPos ); + + if ( m_iDirection ) + { + m_aButtons[i]->setPos( 0, (iVisibleButtons-1) * (m_flButtonSizeY-1) - iCurrentY ); + } + else + { + m_aButtons[i]->setPos( 0, iCurrentY ); + } + + iCurrentY += (m_flButtonSizeY-1); + } + } + + return iVisibleButtons?true:false; +} + +// Make sure all submenus can fit on the screen +void CCommandMenu::RecalculatePositions( int iYOffset ) +{ + int iTop; + int iAdjust = 0; + + m_iYOffset+= iYOffset; + if ( m_iDirection ) + iTop = ScreenHeight() - (m_iYOffset + _size[1] ); + else + iTop = m_iYOffset; + + if ( iTop < 0 ) + iTop = 0; + // Calculate if this is going to fit onscreen, and shuffle it up if it won't + int iBottom = iTop + _size[1]; + if ( iBottom > ScreenHeight() ) + { + // Move in increments of button sizes + while (iAdjust < (iBottom - ScreenHeight())) + { + iAdjust += m_flButtonSizeY - 1; + } + iTop -= iAdjust; + + // Make sure it doesn't move off the top of the screen (the menu's too big to fit it all) + if ( iTop < 0 ) + { + iAdjust -= (0 - iTop); + iTop = 0; + } + } + + setPos( _pos[0], iTop ); + // We need to force all menus below this one to update their positions now, because they + // might have submenus riding off buttons in this menu that have just shifted. + for (int i = 0; i < m_iButtons; i++) + m_aButtons[i]->UpdateSubMenus( iAdjust ); +} + + +// Make this menu and all menus above it in the chain visible +void CCommandMenu::MakeVisible( CCommandMenu *pChildMenu ) +{ + setVisible(true); + if (m_pParentMenu) + m_pParentMenu->MakeVisible( this ); +} + +//================================================================ +// CreateSubMenu +CCommandMenu *TeamFortressViewport::CreateSubMenu( CommandButton *pButton, CCommandMenu *pParentMenu, int iYOffset, int iXOffset ) +{ + int iXPos = 0; + int iYPos = 0; + int iWide = CMENU_SIZE_X; + int iTall = 0; + int iDirection = 0; + + if (pParentMenu) + { + iXPos = m_pCurrentCommandMenu->GetXOffset() + (CMENU_SIZE_X - 1) + iXOffset; + iYPos = m_pCurrentCommandMenu->GetYOffset() + iYOffset; + iDirection = pParentMenu->GetDirection(); + } + + CCommandMenu *pMenu = new CCommandMenu(pParentMenu, iDirection, iXPos, iYPos, iWide, iTall ); + pMenu->setParent(this); + pButton->AddSubMenu( pMenu ); + pButton->setFont( Scheme::sf_primary3 ); + pMenu->m_flButtonSizeY = m_pCurrentCommandMenu->m_flButtonSizeY; + + // Create the Submenu-open signal + InputSignal *pISignal = new CMenuHandler_PopupSubMenuInput(pButton, pMenu); + pButton->addInputSignal(pISignal); + + // Put a > to show it's a submenu + CImageLabel *pLabel = new CImageLabel( "arrow", CMENU_SIZE_X - SUBMENU_SIZE_X, SUBMENU_SIZE_Y ); + pLabel->setParent(pButton); + pLabel->addInputSignal(pISignal); + + // Reposition + pLabel->getPos( iXPos, iYPos ); + pLabel->setPos( CMENU_SIZE_X - pLabel->getImageWide(), (BUTTON_SIZE_Y - pLabel->getImageTall()) / 2 ); + + // Create the mouse off signal for the Label too + if (!pButton->m_bNoHighlight) + pLabel->addInputSignal( new CHandler_CommandButtonHighlight(pButton) ); + + return pMenu; +} + +//----------------------------------------------------------------------------- +// Purpose: Makes sure the memory allocated for TeamFortressViewport is nulled out +// Input : stAllocateBlock - +// Output : void * +//----------------------------------------------------------------------------- +void *TeamFortressViewport::operator new( size_t stAllocateBlock ) +{ +// void *mem = Panel::operator new( stAllocateBlock ); + void *mem = ::operator new( stAllocateBlock ); + memset( mem, 0, stAllocateBlock ); + return mem; +} + +//----------------------------------------------------------------------------- +// Purpose: InputSignal handler for the main viewport +//----------------------------------------------------------------------------- +class CViewPortInputHandler : public InputSignal +{ +public: + bool bPressed; + + CViewPortInputHandler() + { + } + + virtual void cursorMoved(int x,int y,Panel* panel) {} + virtual void cursorEntered(Panel* panel) {} + virtual void cursorExited(Panel* panel) {} + virtual void mousePressed(MouseCode code,Panel* panel) + { + if ( code != MOUSE_LEFT ) + { + // send a message to close the command menu + // this needs to be a message, since a direct call screws the timing + gEngfuncs.pfnClientCmd( "ForceCloseCommandMenu\n" ); + } + } + virtual void mouseReleased(MouseCode code,Panel* panel) + { + } + + virtual void mouseDoublePressed(MouseCode code,Panel* panel) {} + virtual void mouseWheeled(int delta,Panel* panel) {} + virtual void keyPressed(KeyCode code,Panel* panel) {} + virtual void keyTyped(KeyCode code,Panel* panel) {} + virtual void keyReleased(KeyCode code,Panel* panel) {} + virtual void keyFocusTicked(Panel* panel) {} +}; + + +//================================================================ +TeamFortressViewport::TeamFortressViewport(int x,int y,int wide,int tall) : Panel(x,y,wide,tall), m_SchemeManager(wide,tall) +{ + gViewPort = this; + m_iInitialized = false; + m_pTeamMenu = NULL; + m_pClassMenu = NULL; + m_pScoreBoard = NULL; + mOptionsScreen = NULL; + mOptionsButtons = new CommandButton*[kNumOptionsButtons]; + + m_pSpectatorPanel = NULL; + m_pCurrentMenu = NULL; + m_pCurrentCommandMenu = NULL; + + Initialize(); + addInputSignal( new CViewPortInputHandler ); + + int r, g, b, a; + + Scheme* pScheme = App::getInstance()->getScheme(); + + // primary text color + // Get the colors + //!! two different types of scheme here, need to integrate + SchemeHandle_t hPrimaryScheme = m_SchemeManager.getSchemeHandle( "Primary Button Text" ); + { + // font + pScheme->setFont( Scheme::sf_primary1, m_SchemeManager.getFont(hPrimaryScheme) ); + + // text color + m_SchemeManager.getFgColor( hPrimaryScheme, r, g, b, a ); + pScheme->setColor(Scheme::sc_primary1, r, g, b, a ); // sc_primary1 is non-transparent orange + + // background color (transparent black) + m_SchemeManager.getBgColor( hPrimaryScheme, r, g, b, a ); + pScheme->setColor(Scheme::sc_primary3, r, g, b, a ); + + // armed foreground color + m_SchemeManager.getFgArmedColor( hPrimaryScheme, r, g, b, a ); + pScheme->setColor(Scheme::sc_secondary2, r, g, b, a ); + + // armed background color + m_SchemeManager.getBgArmedColor( hPrimaryScheme, r, g, b, a ); + pScheme->setColor(Scheme::sc_primary2, r, g, b, a ); + + //!! need to get this color from scheme file + // used for orange borders around buttons + m_SchemeManager.getBorderColor( hPrimaryScheme, r, g, b, a ); + // pScheme->setColor(Scheme::sc_secondary1, r, g, b, a ); + pScheme->setColor(Scheme::sc_secondary1, 255*0.7, 170*0.7, 0, 0); + } + + // Change the second primary font (used in the scoreboard) + SchemeHandle_t hScoreboardScheme = m_SchemeManager.getSchemeHandle( "Scoreboard Text" ); + { + pScheme->setFont(Scheme::sf_primary2, m_SchemeManager.getFont(hScoreboardScheme) ); + } + + // Change the third primary font (used in command menu) + SchemeHandle_t hCommandMenuScheme = m_SchemeManager.getSchemeHandle( "CommandMenu Text" ); + { + pScheme->setFont(Scheme::sf_primary3, m_SchemeManager.getFont(hCommandMenuScheme) ); + } + + App::getInstance()->setScheme(pScheme); + + // VGUI MENUS + CreateTeamMenu(); + CreateClassMenu(); + CreateSpectatorMenu(); + CreateScoreBoard(); + CreateOptionsMenu(); + // Init command menus + m_iNumMenus = 0; + m_iCurrentTeamNumber = m_iUser1 = m_iUser2 = m_iUser3 = 0; + + m_StandardMenu = CreateCommandMenu("commandmenu.txt", 0, CMENU_TOP, false, CMENU_SIZE_X, BUTTON_SIZE_Y, 0 ); + m_SpectatorOptionsMenu = CreateCommandMenu("spectatormenu.txt", 1, YRES(32), true, CMENU_SIZE_X, BUTTON_SIZE_Y / 2, 0 ); // above bottom bar, flat design + m_SpectatorCameraMenu = CreateCommandMenu("spectcammenu.txt", 1, YRES(32), true, XRES( 200 ), BUTTON_SIZE_Y / 2, ScreenWidth() - ( XRES ( 200 ) + 15 ) ); // above bottom bar, flat design + + CreatePlayerMenu(); + + CreateServerBrowser(); + + // : 0000989: + // m_chatPanel = new ChatPanel(10, (ScreenHeight() * 0.75 - 30) / 2, ScreenWidth() - 20, 30); + m_chatPanel = new ChatPanel(10, ScreenHeight() * 0.57f - 30, ScreenWidth() - 20, 30); + // : + m_chatPanel->setParent(this); + m_chatPanel->setVisible(false); + +} + +//----------------------------------------------------------------------------- +// Purpose: Called everytime a new level is started. Viewport clears out it's data. +//----------------------------------------------------------------------------- +void TeamFortressViewport::Initialize( void ) +{ + // Force each menu to Initialize + if (m_pTeamMenu) + { + m_pTeamMenu->Initialize(); + } + if (m_pClassMenu) + { + m_pClassMenu->Initialize(); + } + if (m_pScoreBoard) + { + m_pScoreBoard->Initialize(); + HideScoreBoard(); + } + if (m_pSpectatorPanel) + { + // Spectator menu doesn't need initializing + m_pSpectatorPanel->setVisible( false ); + } + + // Make sure all menus are hidden + HideVGUIMenu(); + HideCommandMenu(); + + // Clear out some data + m_iGotAllMOTD = true; + m_iRandomPC = false; + m_flScoreBoardLastUpdated = 0; + m_flSpectatorPanelLastUpdated = 0; + + // reset player info + g_iPlayerClass = 0; + g_iTeamNumber = 0; + + memset(this->m_sMapName, 0, MAX_MAPNAME_LENGTH); + memset(this->m_szServerName, 0, MAX_SERVERNAME_LENGTH); + for (int i = 0; i < 5; i++) + { + m_iValidClasses[i] = 0; + strcpy(m_sTeamNames[i], ""); + } + + App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(Scheme::SchemeCursor::scu_none) ); +} + +class CException; +//----------------------------------------------------------------------------- +// Purpose: Read the Command Menu structure from the txt file and create the menu. +// Returns Index of menu in m_pCommandMenus +//----------------------------------------------------------------------------- +int TeamFortressViewport::CreateCommandMenu( char * menuFile, int direction, int yOffset, bool flatDesign, float flButtonSizeX, float flButtonSizeY, int xOffset ) +{ + // COMMAND MENU + // Create the root of this new Command Menu + + int newIndex = m_iNumMenus; + + m_pCommandMenus[newIndex] = new CCommandMenu(NULL, direction, xOffset, yOffset, flButtonSizeX, 300); // This will be resized once we know how many items are in it + m_pCommandMenus[newIndex]->setParent(this); + m_pCommandMenus[newIndex]->setVisible(false); + m_pCommandMenus[newIndex]->m_flButtonSizeY = flButtonSizeY; + m_pCommandMenus[newIndex]->m_iSpectCmdMenu = direction; + + m_iNumMenus++; + + // Read Command Menu from the txt file + char token[1024]; + char *pfile = (char*)gEngfuncs.COM_LoadFile( menuFile, 5, NULL); + if (!pfile) + { + gEngfuncs.Con_DPrintf( "Unable to open %s\n", menuFile); + SetCurrentCommandMenu( NULL ); + return newIndex; + } + +try +{ + // First, read in the localisation strings + + // Detpack strings + gHUD.m_TextMessage.LocaliseTextString( "#DetpackSet_For5Seconds", m_sDetpackStrings[0], MAX_BUTTON_SIZE ); + gHUD.m_TextMessage.LocaliseTextString( "#DetpackSet_For20Seconds", m_sDetpackStrings[1], MAX_BUTTON_SIZE ); + gHUD.m_TextMessage.LocaliseTextString( "#DetpackSet_For50Seconds", m_sDetpackStrings[2], MAX_BUTTON_SIZE ); + + // Now start parsing the menu structure + m_pCurrentCommandMenu = m_pCommandMenus[newIndex]; + char szLastButtonText[32] = "file start"; + pfile = gEngfuncs.COM_ParseFile(pfile, token); + while ( ( strlen ( token ) > 0 ) && ( m_iNumMenus < MAX_MENUS ) ) + { + // Keep looping until we hit the end of this menu + while ( token[0] != '}' && ( strlen( token ) > 0 ) ) + { + char cText[32] = ""; + char cBoundKey[32] = ""; + char cCustom[32] = ""; + static const int cCommandLength = 128; + char cCommand[cCommandLength] = ""; + char szMap[MAX_MAPNAME] = ""; + int iPlayerClass = 0; + int iCustom = false; + int iTeamOnly = -1; + int iToggle = 0; + int iButtonY; + bool bGetExtraToken = true; + CommandButton *pButton = NULL; + + // We should never be here without a Command Menu + if (!m_pCurrentCommandMenu) + { + gEngfuncs.Con_Printf("Error in %s file after '%s'.\n",menuFile, szLastButtonText ); + m_iInitialized = false; + return newIndex; + } + + // token should already be the bound key, or the custom name + strncpy( cCustom, token, 32 ); + cCustom[31] = '\0'; + + // See if it's a custom button + if (!strcmp(cCustom, "CUSTOM") ) + { + iCustom = true; + + // Get the next token + pfile = gEngfuncs.COM_ParseFile(pfile, token); + } + // See if it's a map + else if (!strcmp(cCustom, "MAP") ) + { + // Get the mapname + pfile = gEngfuncs.COM_ParseFile(pfile, token); + strncpy( szMap, token, MAX_MAPNAME ); + szMap[MAX_MAPNAME-1] = '\0'; + + // Get the next token + pfile = gEngfuncs.COM_ParseFile(pfile, token); + } + else if ( !strncmp(cCustom, "TEAM", 4) ) // TEAM1, TEAM2, TEAM3, TEAM4 + { + // make it a team only button + iTeamOnly = atoi( cCustom + 4 ); + + // Get the next token + pfile = gEngfuncs.COM_ParseFile(pfile, token); + } + else if ( !strncmp(cCustom, "TOGGLE", 6) ) + { + iToggle = true; + // Get the next token + pfile = gEngfuncs.COM_ParseFile(pfile, token); + } + else + { + // See if it's a Class + for (int i = 1; i <= PC_ENGINEER; i++) + { + if ( !strcmp(token, sTFClasses[i]) ) + { + // Save it off + iPlayerClass = i; + + // Get the button text + pfile = gEngfuncs.COM_ParseFile(pfile, token); + break; + } + } + } + + // Get the button bound key + strncpy( cBoundKey, token, 32 ); + cText[31] = '\0'; + + // Get the button text + pfile = gEngfuncs.COM_ParseFile(pfile, token); + strncpy( cText, token, 32 ); + cText[31] = '\0'; + + // save off the last button text we've come across (for error reporting) + strcpy( szLastButtonText, cText ); + + // Get the button command + pfile = gEngfuncs.COM_ParseFile(pfile, token); + strncpy( cCommand, token, cCommandLength ); + cCommand[cCommandLength - 1] = '\0'; + + iButtonY = (BUTTON_SIZE_Y-1) * m_pCurrentCommandMenu->GetNumButtons(); + // Custom button handling + if ( iCustom ) + { + pButton = CreateCustomButton( cText, cCommand, iButtonY ); + + // Get the next token to see if we're a menu + pfile = gEngfuncs.COM_ParseFile(pfile, token); + + if ( token[0] == '{' ) + { + strcpy( cCommand, token ); + } + else + { + bGetExtraToken = false; + } + } + else if ( szMap[0] != '\0' ) + { + // create a map button + pButton = new MapButton(szMap, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY ); + } + else if ( iTeamOnly != -1) + { + // button that only shows up if the player is on team iTeamOnly + pButton = new TeamOnlyCommandButton( iTeamOnly, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY, flatDesign ); + } + else if ( iToggle && direction == 0 ) + { + pButton = new ToggleCommandButton( cCommand, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY, flatDesign ); + } + else if ( direction == 1 ) + { + if ( iToggle ) + pButton = new SpectToggleButton( cCommand, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY, flatDesign ); + else + pButton = new SpectButton( iPlayerClass, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY ); + } + else + { + // normal button + pButton = new CommandButton( iPlayerClass, cText, xOffset, iButtonY, flButtonSizeX, flButtonSizeY, flatDesign ); + } + + // add the button into the command menu + if ( pButton ) + { + m_pCurrentCommandMenu->AddButton( pButton ); + pButton->setBoundKey( cBoundKey[0] ); + pButton->setParentMenu( m_pCurrentCommandMenu ); + + // Override font in CommandMenu + pButton->setFont( Scheme::sf_primary3 ); + } + + // Find out if it's a submenu or a button we're dealing with + if ( cCommand[0] == '{' ) + { + if ( m_iNumMenus >= MAX_MENUS ) + { + gEngfuncs.Con_Printf( "Too many menus in %s past '%s'\n",menuFile, szLastButtonText ); + } + else + { + // Create the menu + m_pCommandMenus[m_iNumMenus] = CreateSubMenu(pButton, m_pCurrentCommandMenu, iButtonY ); + m_pCurrentCommandMenu = m_pCommandMenus[m_iNumMenus]; + m_iNumMenus++; + } + } + else if ( !iCustom ) + { + // Create the button and attach it to the current menu + if ( iToggle ) + pButton->addActionSignal(new CMenuHandler_ToggleCvar(cCommand)); + else + pButton->addActionSignal(new CMenuHandler_StringCommand(cCommand)); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + + // Get the next token + if ( bGetExtraToken ) + { + pfile = gEngfuncs.COM_ParseFile(pfile, token); + } + } + + // Move back up a menu + m_pCurrentCommandMenu = m_pCurrentCommandMenu->GetParentMenu(); + + pfile = gEngfuncs.COM_ParseFile(pfile, token); + } +} +catch( CException *e ) +{ + e; + //e->Delete(); + e = NULL; + m_iInitialized = false; + return newIndex; +} + + SetCurrentMenu( NULL ); + SetCurrentCommandMenu( NULL ); + gEngfuncs.COM_FreeFile( pfile ); + + m_iInitialized = true; + return newIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: Creates all the class choices under a spy's disguise menus, and +// maps a command to them +// Output : CCommandMenu +//----------------------------------------------------------------------------- +CCommandMenu *TeamFortressViewport::CreateDisguiseSubmenu( CommandButton *pButton, CCommandMenu *pParentMenu, const char *commandText, int iYOffset, int iXOffset ) +{ + // create the submenu, under which the class choices will be listed + CCommandMenu *pMenu = CreateSubMenu( pButton, pParentMenu, iYOffset, iXOffset ); + m_pCommandMenus[m_iNumMenus] = pMenu; + m_iNumMenus++; + + // create the class choice buttons + for ( int i = PC_SCOUT; i <= PC_ENGINEER; i++ ) + { + CommandButton *pDisguiseButton = new CommandButton( CHudTextMessage::BufferedLocaliseTextString( sLocalisedClasses[i] ), 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y ); + + char sz[256]; + sprintf(sz, "%s %d", commandText, i ); + pDisguiseButton->addActionSignal(new CMenuHandler_StringCommand(sz)); + + pMenu->AddButton( pDisguiseButton ); + } + + return pMenu; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pButtonText - +// *pButtonName - +// Output : CommandButton +//----------------------------------------------------------------------------- +CommandButton *TeamFortressViewport::CreateCustomButton( char *pButtonText, char *pButtonName, int iYOffset ) +{ + CommandButton *pButton = NULL; + CCommandMenu *pMenu = NULL; + + // ChangeTeam + if ( !strcmp( pButtonName, "!CHANGETEAM" ) ) + { + // ChangeTeam Submenu + pButton = new CommandButton(pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + + // Create the submenu + pMenu = CreateSubMenu(pButton, m_pCurrentCommandMenu, iYOffset ); + m_pCommandMenus[m_iNumMenus] = pMenu; + m_iNumMenus++; + + // ChangeTeam buttons + for (int i = 0; i < 4; i++) + { + char sz[256]; + sprintf(sz, "jointeam %d", i+1); + m_pTeamButtons[i] = new TeamButton(i+1, "teamname", 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + m_pTeamButtons[i]->addActionSignal(new CMenuHandler_StringCommandWatch( sz )); + pMenu->AddButton( m_pTeamButtons[i] ); + } + + // Auto Assign button + m_pTeamButtons[4] = new TeamButton(5, gHUD.m_TextMessage.BufferedLocaliseTextString( "#Team_AutoAssign" ), 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + m_pTeamButtons[4]->addActionSignal(new CMenuHandler_StringCommand( "jointeam 5" )); + pMenu->AddButton( m_pTeamButtons[4] ); + + // Spectate button + m_pTeamButtons[5] = new SpectateButton( CHudTextMessage::BufferedLocaliseTextString( "#Menu_Spectate" ), 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y, false); + m_pTeamButtons[5]->addActionSignal(new CMenuHandler_StringCommand( "spectate" )); + pMenu->AddButton( m_pTeamButtons[5] ); + } + // ChangeClass + else if ( !strcmp( pButtonName, "!CHANGECLASS" ) ) + { + // Create the Change class menu + pButton = new ClassButton(-1, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y, false); + + // ChangeClass Submenu + pMenu = CreateSubMenu(pButton, m_pCurrentCommandMenu, iYOffset ); + m_pCommandMenus[m_iNumMenus] = pMenu; + m_iNumMenus++; + + for (int i = PC_SCOUT; i <= PC_RANDOM; i++ ) + { + char sz[256]; + + // ChangeClass buttons + CHudTextMessage::LocaliseTextString( sLocalisedClasses[i], sz, 256 ); + ClassButton *pClassButton = new ClassButton( i, sz, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y, false); + + sprintf(sz, "%s", sTFClassSelection[i]); + pClassButton->addActionSignal(new CMenuHandler_StringCommandClassSelect(sz)); + pMenu->AddButton( pClassButton ); + } + } + // Map Briefing + else if ( !strcmp( pButtonName, "!MAPBRIEFING" ) ) + { + pButton = new CommandButton(pButtonText, 0, BUTTON_SIZE_Y * m_pCurrentCommandMenu->GetNumButtons(), CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_TextWindow(MENU_MAPBRIEFING)); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + // Class Descriptions + else if ( !strcmp( pButtonName, "!CLASSDESC" ) ) + { + pButton = new ClassButton(0, pButtonText, 0, BUTTON_SIZE_Y * m_pCurrentCommandMenu->GetNumButtons(), CMENU_SIZE_X, BUTTON_SIZE_Y, false); + pButton->addActionSignal(new CMenuHandler_TextWindow(MENU_CLASSHELP)); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + else if ( !strcmp( pButtonName, "!SERVERINFO" ) ) + { + pButton = new ClassButton(0, pButtonText, 0, BUTTON_SIZE_Y * m_pCurrentCommandMenu->GetNumButtons(), CMENU_SIZE_X, BUTTON_SIZE_Y, false); + pButton->addActionSignal(new CMenuHandler_TextWindow(MENU_INTRO)); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + // Spy abilities + else if ( !strcmp( pButtonName, "!SPY" ) ) + { + pButton = new DisguiseButton( 0, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y ); + } + // Feign + else if ( !strcmp( pButtonName, "!FEIGN" ) ) + { + pButton = new FeignButton(FALSE, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand( "feign" )); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + // Feign Silently + else if ( !strcmp( pButtonName, "!FEIGNSILENT" ) ) + { + pButton = new FeignButton(FALSE, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand( "sfeign" )); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + // Stop Feigning + else if ( !strcmp( pButtonName, "!FEIGNSTOP" ) ) + { + pButton = new FeignButton(TRUE, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand( "feign" )); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + // Disguise + else if ( !strcmp( pButtonName, "!DISGUISEENEMY" ) ) + { + // Create the disguise enemy button, which active only if there are 2 teams + pButton = new DisguiseButton(DISGUISE_TEAM2, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + CreateDisguiseSubmenu( pButton, m_pCurrentCommandMenu, "disguise_enemy", iYOffset); + } + else if ( !strcmp( pButtonName, "!DISGUISEFRIENDLY" ) ) + { + // Create the disguise friendly button, which active only if there are 1 or 2 teams + pButton = new DisguiseButton(DISGUISE_TEAM1 | DISGUISE_TEAM2, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + CreateDisguiseSubmenu( pButton, m_pCurrentCommandMenu, "disguise_friendly", iYOffset ); + } + else if ( !strcmp( pButtonName, "!DISGUISE" ) ) + { + // Create the Disguise button + pButton = new DisguiseButton( DISGUISE_TEAM3 | DISGUISE_TEAM4, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + CCommandMenu *pDisguiseMenu = CreateSubMenu( pButton, m_pCurrentCommandMenu, iYOffset ); + m_pCommandMenus[m_iNumMenus] = pDisguiseMenu; + m_iNumMenus++; + + // Disguise Enemy submenu buttons + for ( int i = 1; i <= 4; i++ ) + { + // only show the 4th disguise button if we have 4 teams + m_pDisguiseButtons[i] = new DisguiseButton( ((i < 4) ? DISGUISE_TEAM3 : 0) | DISGUISE_TEAM4, "Disguise", 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + + pDisguiseMenu->AddButton( m_pDisguiseButtons[i] ); + m_pDisguiseButtons[i]->setParentMenu( pDisguiseMenu ); + + char sz[256]; + sprintf( sz, "disguise %d", i ); + CreateDisguiseSubmenu( m_pDisguiseButtons[i], pDisguiseMenu, sz, iYOffset, CMENU_SIZE_X - 1 ); + } + } + // Start setting a Detpack + else if ( !strcmp( pButtonName, "!DETPACKSTART" ) ) + { + // Detpack Submenu + pButton = new DetpackButton(2, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + + // Create the submenu + pMenu = CreateSubMenu(pButton, m_pCurrentCommandMenu, iYOffset ); + m_pCommandMenus[m_iNumMenus] = pMenu; + m_iNumMenus++; + + // Set detpack buttons + CommandButton *pDetButton; + pDetButton = new CommandButton(m_sDetpackStrings[0], 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + pDetButton->addActionSignal(new CMenuHandler_StringCommand("detstart 5")); + pMenu->AddButton( pDetButton ); + pDetButton = new CommandButton(m_sDetpackStrings[1], 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + pDetButton->addActionSignal(new CMenuHandler_StringCommand("detstart 20")); + pMenu->AddButton( pDetButton ); + pDetButton = new CommandButton(m_sDetpackStrings[2], 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + pDetButton->addActionSignal(new CMenuHandler_StringCommand("detstart 50")); + pMenu->AddButton( pDetButton ); + } + // Stop setting a Detpack + else if ( !strcmp( pButtonName, "!DETPACKSTOP" ) ) + { + pButton = new DetpackButton(1, pButtonText, 0, BUTTON_SIZE_Y, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand( "detstop" )); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + // Engineer building + else if ( !strcmp( pButtonName, "!BUILD" ) ) + { + // only appears if the player is an engineer, and either they have built something or have enough metal to build + pButton = new BuildButton( BUILDSTATE_BASE, 0, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + } + else if ( !strcmp( pButtonName, "!BUILDSENTRY" ) ) + { + pButton = new BuildButton( BUILDSTATE_CANBUILD, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("build 2")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + else if ( !strcmp( pButtonName, "!BUILDDISPENSER" ) ) + { + pButton = new BuildButton( BUILDSTATE_CANBUILD, BuildButton::DISPENSER, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("build 1")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + else if ( !strcmp( pButtonName, "!ROTATESENTRY180" ) ) + { + pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("rotatesentry180")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + else if ( !strcmp( pButtonName, "!ROTATESENTRY" ) ) + { + pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("rotatesentry")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + else if ( !strcmp( pButtonName, "!DISMANTLEDISPENSER" ) ) + { + pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::DISPENSER, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("dismantle 1")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + else if ( !strcmp( pButtonName, "!DISMANTLESENTRY" ) ) + { + pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("dismantle 2")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + else if ( !strcmp( pButtonName, "!DETONATEDISPENSER" ) ) + { + pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::DISPENSER, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("detdispenser")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + else if ( !strcmp( pButtonName, "!DETONATESENTRY" ) ) + { + pButton = new BuildButton( BUILDSTATE_HASBUILDING, BuildButton::SENTRYGUN, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("detsentry")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + // Stop building + else if ( !strcmp( pButtonName, "!BUILDSTOP" ) ) + { + pButton = new BuildButton( BUILDSTATE_BUILDING, 0, pButtonText, 0, BUTTON_SIZE_Y * 2, CMENU_SIZE_X, BUTTON_SIZE_Y); + pButton->addActionSignal(new CMenuHandler_StringCommand("build")); + // Create an input signal that'll popup the current menu + pButton->addInputSignal( new CMenuHandler_PopupSubMenuInput(pButton, m_pCurrentCommandMenu) ); + } + + return pButton; +} + +void TeamFortressViewport::ToggleServerBrowser() +{ + if (!m_iInitialized) + return; + + if ( !m_pServerBrowser ) + return; + + if ( m_pServerBrowser->isVisible() ) + { + m_pServerBrowser->setVisible( false ); + } + else + { + m_pServerBrowser->setVisible( true ); + } + + UpdateCursorState(); +} + +//======================================================================= +void TeamFortressViewport::ShowCommandMenu(int menuIndex) +{ + if (!m_iInitialized) + return; + + //Already have a menu open. + if ( m_pCurrentMenu ) + return; + + // is the command menu open? + if ( m_pCurrentCommandMenu == m_pCommandMenus[menuIndex] ) + { + HideCommandMenu(); + return; + } + + // Not visible while in intermission + if ( gHUD.m_iIntermission ) + return; + + // Recalculate visible menus + UpdateCommandMenu( menuIndex ); + HideVGUIMenu(); + + SetCurrentCommandMenu( m_pCommandMenus[menuIndex] ); + m_flMenuOpenTime = gHUD.m_flTime; + UpdateCursorState(); + + // get command menu parameters + for ( int i = 2; i < gEngfuncs.Cmd_Argc(); i++ ) + { + const char *param = gEngfuncs.Cmd_Argv( i - 1 ); + if ( param ) + { + if ( m_pCurrentCommandMenu->KeyInput(param[0]) ) + { + // kill the menu open time, since the key input is final + HideCommandMenu(); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles the key input of "-commandmenu" +// Input : +//----------------------------------------------------------------------------- +void TeamFortressViewport::InputSignalHideCommandMenu() +{ + if (!m_iInitialized) + return; + + // if they've just tapped the command menu key, leave it open + if ( (m_flMenuOpenTime + 0.3) > gHUD.m_flTime ) + return; + + HideCommandMenu(); +} + +//----------------------------------------------------------------------------- +// Purpose: Hides the command menu +//----------------------------------------------------------------------------- +void TeamFortressViewport::HideCommandMenu() +{ + if (!m_iInitialized) + return; + + if ( m_pCommandMenus[m_StandardMenu] ) + { + m_pCommandMenus[m_StandardMenu]->ClearButtonsOfArmedState(); + } + + if ( m_pCommandMenus[m_SpectatorOptionsMenu] ) + { + m_pCommandMenus[m_SpectatorOptionsMenu]->ClearButtonsOfArmedState(); + } + + if ( m_pCommandMenus[m_SpectatorCameraMenu] ) + { + m_pCommandMenus[m_SpectatorCameraMenu]->ClearButtonsOfArmedState(); + } + + m_flMenuOpenTime = 0.0f; + SetCurrentCommandMenu( NULL ); + UpdateCursorState(); +} + +//----------------------------------------------------------------------------- +// Purpose: Bring up the scoreboard +//----------------------------------------------------------------------------- +void TeamFortressViewport::ShowScoreBoard( void ) +{ + if (m_pScoreBoard) + { + // No Scoreboard in single-player + if ( gEngfuncs.GetMaxClients() > 1 ) + { + if(gHUD.SwitchUIMode(SCOREBOARD_MODE)) + { + m_pScoreBoard->Open(); + + // TODO: HLSDK 2.3 requires this? + //UpdateCursorState(); + + // If cursor is visible, set squelch mode automatically (used for commander) + if(gHUD.GetIsInTopDownMode()) + { + m_pScoreBoard->SetSquelchMode(true); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the scoreboard is up +//----------------------------------------------------------------------------- +bool TeamFortressViewport::IsScoreBoardVisible( void ) +{ + if (m_pScoreBoard) + return m_pScoreBoard->isVisible(); + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Hide the scoreboard +//----------------------------------------------------------------------------- +const float kButtonWidth = .15f; +const float kButtonHeight = .08f; +const float kButtonHorizontalSpacing = .15f; +const float kButtonVerticalSpacing = .05f; +const float xInset = .05f; +const float yInset = .07f; + +void TeamFortressViewport::HideScoreBoard( void ) +{ + // Prevent removal of scoreboard during intermission + if ( gHUD.m_iIntermission ) + return; + + if (m_pScoreBoard && m_pScoreBoard->isVisible()) + { + // Hide other components + if(gHUD.SwitchUIMode(MAIN_MODE)) + { + m_pScoreBoard->setVisible(false); + + //GetClientVoiceMgr()->StopSquelchMode(); + m_pScoreBoard->SetSquelchMode(false); + + //UpdateCursorState(); + } + } +} + +void TeamFortressViewport::ShowOptionsMenu() +{ + if(this->mOptionsScreen) + { + const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_ON); + gHUD.PlayHUDSound(theSound, kHUDSoundVolume); + + this->mOptionsScreen->setVisible(true); + + for(int i = 0; i < kNumOptionsButtons; i++) + { + if(this->mOptionsButtons[i]) + { + this->mOptionsButtons[i]->setVisible(true); + } + } + + gHUD.GetManager().SetMouseVisibility(true); + } +} + +void TeamFortressViewport::HideOptionsMenu() +{ + if(this->mOptionsScreen) + { + const char* theSound = AvHSHUGetCommonSoundName(gHUD.GetIsAlien(), WEAPON_SOUND_HUD_OFF); + gHUD.PlayHUDSound(theSound, kHUDSoundVolume); + + this->mOptionsScreen->setVisible(false); + for(int i = 0; i < kNumOptionsButtons; i++) + { + if(this->mOptionsButtons[i]) + { + this->mOptionsButtons[i]->setVisible(false); + } + } + + gHUD.GetManager().SetMouseVisibility(false); + + gEngfuncs.pfnSetMousePos(gEngfuncs.GetWindowCenterX(), gEngfuncs.GetWindowCenterY()); + } +} + +bool TeamFortressViewport::IsOptionsMenuVisible() +{ + bool theIsVisible = false; + + if(this->mOptionsScreen) + { + theIsVisible = this->mOptionsScreen->isVisible(); + } + + return theIsVisible; +} + +// Set the submenu of the Command Menu +void TeamFortressViewport::SetCurrentCommandMenu( CCommandMenu *pNewMenu ) +{ + for (int i = 0; i < m_iNumMenus; i++) + m_pCommandMenus[i]->setVisible(false); + + m_pCurrentCommandMenu = pNewMenu; + + if (m_pCurrentCommandMenu) + m_pCurrentCommandMenu->MakeVisible( NULL ); +} + +void TeamFortressViewport::UpdateCommandMenu(int menuIndex) +{ + m_pCommandMenus[menuIndex]->RecalculateVisibles( 0, false ); + m_pCommandMenus[menuIndex]->RecalculatePositions( 0 ); +} + +void COM_FileBase ( const char *in, char *out); + +void TeamFortressViewport::UpdateSpectatorPanel() +{ + m_iUser1 = g_iUser1; + m_iUser2 = g_iUser2; + m_iUser3 = g_iUser3; + + if (!m_pSpectatorPanel) + return; + + if ( g_iUser1 && gHUD.m_pCvarDraw->value && !gHUD.m_iIntermission) // don't draw in dev_overview mode + { + char bottomText[128]; + char helpString2[128]; + char * name; + int player = 0; + + // check if spectator combinations are still valid + gHUD.m_Spectator.CheckSettings(); + + if ( !m_pSpectatorPanel->isVisible() ) + { + m_pSpectatorPanel->setVisible( true ); // show spectator panel, but + m_pSpectatorPanel->ShowMenu( true ); + + // Removed by mmcguire. + // This text isn't applicable anymore. + + /* + char tempString[128]; + + _snprintf( tempString, sizeof( tempString ) - 1, "%c%s", HUD_PRINTCENTER, CHudTextMessage::BufferedLocaliseTextString( "#Spec_Duck" ) ); + tempString[ sizeof( tempString ) - 1 ] = '\0'; + + gHUD.m_TextMessage.MsgFunc_TextMsg( NULL, strlen( tempString ) + 1, tempString ); + */ + } + + sprintf(bottomText,"#Spec_Mode%d", g_iUser1 ); + sprintf(helpString2,"#Spec_Mode%d", g_iUser1 ); + + if ( gEngfuncs.IsSpectateOnly() ) + strcat(helpString2, " - HLTV"); + + // check if we're locked onto a target, show the player's name + if ( (g_iUser2 > 0) && (g_iUser2 <= gEngfuncs.GetMaxClients()) && (g_iUser1 != OBS_ROAMING) ) + { + player = g_iUser2; + } + + name = g_PlayerInfoList[player].name; + + // create player & health string + if ( player && name ) + { + strcpy( bottomText, name ); + if ( gEngfuncs.IsSpectateOnly() ) { + char tmp[32]; + sprintf(tmp, " (%d)", g_PlayerExtraInfo[player].health); + strcat( bottomText, tmp); + } + } +/* + // in first person mode colorize player names + if ( (g_iUser1 == OBS_IN_EYE) && player ) + { + float * color = GetClientColor( player ); + int r = color[0]*255; + int g = color[1]*255; + int b = color[2]*255; + + // set team color, a bit transparent + m_pSpectatorPanel->m_BottomMainLabel->setFgColor(r,g,b,0); + } + else + { // restore GUI color + m_pSpectatorPanel->m_BottomMainLabel->setFgColor( 143, 143, 54, 0 ); + } +*/ + // add sting auto if we are in auto directed mode + if ( CVAR_GET_FLOAT("spec_autodirector") != 0 ) + { + char tempString[128]; + sprintf(tempString, "#Spec_Auto %s", helpString2); + strcpy( helpString2, tempString ); + } + + m_pSpectatorPanel->m_BottomMainLabel->setText( CHudTextMessage::BufferedLocaliseTextString( bottomText ) ); + + + // update extra info field + char szText[64]; + + if ( gEngfuncs.IsSpectateOnly() ) + { + // in HLTV mode show number of spectators + _snprintf( szText, 63, "%s: %d", CHudTextMessage::BufferedLocaliseTextString( "#Spectators" ), gHUD.m_Spectator.m_iSpectatorNumber ); + } + else + { + // otherwise show map name + char szMapName[64]; + COM_FileBase( gEngfuncs.pfnGetLevelName(), szMapName ); + + _snprintf ( szText, 63, "%s: %s",CHudTextMessage::BufferedLocaliseTextString( "#Spec_Map" ), szMapName ); + } + + szText[63] = 0; + + m_pSpectatorPanel->m_ExtraInfo->setText ( szText ); + + /* + int timer = (int)( gHUD.m_roundTimer.m_flTimeEnd - gHUD.m_flTime ); + + if ( timer < 0 ) + timer = 0; + + _snprintf ( szText, 63, "%d:%02d\n", (timer / 60), (timer % 60) ); + + szText[63] = 0; + + m_pSpectatorPanel->m_CurrentTime->setText( szText ); */ + int theTimeToDisplay = gHUD.GetGameTime(); + _snprintf ( szText, 63, "%d:%02d\n", (theTimeToDisplay / 60), (theTimeToDisplay % 60) ); + szText[63] = 0; + m_pSpectatorPanel->m_CurrentTime->setText( szText ); + + // update spectator panel + gViewPort->m_pSpectatorPanel->Update(); + } + else + { + if ( m_pSpectatorPanel->isVisible() ) + { + m_pSpectatorPanel->setVisible( false ); + } + if (m_pSpectatorPanel->m_menuVisible) + { + m_pSpectatorPanel->ShowMenu( false ); // dsiable all menus/buttons + } + + } + + m_flSpectatorPanelLastUpdated = gHUD.m_flTime + 1.0; // update every seconds +} + +//====================================================================== +void TeamFortressViewport::CreateScoreBoard( void ) +{ + int xdent = SBOARD_INDENT_X; + int ydent = SBOARD_INDENT_Y; + if (ScreenWidth() == 512) + { + xdent = SBOARD_INDENT_X_512; + ydent = SBOARD_INDENT_Y_512; + } + else if (ScreenWidth() == 400) + { + xdent = SBOARD_INDENT_X_400; + ydent = SBOARD_INDENT_Y_400; + } + + m_pScoreBoard = new ScorePanel(xdent, ydent, ScreenWidth() - (xdent * 2), ScreenHeight() - (ydent * 2)); + m_pScoreBoard->setParent(this); + m_pScoreBoard->setVisible(false); +} + +void TeamFortressViewport::CreateOptionsMenu() +{ + //this->mOptionsScreen = new ColoredPanel(0, 0, ScreenWidth, ScreenHeight); + this->mOptionsScreen = new CTransparentPanel(128, 0, 0, ScreenWidth(), ScreenHeight()); + this->mOptionsScreen->setBorder( new LineBorder( Color(255*0.7,170*0.7,0,0 )) ); + this->mOptionsScreen->setParent(this); + this->mOptionsScreen->setBgColor(128, 128, 128, 40); + this->mOptionsScreen->setVisible(false); + + int theScreenWidth = ScreenWidth(); + int theScreenHeight = ScreenHeight(); +} + +void TeamFortressViewport::CreateServerBrowser( void ) +{ + m_pServerBrowser = new ServerBrowser( 0, 0, ScreenWidth(), ScreenHeight() ); + m_pServerBrowser->setParent(this); + m_pServerBrowser->setVisible(false); +} + +void TeamFortressViewport::CreatePlayerMenu() +{ + + int newIndex = m_iNumMenus; + + m_pCommandMenus[newIndex] = new CCommandMenu(NULL, 1, XRES(390), YRES(32), XRES(kPlayerMenuWidth), YRES(300)); // This will be resized once we know how many items are in it + + m_pCommandMenus[newIndex]->setParent(this); + m_pCommandMenus[newIndex]->setVisible(false); + m_pCommandMenus[newIndex]->m_flButtonSizeY = BUTTON_SIZE_Y / 2; + m_pCommandMenus[newIndex]->m_iSpectCmdMenu = 1; + + m_iNumMenus++; + + m_SpectatorPlayerMenu = newIndex; + +} + +void TeamFortressViewport::UpdatePlayerMenu() +{ + + CCommandMenu* pMenu = m_pCommandMenus[m_SpectatorPlayerMenu]; + + int xOffset = 0; + + float flButtonSizeX = XRES(kPlayerMenuWidth); + float flButtonSizeY = BUTTON_SIZE_Y / 2; + + // Create a menu item for each of the players. + + pMenu->ClearButtonsOfArmedState(); + pMenu->RemoveAllButtons(); + + // Make sure we have player info + gViewPort->GetAllPlayersInfo(); + + for (int team = 0; team < 4; ++team) + { + + if (gHUD.GetPlayMode() != PLAYMODE_OBSERVER) + { + // Filter out players who aren't on our team. + + int theLocalPlayerTeam = 0; + + if (gEngfuncs.GetLocalPlayer()) + { + theLocalPlayerTeam = gEngfuncs.GetLocalPlayer()->curstate.team; + } + + if (theLocalPlayerTeam != team) + { + continue; + } + + } + + for (int i = 1; i <= MAX_PLAYERS; ++i) + { + + if ( g_PlayerInfoList[i].name == NULL ) + continue; // empty player slot, skip + + if ( g_PlayerExtraInfo[i].teamname[0] == 0 ) + continue; // skip over players who are not in a team + + if ( g_PlayerExtraInfo[i].teamnumber != team) + continue; + + switch(g_PlayerExtraInfo[i].playerclass) + { + case PLAYERCLASS_SPECTATOR: + case PLAYERCLASS_COMMANDER: + case PLAYERCLASS_REINFORCING: + continue; + } + + int iButtonY = (BUTTON_SIZE_Y-1) * pMenu->GetNumButtons(); + + PlayerButton* pButton = new PlayerButton(i, g_PlayerInfoList[i].name, 0, iButtonY, flButtonSizeX, flButtonSizeY, false, true); + + //pButton->setArmedColor(teamR, teamG, teamB, 0); + //pButton->setUnArmedColor(teamR, teamG, teamB, 0); + //pButton->setArmedBorderColor(255 / gHUD.GetGammaSlope(), 255 / gHUD.GetGammaSlope(), 255 / gHUD.GetGammaSlope(), 0); + + if ( pButton ) + { + pMenu->AddButton( pButton ); + pButton->setParentMenu( pMenu ); + pButton->setFont( Scheme::sf_primary3 ); + + char command[MAX_COMMAND_SIZE]; + sprintf(command, "follow %d", i); + + pButton->addActionSignal(new CMenuHandler_StringCommand(command)); + + } + + } + } + +} + +//====================================================================== +// Set the VGUI Menu +void TeamFortressViewport::SetCurrentMenu( CMenuPanel *pMenu ) +{ + m_pCurrentMenu = pMenu; + if ( m_pCurrentMenu ) + { + // Don't open menus in demo playback + if ( gEngfuncs.pDemoAPI->IsPlayingback() ) + return; + + m_pCurrentMenu->Open(); + } +} + +//================================================================ +// Text Window +CMenuPanel* TeamFortressViewport::CreateTextWindow( int iTextToShow ) +{ + char sz[256]; + char *cText; + char *pfile = NULL; + static const int MAX_TITLE_LENGTH = 32; + char cTitle[MAX_TITLE_LENGTH]; + + if ( iTextToShow == SHOW_MOTD ) + { + if (!m_szServerName || !m_szServerName[0]) + strcpy( cTitle, kAvHGameName ); + else + strncpy( cTitle, m_szServerName, MAX_TITLE_LENGTH ); + cTitle[MAX_TITLE_LENGTH-1] = 0; + cText = m_szMOTD; + } + else if ( iTextToShow == SHOW_MAPBRIEFING ) + { + // Get the current mapname, and open it's map briefing text + if (m_sMapName && m_sMapName[0]) + { + strcpy( sz, "maps/"); + strcat( sz, m_sMapName ); + strcat( sz, ".txt" ); + } + else + { + const char *level = gEngfuncs.pfnGetLevelName(); + if (!level) + return NULL; + + strcpy( sz, level ); + char *ch = strchr( sz, '.' ); + *ch = '\0'; + strcat( sz, ".txt" ); + + // pull out the map name + strcpy( m_sMapName, level ); + ch = strchr( m_sMapName, '.' ); + if ( ch ) + { + *ch = 0; + } + + ch = strchr( m_sMapName, '/' ); + if ( ch ) + { + // move the string back over the '/' + memmove( m_sMapName, ch+1, strlen(ch)+1 ); + } + } + + pfile = (char*)gEngfuncs.COM_LoadFile( sz, 5, NULL ); + + if (!pfile) + return NULL; + + cText = pfile; + + strncpy( cTitle, m_sMapName, MAX_TITLE_LENGTH ); + cTitle[MAX_TITLE_LENGTH-1] = 0; + } + else if ( iTextToShow == SHOW_CLASSDESC ) + { + switch ( g_iPlayerClass ) + { + case PC_SCOUT: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_scout" ); + CHudTextMessage::LocaliseTextString( "#Title_scout", cTitle, MAX_TITLE_LENGTH ); break; + case PC_SNIPER: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_sniper" ); + CHudTextMessage::LocaliseTextString( "#Title_sniper", cTitle, MAX_TITLE_LENGTH ); break; + case PC_SOLDIER: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_soldier" ); + CHudTextMessage::LocaliseTextString( "#Title_soldier", cTitle, MAX_TITLE_LENGTH ); break; + case PC_DEMOMAN: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_demoman" ); + CHudTextMessage::LocaliseTextString( "#Title_demoman", cTitle, MAX_TITLE_LENGTH ); break; + case PC_MEDIC: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_medic" ); + CHudTextMessage::LocaliseTextString( "#Title_medic", cTitle, MAX_TITLE_LENGTH ); break; + case PC_HVYWEAP: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_hwguy" ); + CHudTextMessage::LocaliseTextString( "#Title_hwguy", cTitle, MAX_TITLE_LENGTH ); break; + case PC_PYRO: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_pyro" ); + CHudTextMessage::LocaliseTextString( "#Title_pyro", cTitle, MAX_TITLE_LENGTH ); break; + case PC_SPY: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_spy" ); + CHudTextMessage::LocaliseTextString( "#Title_spy", cTitle, MAX_TITLE_LENGTH ); break; + case PC_ENGINEER: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_engineer" ); + CHudTextMessage::LocaliseTextString( "#Title_engineer", cTitle, MAX_TITLE_LENGTH ); break; + case PC_CIVILIAN: cText = CHudTextMessage::BufferedLocaliseTextString( "#Help_civilian" ); + CHudTextMessage::LocaliseTextString( "#Title_civilian", cTitle, MAX_TITLE_LENGTH ); break; + default: + return NULL; + } + + if ( g_iPlayerClass == PC_CIVILIAN ) + { + sprintf(sz, "classes/long_civilian.txt"); + } + else + { + sprintf(sz, "classes/long_%s.txt", sTFClassSelection[ g_iPlayerClass ]); + } + char *pfile = (char*)gEngfuncs.COM_LoadFile( sz, 5, NULL ); + if (pfile) + { + cText = pfile; + } + } + else if ( iTextToShow == SHOW_SPECHELP ) + { + CHudTextMessage::LocaliseTextString( "#Spec_Help_Title", cTitle, MAX_TITLE_LENGTH ); + cTitle[MAX_TITLE_LENGTH-1] = 0; + + char *pfile = CHudTextMessage::BufferedLocaliseTextString( "#Spec_Help_Text" ); + if ( pfile ) + { + cText = pfile; + } + } + + // if we're in the game (ie. have selected a class), flag the menu to be only grayed in the dialog box, instead of full screen + CMenuPanel *pMOTDPanel = CMessageWindowPanel_Create( cText, cTitle, g_iPlayerClass == PC_UNDEFINED, false, 0, 0, ScreenWidth(), ScreenHeight() ); + pMOTDPanel->setParent( this ); + + if ( pfile ) + gEngfuncs.COM_FreeFile( pfile ); + + return pMOTDPanel; +} + +char* TeamFortressViewport::GetTeamName( int iTeam ) +{ + return m_sTeamNames[iTeam]; +} + +//================================================================ +// VGUI Menus +void TeamFortressViewport::ShowVGUIMenu( int iMenu ) +{ + CMenuPanel *pNewMenu = NULL; + + gHUD.SetUsingVGUI(true); + + // Don't open menus in demo playback + if ( gEngfuncs.pDemoAPI->IsPlayingback() ) + return; + + // Don't open any menus except the MOTD during intermission + // MOTD needs to be accepted because it's sent down to the client + // after map change, before intermission's turned off + if ( gHUD.m_iIntermission && iMenu != MENU_INTRO ) + return; + + // Don't create one if it's already in the list + if (m_pCurrentMenu) + { + CMenuPanel *pMenu = m_pCurrentMenu; + while (pMenu != NULL) + { + if (pMenu->GetMenuID() == iMenu) + return; + pMenu = pMenu->GetNextMenu(); + } + } + + switch ( iMenu ) + { + case MENU_TEAM: + pNewMenu = ShowTeamMenu(); + break; + + // Map Briefing removed now that it appears in the team menu + case MENU_MAPBRIEFING: + pNewMenu = CreateTextWindow( SHOW_MAPBRIEFING ); + break; + + case MENU_INTRO: + pNewMenu = CreateTextWindow( SHOW_MOTD ); + break; + + case MENU_CLASSHELP: + pNewMenu = CreateTextWindow( SHOW_CLASSDESC ); + break; + + case MENU_SPECHELP: + pNewMenu = CreateTextWindow( SHOW_SPECHELP ); + break; + case MENU_CLASS: + pNewMenu = ShowClassMenu(); + break; + + default: + break; + } + + if (!pNewMenu) + return; + + // Close the Command Menu if it's open + HideCommandMenu(); + + pNewMenu->SetMenuID( iMenu ); + pNewMenu->SetActive( true ); + pNewMenu->setParent(this); + + // See if another menu is visible, and if so, cache this one for display once the other one's finished + if (m_pCurrentMenu) + { + if ( m_pCurrentMenu->GetMenuID() == MENU_CLASS && iMenu == MENU_TEAM ) + { + CMenuPanel *temp = m_pCurrentMenu; + m_pCurrentMenu->Close(); + m_pCurrentMenu = pNewMenu; + m_pCurrentMenu->SetNextMenu( temp ); + m_pCurrentMenu->Open(); + UpdateCursorState(); + } + else + { + m_pCurrentMenu->SetNextMenu( pNewMenu ); + } + } + else + { + m_pCurrentMenu = pNewMenu; + m_pCurrentMenu->Open(); + UpdateCursorState(); + } +} + +// Removes all VGUI Menu's onscreen +void TeamFortressViewport::HideVGUIMenu() +{ + while (m_pCurrentMenu) + { + HideTopMenu(); + } + gHUD.SetUsingVGUI(false); +} + +// Remove the top VGUI menu, and bring up the next one +void TeamFortressViewport::HideTopMenu() +{ + if (m_pCurrentMenu) + { + // Close the top one + m_pCurrentMenu->Close(); + + // Bring up the next one + gViewPort->SetCurrentMenu( m_pCurrentMenu->GetNextMenu() ); + } + + UpdateCursorState(); +} + +// Return TRUE if the HUD's allowed to print text messages +bool TeamFortressViewport::AllowedToPrintText( void ) +{ + // Prevent text messages when fullscreen menus are up + if ( m_pCurrentMenu && g_iPlayerClass == 0 ) + { + int iId = m_pCurrentMenu->GetMenuID(); + if ( iId == MENU_TEAM || iId == MENU_CLASS || iId == MENU_INTRO || iId == MENU_CLASSHELP ) + return FALSE; + } + + return TRUE; +} + +//====================================================================================== +// TEAM MENU +//====================================================================================== +// Bring up the Team selection Menu +CMenuPanel* TeamFortressViewport::ShowTeamMenu() +{ + // Don't open menus in demo playback + if ( gEngfuncs.pDemoAPI->IsPlayingback() ) + return NULL; + + m_pTeamMenu->Reset(); + return m_pTeamMenu; +} + +void TeamFortressViewport::CreateTeamMenu() +{ + // Create the panel + m_pTeamMenu = new CTeamMenuPanel(100, false, 0, 0, ScreenWidth(), ScreenHeight()); + m_pTeamMenu->setParent( this ); + m_pTeamMenu->setVisible( false ); +} + +//====================================================================================== +// CLASS MENU +//====================================================================================== +// Bring up the Class selection Menu +CMenuPanel* TeamFortressViewport::ShowClassMenu() +{ + // Don't open menus in demo playback + if ( gEngfuncs.pDemoAPI->IsPlayingback() ) + return NULL; + + m_pClassMenu->Reset(); + return m_pClassMenu; +} + +void TeamFortressViewport::CreateClassMenu() +{ + // Create the panel + m_pClassMenu = new CClassMenuPanel(100, false, 0, 0, ScreenWidth(), ScreenHeight()); + m_pClassMenu->setParent(this); + m_pClassMenu->setVisible( false ); +} + +//====================================================================================== +//====================================================================================== +// SPECTATOR MENU +//====================================================================================== +// Spectator "Menu" explaining the Spectator buttons +void TeamFortressViewport::CreateSpectatorMenu() +{ + // Create the Panel + m_pSpectatorPanel = new SpectatorPanel(0, 0, ScreenWidth(), ScreenHeight()); + m_pSpectatorPanel->setParent(this); + m_pSpectatorPanel->setVisible(false); + m_pSpectatorPanel->Initialize(); +} + +//====================================================================================== +// UPDATE HUD SECTIONS +//====================================================================================== +// We've got an update on player info +// Recalculate any menus that use it. +void TeamFortressViewport::UpdateOnPlayerInfo() +{ + if (m_pTeamMenu) + m_pTeamMenu->Update(); + if (m_pClassMenu) + m_pClassMenu->Update(); + if (m_pScoreBoard) + m_pScoreBoard->Update(); +} + +void TeamFortressViewport::UpdateCursorState() +{ + + if (gHUD.GetInTopDownMode() || m_pSpectatorPanel->isVisible() || GetClientVoiceMgr()->IsInSquelchMode()) + { + gHUD.GetManager().SetMouseVisibility(true); + } + else + { + + // Don't reset mouse in demo playback + if ( !gEngfuncs.pDemoAPI->IsPlayingback() ) + { + IN_ResetMouse(); + } + + gHUD.GetManager().SetMouseVisibility(false); + + } + + /* + if(!gHUD.GetInTopDownMode()) + { + // Need cursor if any VGUI window is up + if ( m_pSpectatorPanel->m_menuVisible || m_pCurrentMenu || m_pTeamMenu->isVisible() || m_pServerBrowser->isVisible() || GetClientVoiceMgr()->IsInSquelchMode() ) + { + gHUD.GetManager().SetMouseVisibility(true); + //g_iVisibleMouse = true; + //App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(Scheme::SchemeCursor::scu_arrow) ); + return; + } + else if ( m_pCurrentCommandMenu ) + { + // commandmenu doesn't have cursor if hud_capturemouse is turned off + if ( gHUD.m_pCvarStealMouse->value != 0.0f ) + { + gHUD.GetManager().SetMouseVisibility(true); + //g_iVisibleMouse = true; + //App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(Scheme::SchemeCursor::scu_arrow) ); + return; + } + } + + // Don't reset mouse in demo playback + if ( !gEngfuncs.pDemoAPI->IsPlayingback() ) + { + IN_ResetMouse(); + } + + gHUD.GetManager().SetMouseVisibility(false); + //g_iVisibleMouse = false; + //App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(Scheme::SchemeCursor::scu_none) ); + } + */ + +} + +void TeamFortressViewport::UpdateHighlights() +{ + if (m_pCurrentCommandMenu) + m_pCurrentCommandMenu->MakeVisible( NULL ); +} + +void TeamFortressViewport::GetAllPlayersInfo( void ) +{ + for ( int i = 1; i <= MAX_PLAYERS; i++ ) + { + GetPlayerInfo( i, &g_PlayerInfoList[i] ); + + if ( g_PlayerInfoList[i].thisplayer ) + m_pScoreBoard->m_iPlayerNum = i; // !!!HACK: this should be initialized elsewhere... maybe gotten from the engine + } +} + +void TeamFortressViewport::paintBackground() +{ + if (m_pScoreBoard) + { + int x, y; + getApp()->getCursorPos(x, y); + m_pScoreBoard->cursorMoved(x, y, m_pScoreBoard); + } + + // See if the command menu is visible and needs recalculating due to some external change + if ( g_iTeamNumber != m_iCurrentTeamNumber ) + { + UpdateCommandMenu( m_StandardMenu ); + + if ( m_pClassMenu ) + { + m_pClassMenu->Update(); + } + + m_iCurrentTeamNumber = g_iTeamNumber; + } + + if ( g_iPlayerClass != m_iCurrentPlayerClass ) + { + UpdateCommandMenu( m_StandardMenu ); + + m_iCurrentPlayerClass = g_iPlayerClass; + } + + // See if the Spectator Menu needs to be update + if ( ( g_iUser1 != m_iUser1 || g_iUser2 != m_iUser2 ) || + ( m_flSpectatorPanelLastUpdated < gHUD.m_flTime ) ) + { + UpdateSpectatorPanel(); + } + + // Update the Scoreboard, if it's visible + if ( m_pScoreBoard->isVisible() && (m_flScoreBoardLastUpdated < gHUD.m_flTime) ) + { + m_pScoreBoard->Update(); + m_flScoreBoardLastUpdated = gHUD.m_flTime + 0.5; + } + + int extents[4]; + getAbsExtents(extents[0],extents[1],extents[2],extents[3]); + VGui_ViewportPaintBackground(extents); +} + +//================================================================ +// Input Handler for Drag N Drop panels +void CDragNDropHandler::cursorMoved(int x,int y,Panel* panel) +{ + if(m_bDragging) + { + App::getInstance()->getCursorPos(x,y); + m_pPanel->setPos(m_iaDragOrgPos[0]+(x-m_iaDragStart[0]),m_iaDragOrgPos[1]+(y-m_iaDragStart[1])); + + if(m_pPanel->getParent()!=null) + { + m_pPanel->getParent()->repaint(); + } + } +} + +void CDragNDropHandler::mousePressed(MouseCode code,Panel* panel) +{ + int x,y; + App::getInstance()->getCursorPos(x,y); + m_bDragging=true; + m_iaDragStart[0]=x; + m_iaDragStart[1]=y; + m_pPanel->getPos(m_iaDragOrgPos[0],m_iaDragOrgPos[1]); + App::getInstance()->setMouseCapture(panel); + + m_pPanel->setDragged(m_bDragging); + m_pPanel->requestFocus(); +} + +void CDragNDropHandler::mouseReleased(MouseCode code,Panel* panel) +{ + m_bDragging=false; + m_pPanel->setDragged(m_bDragging); + App::getInstance()->setMouseCapture(null); +} + +//================================================================ +// Number Key Input +bool TeamFortressViewport::SlotInput( int iSlot ) +{ + // If there's a menu up, give it the input + if ( m_pCurrentMenu ) + return m_pCurrentMenu->SlotInput( iSlot ); + + if(gHUD.SlotInput(iSlot)) + { + return TRUE; + } + + return FALSE; +} + +// Direct Key Input +int TeamFortressViewport::KeyInput( int down, int keynum, const char *pszCurrentBinding ) +{ + + if (m_chatPanel->isVisible()) + { + // Don't let the game handle the input while the user is typing in the + // chat window. + return 0; + } + + if (down && pszCurrentBinding && !strcmp(pszCurrentBinding, kcGlobalChat)) + { + m_chatPanel->setVisible(true); + m_chatPanel->requestFocus(); + m_chatPanel->SetChatMode(ChatPanel::chatModeAll); + return 0; + } + else if (down && pszCurrentBinding && !strcmp(pszCurrentBinding, kcTeamChat)) + { + m_chatPanel->setVisible(true); + m_chatPanel->requestFocus(); + m_chatPanel->SetChatMode(ChatPanel::chatModeTeam); + return 0; + } + + // Open Text Window? + if (m_pCurrentMenu && gEngfuncs.Con_IsVisible() == false) + { + int iMenuID = m_pCurrentMenu->GetMenuID(); + + // Get number keys as Input for Team/Class menus + if (iMenuID == MENU_TEAM || iMenuID == MENU_CLASS) + { + // Escape gets you out of Team/Class menus if the Cancel button is visible + if ( keynum == K_ESCAPE ) + { + if ( (iMenuID == MENU_TEAM && g_iTeamNumber) || (iMenuID == MENU_CLASS && g_iPlayerClass) ) + { + HideTopMenu(); + return 0; + } + } + + for (int i = '0'; i <= '9'; i++) + { + if ( down && (keynum == i) ) + { + SlotInput( i - '0' ); + return 0; + } + } + } + + // Grab enter keys to close TextWindows + if ( down && (keynum == K_ENTER || keynum == K_KP_ENTER || keynum == K_SPACE || keynum == K_ESCAPE) ) + { + if ( iMenuID == MENU_MAPBRIEFING || iMenuID == MENU_INTRO || iMenuID == MENU_CLASSHELP ) + { + HideTopMenu(); + return 0; + } + } + + // Grab jump key on Team Menu as autoassign + if ( pszCurrentBinding && down && !strcmp(pszCurrentBinding, "+jump") ) + { + if (iMenuID == MENU_TEAM) + { + m_pTeamMenu->SlotInput(5); + return 0; + } + } + + } + + // if we're in a command menu, try hit one of it's buttons + if ( down && m_pCurrentCommandMenu ) + { + // Escape hides the command menu + if ( keynum == K_ESCAPE ) + { + HideCommandMenu(); + return 0; + } + + // only trap the number keys + if ( keynum >= '0' && keynum <= '9' ) + { + if ( m_pCurrentCommandMenu->KeyInput(keynum) ) + { + // a final command has been issued, so close the command menu + HideCommandMenu(); + } + + return 0; + } + } + + return 1; +} + +ChatPanel* TeamFortressViewport::GetChatPanel() +{ + return m_chatPanel; +} + +//================================================================ +// Message Handlers +int TeamFortressViewport::MsgFunc_TeamNames(const char *pszName, int iSize, void *pbuf ) +{ + StringList team_names; + NetMsg_TeamNames( pbuf, iSize, team_names ); + + char team_translated[128]; + strcpy(team_translated, gHUD.m_TextMessage.LookupString(kTeamTitle)); + + char team_name_translated[MAX_TEAMNAME_SIZE]; + for( int team_number = 0; team_number < team_names.size(); team_number++ ) + { + gHUD.m_TextMessage.LocaliseTextString( team_names[team_number].c_str(), team_name_translated, MAX_TEAMNAME_SIZE ); + + team_names[team_number] = ""; + if( team_number != 0 && team_number < (m_iNumberOfTeams-1) ) //don't prepend information for spectators or team 0 + { + team_names[team_number] += team_translated; + team_names[team_number] += " "; + team_names[team_number] += MakeStringFromInt( team_number ); + team_names[team_number] += ": "; + } + team_names[team_number] += team_name_translated; + strcpy(m_sTeamNames[team_number], team_names[team_number].c_str()); + } + + return 1; +} + +int TeamFortressViewport::MsgFunc_MOTD( const char *pszName, int iSize, void *pbuf ) +{ + if (m_iGotAllMOTD) + m_szMOTD[0] = 0; + + string MOTD; + bool is_finished; + NetMsg_MOTD( pbuf, iSize, is_finished, MOTD ); + + m_iGotAllMOTD = is_finished ? 1 : 0; + + int roomInArray = (int)max( 0, sizeof(m_szMOTD) - strlen(m_szMOTD) - 1); + + strncat( m_szMOTD, MOTD.c_str(), roomInArray ); + m_szMOTD[ sizeof(m_szMOTD)-1 ] = '\0'; + + return 1; +} + +int TeamFortressViewport::MsgFunc_ServerName( const char *pszName, int iSize, void *pbuf ) +{ + string name; + NetMsg_ServerName( pbuf, iSize, name ); + + memset(this->m_szServerName, 0, MAX_SERVERNAME_LENGTH); + + strncpy( m_szServerName, name.c_str(), MAX_SERVERNAME_LENGTH ); + + return 1; +} + +int TeamFortressViewport::MsgFunc_ScoreInfo( const char *pszName, int iSize, void *pbuf ) +{ + ScoreInfo info; + NetMsg_ScoreInfo( pbuf, iSize, info ); + + if ( info.player_index > 0 && info.player_index <= MAX_PLAYERS ) + { + // Update score, but show + or - indicator on scoreboard when it changes + g_PlayerExtraInfo[info.player_index].lastScore = g_PlayerExtraInfo[info.player_index].score; + g_PlayerExtraInfo[info.player_index].score = info.score; + if(g_PlayerExtraInfo[info.player_index].score != g_PlayerExtraInfo[info.player_index].lastScore) + { + g_PlayerExtraInfo[info.player_index].timeOfLastScoreChange = gHUD.GetTimeOfLastUpdate(); + } + + // Update other info + g_PlayerExtraInfo[info.player_index].frags = info.frags; + g_PlayerExtraInfo[info.player_index].deaths = info.deaths; + g_PlayerExtraInfo[info.player_index].extra = info.extra; + g_PlayerExtraInfo[info.player_index].playerclass = info.player_class; + // : 0001073 + g_PlayerExtraInfo[info.player_index].auth = info.auth; + g_PlayerExtraInfo[info.player_index].teamnumber = max( info.team, 0 ); + g_PlayerExtraInfo[info.player_index].health = info.health; + + // Icon is now handled through the ProfileInfo update + + UpdateOnPlayerInfo(); + } + + return 1; +} + +// Message handler for TeamScore message +// accepts three values: +// string: team name +// short: teams kills +// short: teams deaths +// if this message is never received, then scores will simply be the combined totals of the players. +int TeamFortressViewport::MsgFunc_TeamScore( const char *pszName, int iSize, void *pbuf ) +{ + string team_name; + int score, reset; + NetMsg_TeamScore( pbuf, iSize, team_name, score, reset); + + // find the team matching the name + for ( int i = 1; i <= m_pScoreBoard->m_iNumTeams; i++ ) + { + if ( !stricmp( team_name.c_str(), g_TeamInfo[i].name ) ) + break; + } + + if ( i > m_pScoreBoard->m_iNumTeams ) + return 1; + + // use this new score data instead of combined player scores + if ( reset ) + g_TeamInfo[i]. scores_overriden = FALSE; + else + g_TeamInfo[i]. scores_overriden = TRUE; + + g_TeamInfo[i].score = score; + + return 1; +} + +// Message handler for TeamInfo message +// accepts two values: +// byte: client number +// string: client team name +int TeamFortressViewport::MsgFunc_TeamInfo( const char *pszName, int iSize, void *pbuf ) +{ + if (!m_pScoreBoard) + return 1; + + int player_index; + string team_id; + NetMsg_TeamInfo( pbuf, iSize, player_index, team_id ); + + if ( player_index > 0 && player_index <= MAX_PLAYERS ) + { + // set the players team + strncpy( g_PlayerExtraInfo[player_index].teamname, team_id.c_str(), MAX_TEAM_NAME ); + } + + // rebuild the list of teams + m_pScoreBoard->RebuildTeams(); + + return 1; +} + +void TeamFortressViewport::DeathMsg( int killer, int victim ) +{ + m_pScoreBoard->DeathMsg(killer,victim); +} diff --git a/main/source/cl_dll/view.cpp b/main/source/cl_dll/view.cpp index 0b93361..9259f41 100644 --- a/main/source/cl_dll/view.cpp +++ b/main/source/cl_dll/view.cpp @@ -7,6 +7,7 @@ // view/refresh setup functions + #include "hud.h" #include "cl_util.h" #include "common/cvardef.h" @@ -20,6 +21,7 @@ #include "pm_shared/pm_movevars.h" #include "pm_shared/pm_shared.h" #include "pm_shared/pm_defs.h" +#include "pm_shared/pm_debug.h" #include "common/event_api.h" #include "common/pmtrace.h" #include "common/screenfade.h" @@ -41,8 +43,8 @@ extern float gTopDownViewAngles[3]; #ifndef M_PI #define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h #endif - -void PM_ParticleLine( float *start, float *end, int pcolor, float life, float vert); +//#include "pm_shared/pm_debug.h" +//void PM_ParticleLine(vec3_t start, vec3_t end, int pcolor, float life, float vert); int PM_GetVisEntInfo( int ent ); int PM_GetPhysEntInfo( int ent ); void InterpolateAngles( float *start, float *end, float *output, float frac ); @@ -2385,7 +2387,32 @@ float CalcFov (float fov_x, float width, float height) } int hitent = -1; +extern playermove_t *pmove; +void PM_ParticleLine(vec3_t start, vec3_t end, int pcolor, float life, float vert) +{ + float linestep = 2.0f; + float curdist; + float len; + vec3_t curpos; + vec3_t diff; + int i; + // Determine distance; + VectorSubtract(end, start, diff); + + len = VectorNormalize(diff); + + curdist = 0; + while (curdist <= len) + { + for (i = 0; i < 3; i++) + curpos[i] = start[i] + curdist * diff[i]; + + pmove->PM_Particle( curpos, pcolor, life, 0, vert); + curdist += linestep; + } + +} void V_Move( int mx, int my ) { float fov; @@ -2399,10 +2426,10 @@ void V_Move( int mx, int my ) vec3_t farpoint; pmtrace_t tr; - fov = CalcFov( in_fov, (float)ScreenWidth, (float)ScreenHeight ); + fov = CalcFov( in_fov, (float)ScreenWidth(), (float)ScreenHeight() ); - c_x = (float)ScreenWidth / 2.0; - c_y = (float)ScreenHeight / 2.0; + c_x = (float)ScreenWidth() / 2.0; + c_y = (float)ScreenHeight() / 2.0; dx = (float)mx - c_x; dy = (float)my - c_y; @@ -2430,7 +2457,7 @@ void V_Move( int mx, int my ) if ( tr.fraction != 1.0 && tr.ent != 0 ) { hitent = PM_GetPhysEntInfo( tr.ent ); - PM_ParticleLine( (float *)&v_origin, (float *)&tr.endpos, 5, 1.0, 0.0 ); + PM_ParticleLine( (vec3_t)v_origin, (vec3_t)tr.endpos, 5, 1.0, 0.0 ); } else { diff --git a/main/source/dlls/bmodels.cpp b/main/source/dlls/bmodels.cpp index e97c53d..904daf7 100644 --- a/main/source/dlls/bmodels.cpp +++ b/main/source/dlls/bmodels.cpp @@ -25,6 +25,7 @@ #include "cbase.h" #include "doors.h" #include "mod/AvHSpecials.h" +#include "mod/AvHServerVariables.h" extern DLL_GLOBAL Vector g_vecAttackDir; @@ -255,7 +256,7 @@ LINK_ENTITY_TO_CLASS( func_monsterclip, CFuncMonsterClip ); void CFuncMonsterClip::Spawn( void ) { CFuncWall::Spawn(); - if ( CVAR_GET_FLOAT("showtriggers") == 0 ) + if ( ns_cvar_float(showtriggers) == 0 ) pev->effects = EF_NODRAW; pev->flags |= FL_MONSTERCLIP; } diff --git a/main/source/dlls/cbase.h b/main/source/dlls/cbase.h index c47ecd5..7470be8 100644 --- a/main/source/dlls/cbase.h +++ b/main/source/dlls/cbase.h @@ -1,834 +1,834 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -/* - -Class Hierachy - -CBaseEntity - CBaseDelay - CBaseToggle - CBaseItem - CBaseMonster - CBaseCycler - CBasePlayer - CBaseGroup -*/ -#ifndef CBASE_H -#define CBASE_H - -#include "common/damagetypes.h" -#include "util/Checksum.h" -#include "types.h" - -#define MAX_PATH_SIZE 10 // max number of nodes available for a path. - -// These are caps bits to indicate what an object's capabilities (currently used for save/restore and level transitions) -#define FCAP_CUSTOMSAVE 0x00000001 -#define FCAP_ACROSS_TRANSITION 0x00000002 // should transfer between transitions -#define FCAP_MUST_SPAWN 0x00000004 // Spawn after restore -#define FCAP_DONT_SAVE 0x80000000 // Don't save this -#define FCAP_IMPULSE_USE 0x00000008 // can be used by the player -#define FCAP_CONTINUOUS_USE 0x00000010 // can be used by the player -#define FCAP_ONOFF_USE 0x00000020 // can be used by the player -#define FCAP_DIRECTIONAL_USE 0x00000040 // Player sends +/- 1 when using (currently only tracktrains) -#define FCAP_MASTER 0x00000080 // Can be used to "master" other entities (like multisource) - -// UNDONE: This will ignore transition volumes (trigger_transition), but not the PVS!!! -#define FCAP_FORCE_TRANSITION 0x00000080 // ALWAYS goes across transitions - -#include "saverestore.h" -#include "schedule.h" - -#ifndef MONSTEREVENT_H -#include "monsterevent.h" -#endif - -// C functions for external declarations that call the appropriate C++ methods - -#ifdef _WIN32 -#define EXPORT _declspec( dllexport ) -#else -#define EXPORT /* */ -#endif - -extern "C" EXPORT int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ); -extern "C" EXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ); - -extern int DispatchSpawn( edict_t *pent ); -extern void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd ); -extern void DispatchTouch( edict_t *pentTouched, edict_t *pentOther ); -extern void DispatchUse( edict_t *pentUsed, edict_t *pentOther ); -extern void DispatchThink( edict_t *pent ); -extern void DispatchBlocked( edict_t *pentBlocked, edict_t *pentOther ); -extern void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData ); -extern int DispatchRestore( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ); -extern void DispatchObjectCollsionBox( edict_t *pent ); -extern void SaveWriteFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); -extern void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); -extern void SaveGlobalState( SAVERESTOREDATA *pSaveData ); -extern void RestoreGlobalState( SAVERESTOREDATA *pSaveData ); -extern void ResetGlobalState( void ); - -typedef enum { USE_OFF = 0, USE_ON = 1, USE_SET = 2, USE_TOGGLE = 3 } USE_TYPE; - -extern void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - -typedef void (CBaseEntity::*BASEPTR)(void); -typedef void (CBaseEntity::*ENTITYFUNCPTR)(CBaseEntity *pOther ); -typedef void (CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - -// For CLASSIFY -#define CLASS_NONE 0 -#define CLASS_MACHINE 1 -#define CLASS_PLAYER 2 -#define CLASS_HUMAN_PASSIVE 3 -#define CLASS_HUMAN_MILITARY 4 -#define CLASS_ALIEN_MILITARY 5 -#define CLASS_ALIEN_PASSIVE 6 -#define CLASS_ALIEN_MONSTER 7 -#define CLASS_ALIEN_PREY 8 -#define CLASS_ALIEN_PREDATOR 9 -#define CLASS_INSECT 10 -#define CLASS_PLAYER_ALLY 11 -#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players -#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace -#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. - -class CBaseEntity; -class CBaseMonster; -class CBasePlayerItem; -class CSquadMonster; - - -#define SF_NORESPAWN ( 1 << 30 )// !!!set this bit on guns and stuff that should never respawn. - -// -// EHANDLE. Safe way to point to CBaseEntities who may die between frames -// -class EHANDLE -{ -private: - edict_t *m_pent; - int m_serialnumber; -public: - edict_t *Get( void ); - edict_t *Set( edict_t *pent ); - - operator int (); - - operator CBaseEntity *(); - - CBaseEntity * operator = (CBaseEntity *pEntity); - CBaseEntity * operator ->(); -}; - - -// -// Base Entity. All entity types derive from this -// -class CBaseEntity -{ -public: - // Constructor. Set engine to use C/C++ callback functions - // pointers to engine data - entvars_t *pev; // Don't need to save/restore this pointer, the engine resets it - - // path corners - CBaseEntity *m_pGoalEnt;// path corner we are heading towards - CBaseEntity *m_pLink;// used for temporary link-list operations. - - // initialization functions - virtual void Spawn( void ) { return; } - virtual void Precache( void ) { return; } - virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; } - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - virtual int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; } - virtual void Activate( void ) {} - - // Setup the object->object collision box (pev->mins / pev->maxs is the object->world collision box) - virtual void SetObjectCollisionBox( void ); - -// Classify - returns the type of group (i.e, "houndeye", or "human military" so that monsters with different classnames -// still realize that they are teammates. (overridden for monsters that form groups) - virtual int Classify ( void ) { return CLASS_NONE; }; - virtual void DeathNotice ( entvars_t *pevChild ) {}// monster maker children use this to tell the monster maker that they have died. - - - static TYPEDESCRIPTION m_SaveData[]; - - virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - virtual int TakeHealth( float flHealth, int bitsDamageType ); - virtual int GetPointValue(void) const { return 0; } - virtual void Killed( entvars_t *pevAttacker, int iGib ); - virtual void AwardKill( entvars_t* pevTarget) {} - virtual int BloodColor( void ) { return DONT_BLEED; } - virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); - virtual BOOL IsTriggered( CBaseEntity *pActivator ) {return TRUE;} - virtual CBaseMonster *MyMonsterPointer( void ) { return NULL;} - virtual CSquadMonster *MySquadMonsterPointer( void ) { return NULL;} - virtual int GetToggleState( void ) { return TS_AT_TOP; } - virtual void AddPoints( int score, BOOL bAllowNegativeScore ) {} - virtual void AddPointsToTeam( int score, BOOL bAllowNegativeScore ) {} - virtual BOOL AddPlayerItem( CBasePlayerItem *pItem ) { return 0; } - virtual BOOL RemovePlayerItem( CBasePlayerItem *pItem ) { return 0; } - virtual int GiveAmmo( int iAmount, char *szName, int iMax ) { return -1; }; - virtual float GetDelay( void ) { return 0; } - virtual int IsMoving( void ) { return pev->velocity != g_vecZero; } - virtual void OverrideReset( void ) {} - virtual int DamageDecal( int bitsDamageType ); - // This is ONLY used by the node graph to test movement through a door - virtual void SetToggleState( int state ) {} - virtual void StartSneaking( void ) {} - virtual void StopSneaking( void ) {} - virtual BOOL OnControls( entvars_t *pev ) { return FALSE; } - virtual BOOL IsSneaking( void ) { return FALSE; } - virtual BOOL IsAlive( void ) const { return (pev->deadflag == DEAD_NO) && pev->health > 0; } - virtual BOOL IsBSPModel( void ) { return pev->solid == SOLID_BSP || pev->movetype == MOVETYPE_PUSHSTEP; } - virtual BOOL ReflectGauss( void ) { return ( IsBSPModel() && !pev->takedamage ); } - virtual BOOL HasTarget( string_t targetname ) { return FStrEq(STRING(targetname), STRING(pev->targetname) ); } - virtual BOOL IsInWorld( void ); - virtual BOOL IsPlayer( void ) { return FALSE; } - virtual BOOL IsNetClient( void ) { return FALSE; } - virtual char* TeamID( void ) { return ""; } - - // Added to allow resetting of entities when a game starts - virtual void AddChecksum(Checksum& inChecksum); - virtual void ResetEntity(void) {} - -// virtual void SetActivator( CBaseEntity *pActivator ) {} - virtual CBaseEntity *GetNextTarget( void ); - - // fundamental callbacks - void (CBaseEntity ::*m_pfnThink)(void); - void (CBaseEntity ::*m_pfnTouch)( CBaseEntity *pOther ); - void (CBaseEntity ::*m_pfnUse)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void (CBaseEntity ::*m_pfnBlocked)( CBaseEntity *pOther ); - - virtual void Think( void ) { if (m_pfnThink) (this->*m_pfnThink)(); }; - virtual void Touch( CBaseEntity *pOther ) { if (m_pfnTouch) (this->*m_pfnTouch)( pOther ); }; - virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) - { - if (m_pfnUse) - (this->*m_pfnUse)( pActivator, pCaller, useType, value ); - } - virtual void Blocked( CBaseEntity *pOther ) { if (m_pfnBlocked) (this->*m_pfnBlocked)( pOther ); }; - - // allow engine to allocate instance data - void *operator new( size_t stAllocateBlock, entvars_t *pev ) - { - return (void *)ALLOC_PRIVATE(ENT(pev), (int)stAllocateBlock); - }; - - // don't use this. -#if _MSC_VER >= 1200 // only build this code if MSVC++ 6.0 or higher - void operator delete(void *pMem, entvars_t *pev) - { - pev->flags |= FL_KILLME; - }; -#endif - - virtual void UpdateOnRemove( void ); - - // common member functions - void EXPORT SUB_Remove( void ); - void EXPORT SUB_DoNothing( void ); - void EXPORT SUB_StartFadeOut ( void ); - void EXPORT SUB_FadeOut ( void ); - void EXPORT SUB_CallUseToggle( void ) { this->Use( this, this, USE_TOGGLE, 0 ); } - int ShouldToggle( USE_TYPE useType, BOOL currentState ); - void FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL, int inDamageType = DMG_BULLET | DMG_NEVERGIB); - Vector FireBulletsPlayer( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL, int shared_rand = 0 ); - - virtual CBaseEntity *Respawn( void ) { return NULL; } - - void SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ); - // Do the bounding boxes of these two intersect? - int Intersects( CBaseEntity *pOther ); - void MakeDormant( void ); - int IsDormant( void ); - BOOL IsLockedByMaster( void ) { return FALSE; } - - static CBaseEntity *Instance( edict_t *pent ) - { - if ( !pent ) - pent = ENT(0); - CBaseEntity *pEnt = (CBaseEntity *)GET_PRIVATE(pent); - return pEnt; - } - - static CBaseEntity *Instance( entvars_t *pev ) { return Instance( ENT( pev ) ); } - static CBaseEntity *Instance( int eoffset) { return Instance( ENT( eoffset) ); } - - CBaseMonster *GetMonsterPointer( entvars_t *pevMonster ) - { - CBaseEntity *pEntity = Instance( pevMonster ); - if ( pEntity ) - return pEntity->MyMonsterPointer(); - return NULL; - } - CBaseMonster *GetMonsterPointer( edict_t *pentMonster ) - { - CBaseEntity *pEntity = Instance( pentMonster ); - if ( pEntity ) - return pEntity->MyMonsterPointer(); - return NULL; - } - - - // Ugly code to lookup all functions to make sure they are exported when set. -#ifdef _DEBUG - void FunctionCheck( void *pFunction, char *name ) - { -//#ifdef _WIN32 -// if (pFunction && !NAME_FOR_FUNCTION((unsigned long)(pFunction)) ) -// ALERT( at_error, "No EXPORT: %s:%s (%08lx)\n", STRING(pev->classname), name, (unsigned long)pFunction ); -//#endif // _WIN32 - } - -#pragma warning(push) -#pragma warning(disable: 312) - - BASEPTR ThinkSet( BASEPTR func, char *name ) - { - m_pfnThink = func; - FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnThink)))), name ); - return func; - } - ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name ) - { - m_pfnTouch = func; - FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnTouch)))), name ); - return func; - } - USEPTR UseSet( USEPTR func, char *name ) - { - m_pfnUse = func; - FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnUse)))), name ); - return func; - } - ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, char *name ) - { - m_pfnBlocked = func; - FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnBlocked)))), name ); - return func; - } - -#pragma - -#endif - - - // virtual functions used by a few classes - - // used by monsters that are created by the MonsterMaker - virtual void UpdateOwner( void ) { return; }; - - - // - static CBaseEntity *Create( const char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner = NULL ); - - virtual BOOL FBecomeProne( void ) {return FALSE;}; - edict_t *edict() { return ENT( pev ); }; - EOFFSET eoffset( ) { return OFFSET( pev ); }; - int entindex( ) { return ENTINDEX( edict() ); }; - - virtual Vector Center( ) { return (pev->absmax + pev->absmin) * 0.5; }; // center point of entity - virtual Vector EyePosition( ) { return pev->origin + pev->view_ofs; }; // position of eyes - virtual Vector EarPosition( ) { return pev->origin + pev->view_ofs; }; // position of ears - virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ); }; // position to shoot at - - virtual int Illumination( ) { return GETENTITYILLUM( ENT( pev ) ); }; - - virtual BOOL FVisible ( CBaseEntity *pEntity ); - virtual BOOL FVisible ( const Vector &vecOrigin ); - - //We use this variables to store each ammo count. - int ammo_9mm; - int ammo_357; - int ammo_bolts; - int ammo_buckshot; - int ammo_rockets; - int ammo_uranium; - int ammo_hornets; - int ammo_argrens; - //Special stuff for grenades and satchels. - float m_flStartThrow; - float m_flReleaseThrow; - int m_chargeReady; - int m_fInAttack; - - enum EGON_FIRESTATE { FIRE_OFF, FIRE_CHARGE }; - int m_fireState; -}; - - - -// Ugly technique to override base member functions -// Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a -// member function of a base class. static_cast is a sleezy way around that problem. - -#ifdef _DEBUG - -#define SetThink( a ) ThinkSet( static_cast (a), #a ) -#define SetTouch( a ) TouchSet( static_cast (a), #a ) -#define SetUse( a ) UseSet( static_cast (a), #a ) -#define SetBlocked( a ) BlockedSet( static_cast (a), #a ) - -#else - -#define SetThink( a ) m_pfnThink = static_cast (a) -#define SetTouch( a ) m_pfnTouch = static_cast (a) -#define SetUse( a ) m_pfnUse = static_cast (a) -#define SetBlocked( a ) m_pfnBlocked = static_cast (a) - -#endif - - -class CPointEntity : public CBaseEntity -{ -public: - void Spawn( void ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } -private: -}; - - -typedef struct locksounds // sounds that doors and buttons make when locked/unlocked -{ - string_t sLockedSound; // sound a door makes when it's locked - string_t sLockedSentence; // sentence group played when door is locked - string_t sUnlockedSound; // sound a door makes when it's unlocked - string_t sUnlockedSentence; // sentence group played when door is unlocked - - int iLockedSentence; // which sentence in sentence group to play next - int iUnlockedSentence; // which sentence in sentence group to play next - - float flwaitSound; // time delay between playing consecutive 'locked/unlocked' sounds - float flwaitSentence; // time delay between playing consecutive sentences - BYTE bEOFLocked; // true if hit end of list of locked sentences - BYTE bEOFUnlocked; // true if hit end of list of unlocked sentences -} locksound_t; - -void PlayLockSounds(entvars_t *pev, locksound_t *pls, int flocked, int fbutton); - -// -// MultiSouce -// - -#define MAX_MULTI_TARGETS 16 // maximum number of targets a single multi_manager entity may be assigned. -#define MS_MAX_TARGETS 32 - -class CMultiSource : public CPointEntity -{ -public: - void Spawn( ); - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - int ObjectCaps( void ) { return (CPointEntity::ObjectCaps() | FCAP_MASTER); } - BOOL IsTriggered( CBaseEntity *pActivator ); - void EXPORT Register( void ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - EHANDLE m_rgEntities[MS_MAX_TARGETS]; - int m_rgTriggered[MS_MAX_TARGETS]; - - int m_iTotal; - string_t m_globalstate; -}; - - -// -// generic Delay entity. -// -class CBaseDelay : public CBaseEntity -{ -public: - float m_flDelay; - int m_iszKillTarget; - - virtual void KeyValue( KeyValueData* pkvd); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - // common member functions - void SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ); - void EXPORT DelayThink( void ); -}; - - -class CBaseAnimating : public CBaseDelay -{ -public: - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - // Basic Monster Animation functions - float StudioFrameAdvance( float flInterval = 0.0 ); // accumulate animation frame time from last time called until now - int GetSequenceFlags( void ); - int LookupActivity ( int activity ); - int LookupActivityHeaviest ( int activity ); - int LookupSequence ( const char *label, int queue = 0 ); - const char* LookupSequence(int inSequence); - void ResetSequenceInfo ( ); - void DispatchAnimEvents ( float flFutureInterval = 0.1 ); // Handle events that have happend since last time called up until X seconds into the future - virtual void HandleAnimEvent( MonsterEvent_t *pEvent ) { return; }; - virtual float SetBoneController ( int iController, float flValue ); - void InitBoneControllers ( void ); - float SetBlending ( int iBlender, float flValue ); - void GetBonePosition ( int iBone, Vector &origin, Vector &angles ); - void GetAutomovement( Vector &origin, Vector &angles, float flInterval = 0.1 ); - int FindTransition( int iEndingSequence, int iGoalSequence, int *piDir ); - void GetAttachment ( int iAttachment, Vector &origin, Vector &angles ); - void SetBodygroup( int iGroup, int iValue ); - int GetBodygroup( int iGroup ); - int ExtractBbox( int sequence, float *mins, float *maxs ); - void SetSequenceBox( void ); - - // animation needs - float m_flFrameRate; // computed FPS for current sequence - float m_flGroundSpeed; // computed linear movement rate for current sequence - float m_flLastEventCheck; // last time the event list was checked - BOOL m_fSequenceFinished;// flag set when StudioAdvanceFrame moves across a frame boundry - BOOL m_fSequenceLoops; // true if the sequence loops - - // For performance gain during LookupSequence - char mPreviousLookupString[3][64]; - int mPreviousLookupSequence[3]; -}; - - -// -// generic Toggle entity. -// -#define SF_ITEM_USE_ONLY 256 // ITEM_USE_ONLY = BUTTON_USE_ONLY = DOOR_USE_ONLY!!! - -class CBaseToggle : public CBaseAnimating -{ -public: - void KeyValue( KeyValueData *pkvd ); - - TOGGLE_STATE m_toggle_state; - float m_flActivateFinished;//like attack_finished, but for doors - float m_flMoveDistance;// how far a door should slide or rotate - float m_flWait; - float m_flLip; - float m_flTWidth;// for plats - float m_flTLength;// for plats - - Vector m_vecPosition1; - Vector m_vecPosition2; - Vector m_vecAngle1; - Vector m_vecAngle2; - - int m_cTriggersLeft; // trigger_counter only, # of activations remaining - float m_flHeight; - EHANDLE m_hActivator; - void (CBaseToggle::*m_pfnCallWhenMoveDone)(void); - Vector m_vecFinalDest; - Vector m_vecFinalAngle; - - int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - virtual int GetToggleState( void ) { return m_toggle_state; } - virtual float GetDelay( void ) { return m_flWait; } - - // common member functions - void LinearMove( Vector vecDest, float flSpeed ); - void EXPORT LinearMoveDone( void ); - void AngularMove( Vector vecDestAngle, float flSpeed ); - void EXPORT AngularMoveDone( void ); - BOOL IsLockedByMaster( void ); - - virtual void SaveDataForReset(); - virtual void ResetEntity(); - - static float AxisValue( int flags, const Vector &angles ); - static void AxisDir( entvars_t *pev ); - static float AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ); - - string_t m_sMaster; // If this button has a master switch, this is the targetname. - // A master switch must be of the multisource type. If all - // of the switches in the multisource have been triggered, then - // the button will be allowed to operate. Otherwise, it will be - // deactivated. -private: - vec3_t mSavedOrigin; - float mSavedWait; - float mSavedLip; -}; -#define SetMoveDone( a ) m_pfnCallWhenMoveDone = static_cast (a) - - -// people gib if their health is <= this at the time of death -#define GIB_HEALTH_VALUE -30 - -#define ROUTE_SIZE 8 // how many waypoints a monster can store at one time -#define MAX_OLD_ENEMIES 4 // how many old enemies to remember - -#define bits_CAP_DUCK ( 1 << 0 )// crouch -#define bits_CAP_JUMP ( 1 << 1 )// jump/leap -#define bits_CAP_STRAFE ( 1 << 2 )// strafe ( walk/run sideways) -#define bits_CAP_SQUAD ( 1 << 3 )// can form squads -#define bits_CAP_SWIM ( 1 << 4 )// proficiently navigate in water -#define bits_CAP_CLIMB ( 1 << 5 )// climb ladders/ropes -#define bits_CAP_USE ( 1 << 6 )// open doors/push buttons/pull levers -#define bits_CAP_HEAR ( 1 << 7 )// can hear forced sounds -#define bits_CAP_AUTO_DOORS ( 1 << 8 )// can trigger auto doors -#define bits_CAP_OPEN_DOORS ( 1 << 9 )// can open manual doors -#define bits_CAP_TURN_HEAD ( 1 << 10)// can turn head, always bone controller 0 - -#define bits_CAP_RANGE_ATTACK1 ( 1 << 11)// can do a range attack 1 -#define bits_CAP_RANGE_ATTACK2 ( 1 << 12)// can do a range attack 2 -#define bits_CAP_MELEE_ATTACK1 ( 1 << 13)// can do a melee attack 1 -#define bits_CAP_MELEE_ATTACK2 ( 1 << 14)// can do a melee attack 2 - -#define bits_CAP_FLY ( 1 << 15)// can fly, move all around - -#define bits_CAP_DOORS_GROUP (bits_CAP_USE | bits_CAP_AUTO_DOORS | bits_CAP_OPEN_DOORS) - -// used by suit voice to indicate damage sustained and repaired type to player - -// instant damage - -// time-based damage -//#define DMG_TIMEBASED (~(0x3fff)) // mask for time-based damage -#include "common/damagetypes.h" - -#define DMG_PARALYZE (1 << 15) // slows affected creature down -#define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad -#define DMG_POISON (1 << 17) // blood poisioning -#define DMG_RADIATION (1 << 18) // radiation exposure -#define DMG_DROWNRECOVER (1 << 19) // drowning recovery -#define DMG_ACID (1 << 20) // toxic chemicals or acid burns -#define DMG_SLOWBURN (1 << 21) // in an oven -#define DMG_SLOWFREEZE (1 << 22) // in a subzero freezer -#define DMG_MORTAR (1 << 23) // Hit by air raid (done to distinguish grenade from mortar) - -// these are the damage types that are allowed to gib corpses -#define DMG_GIB_CORPSE ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB ) - -// these are the damage types that have client hud art -#define DMG_SHOWNHUD (DMG_POISON | /*DMG_ACID |*/ DMG_FREEZE | DMG_SLOWFREEZE /*| DMG_DROWN*/ | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION /*| DMG_SHOCK*/) - -// NOTE: tweak these values based on gameplay feedback: - -#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage -#define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval - -#define NERVEGAS_DURATION 2 -#define NERVEGAS_DAMAGE 5.0 - -#define POISON_DURATION 5 -#define POISON_DAMAGE 2.0 - -#define RADIATION_DURATION 2 -#define RADIATION_DAMAGE 1.0 - -#define ACID_DURATION 2 -#define ACID_DAMAGE 5.0 - -#define SLOWBURN_DURATION 2 -#define SLOWBURN_DAMAGE 1.0 - -#define SLOWFREEZE_DURATION 2 -#define SLOWFREEZE_DAMAGE 1.0 - - -#define itbd_Paralyze 0 -#define itbd_NerveGas 1 -#define itbd_Poison 2 -#define itbd_Radiation 3 -#define itbd_DrownRecover 4 -#define itbd_Acid 5 -#define itbd_SlowBurn 6 -#define itbd_SlowFreeze 7 -#define CDMG_TIMEBASED 8 - -// when calling KILLED(), a value that governs gib behavior is expected to be -// one of these three values -#define GIB_NORMAL 0// gib if entity was overkilled -#define GIB_NEVER 1// never gib, no matter how much death damage is done ( freezing, etc ) -#define GIB_ALWAYS 2// always gib ( Houndeye Shock, Barnacle Bite ) -// puzl: 980 -// Use gib paramater to control death of recycled buildings -#define GIB_RECYCLED 3// always gib ( Houndeye Shock, Barnacle Bite ) -// :puzl - -class CBaseMonster; -class CCineMonster; -class CSound; - -#include "basemonster.h" - - -char *ButtonSound( int sound ); // get string of button sound number - - -// -// Generic Button -// -class CBaseButton : public CBaseToggle -{ -public: - void Spawn( void ); - virtual void Precache( void ); - void RotSpawn( void ); - virtual void KeyValue( KeyValueData* pkvd); - - void ButtonActivate( ); - void SparkSoundCache( void ); - - void EXPORT ButtonShot( void ); - void EXPORT ButtonTouch( CBaseEntity *pOther ); - void EXPORT ButtonSpark ( void ); - void EXPORT TriggerAndWait( void ); - void EXPORT ButtonReturn( void ); - void EXPORT ButtonBackHome( void ); - void EXPORT ButtonUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - virtual void SaveDataForReset(); - virtual void ResetEntity(); - - enum BUTTON_CODE { BUTTON_NOTHING, BUTTON_ACTIVATE, BUTTON_RETURN }; - BUTTON_CODE ButtonResponseToTouch( void ); - - static TYPEDESCRIPTION m_SaveData[]; - // Buttons that don't take damage can be IMPULSE used - virtual int ObjectCaps( void ) { return (CBaseToggle:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | (pev->takedamage?0:FCAP_IMPULSE_USE); } - - BOOL m_fStayPushed; // button stays pushed in until touched again? - BOOL m_fRotating; // a rotating button? default is a sliding button. - - string_t m_strChangeTarget; // if this field is not null, this is an index into the engine string array. - // when this button is touched, it's target entity's TARGET field will be set - // to the button's ChangeTarget. This allows you to make a func_train switch paths, etc. - - locksound_t m_ls; // door lock sounds - - BYTE m_bLockedSound; // ordinals from entity selection - BYTE m_bLockedSentence; - BYTE m_bUnlockedSound; - BYTE m_bUnlockedSentence; - int m_sounds; - -private: - float mSavedSpeed; - float mSavedHealth; - vec3_t mSavedMoveDir; - int mSavedSpawnFlags; - BOOL mSavedStayPushed; - float mSavedRotation; - int mSavedFrame; - int mSavedSkin; -}; - -// -// Weapons -// - -#define BAD_WEAPON 0x00007FFF - -// -// Converts a entvars_t * to a class pointer -// It will allocate the class and entity if necessary -// -template T * GetClassPtr( T *a ) -{ - entvars_t *pev = (entvars_t *)a; - - // allocate entity if necessary - if (pev == NULL) - pev = VARS(CREATE_ENTITY()); - - // get the private data - a = (T *)GET_PRIVATE(ENT(pev)); - - if (a == NULL) - { - // allocate private data - a = new(pev) T; - a->pev = pev; - } - return a; -} - - -/* -bit_PUSHBRUSH_DATA | bit_TOGGLE_DATA -bit_MONSTER_DATA -bit_DELAY_DATA -bit_TOGGLE_DATA | bit_DELAY_DATA | bit_MONSTER_DATA -bit_PLAYER_DATA | bit_MONSTER_DATA -bit_MONSTER_DATA | CYCLER_DATA -bit_LIGHT_DATA -path_corner_data -bit_MONSTER_DATA | wildcard_data -bit_MONSTER_DATA | bit_GROUP_DATA -boid_flock_data -boid_data -CYCLER_DATA -bit_ITEM_DATA -bit_ITEM_DATA | func_hud_data -bit_TOGGLE_DATA | bit_ITEM_DATA -EOFFSET -env_sound_data -env_sound_data -push_trigger_data -*/ - -#define TRACER_FREQ 4 // Tracers fire every 4 bullets - -typedef struct _SelAmmo -{ - BYTE Ammo1Type; - BYTE Ammo1; - BYTE Ammo2Type; - BYTE Ammo2; -} SelAmmo; - - -// this moved here from world.cpp, to allow classes to be derived from it -//======================= -// CWorld -// -// This spawns first when each level begins. -//======================= -class CWorld : public CBaseEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); -}; - -typedef vector BaseEntityListType; - +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ +/* + +Class Hierachy + +CBaseEntity + CBaseDelay + CBaseToggle + CBaseItem + CBaseMonster + CBaseCycler + CBasePlayer + CBaseGroup +*/ +#ifndef CBASE_H +#define CBASE_H + +#include "common/damagetypes.h" +#include "util/Checksum.h" +#include "types.h" + +#define MAX_PATH_SIZE 10 // max number of nodes available for a path. + +// These are caps bits to indicate what an object's capabilities (currently used for save/restore and level transitions) +#define FCAP_CUSTOMSAVE 0x00000001 +#define FCAP_ACROSS_TRANSITION 0x00000002 // should transfer between transitions +#define FCAP_MUST_SPAWN 0x00000004 // Spawn after restore +#define FCAP_DONT_SAVE 0x80000000 // Don't save this +#define FCAP_IMPULSE_USE 0x00000008 // can be used by the player +#define FCAP_CONTINUOUS_USE 0x00000010 // can be used by the player +#define FCAP_ONOFF_USE 0x00000020 // can be used by the player +#define FCAP_DIRECTIONAL_USE 0x00000040 // Player sends +/- 1 when using (currently only tracktrains) +#define FCAP_MASTER 0x00000080 // Can be used to "master" other entities (like multisource) + +// UNDONE: This will ignore transition volumes (trigger_transition), but not the PVS!!! +#define FCAP_FORCE_TRANSITION 0x00000080 // ALWAYS goes across transitions + +#include "saverestore.h" +#include "schedule.h" + +#ifndef MONSTEREVENT_H +#include "monsterevent.h" +#endif + +// C functions for external declarations that call the appropriate C++ methods + +#ifdef _WIN32 +#define EXPORT _declspec( dllexport ) +#else +#define EXPORT /* */ +#endif + +extern "C" EXPORT int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ); +extern "C" EXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ); + +extern int DispatchSpawn( edict_t *pent ); +extern void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd ); +extern void DispatchTouch( edict_t *pentTouched, edict_t *pentOther ); +extern void DispatchUse( edict_t *pentUsed, edict_t *pentOther ); +extern void DispatchThink( edict_t *pent ); +extern void DispatchBlocked( edict_t *pentBlocked, edict_t *pentOther ); +extern void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData ); +extern int DispatchRestore( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ); +extern void DispatchObjectCollsionBox( edict_t *pent ); +extern void SaveWriteFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); +extern void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); +extern void SaveGlobalState( SAVERESTOREDATA *pSaveData ); +extern void RestoreGlobalState( SAVERESTOREDATA *pSaveData ); +extern void ResetGlobalState( void ); + +typedef enum { USE_OFF = 0, USE_ON = 1, USE_SET = 2, USE_TOGGLE = 3 } USE_TYPE; + +extern void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + +typedef void (CBaseEntity::*BASEPTR)(void); +typedef void (CBaseEntity::*ENTITYFUNCPTR)(CBaseEntity *pOther ); +typedef void (CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + +// For CLASSIFY +#define CLASS_NONE 0 +#define CLASS_MACHINE 1 +#define CLASS_PLAYER 2 +#define CLASS_HUMAN_PASSIVE 3 +#define CLASS_HUMAN_MILITARY 4 +#define CLASS_ALIEN_MILITARY 5 +#define CLASS_ALIEN_PASSIVE 6 +#define CLASS_ALIEN_MONSTER 7 +#define CLASS_ALIEN_PREY 8 +#define CLASS_ALIEN_PREDATOR 9 +#define CLASS_INSECT 10 +#define CLASS_PLAYER_ALLY 11 +#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players +#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace +#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. + +class CBaseEntity; +class CBaseMonster; +class CBasePlayerItem; +class CSquadMonster; + + +#define SF_NORESPAWN ( 1 << 30 )// !!!set this bit on guns and stuff that should never respawn. + +// +// EHANDLE. Safe way to point to CBaseEntities who may die between frames +// +class EHANDLE +{ +private: + edict_t *m_pent; + int m_serialnumber; +public: + edict_t *Get( void ); + edict_t *Set( edict_t *pent ); + + operator int (); + + operator CBaseEntity *(); + + CBaseEntity * operator = (CBaseEntity *pEntity); + CBaseEntity * operator ->(); +}; + + +// +// Base Entity. All entity types derive from this +// +class CBaseEntity +{ +public: + // Constructor. Set engine to use C/C++ callback functions + // pointers to engine data + entvars_t *pev; // Don't need to save/restore this pointer, the engine resets it + + // path corners + CBaseEntity *m_pGoalEnt;// path corner we are heading towards + CBaseEntity *m_pLink;// used for temporary link-list operations. + + // initialization functions + virtual void Spawn( void ) { return; } + virtual void Precache( void ) { return; } + virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + virtual int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; } + virtual void Activate( void ) {} + + // Setup the object->object collision box (pev->mins / pev->maxs is the object->world collision box) + virtual void SetObjectCollisionBox( void ); + +// Classify - returns the type of group (i.e, "houndeye", or "human military" so that monsters with different classnames +// still realize that they are teammates. (overridden for monsters that form groups) + virtual int Classify ( void ) { return CLASS_NONE; }; + virtual void DeathNotice ( entvars_t *pevChild ) {}// monster maker children use this to tell the monster maker that they have died. + + + static TYPEDESCRIPTION m_SaveData[]; + + virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); + virtual int TakeHealth( float flHealth, int bitsDamageType ); + virtual int GetPointValue(void) const { return 0; } + virtual void Killed( entvars_t *pevAttacker, int iGib ); + virtual void AwardKill( entvars_t* pevTarget) {} + virtual int BloodColor( void ) { return DONT_BLEED; } + virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); + virtual BOOL IsTriggered( CBaseEntity *pActivator ) {return TRUE;} + virtual CBaseMonster *MyMonsterPointer( void ) { return NULL;} + virtual CSquadMonster *MySquadMonsterPointer( void ) { return NULL;} + virtual int GetToggleState( void ) { return TS_AT_TOP; } + virtual void AddPoints( int score, BOOL bAllowNegativeScore ) {} + virtual void AddPointsToTeam( int score, BOOL bAllowNegativeScore ) {} + virtual BOOL AddPlayerItem( CBasePlayerItem *pItem ) { return 0; } + virtual BOOL RemovePlayerItem( CBasePlayerItem *pItem ) { return 0; } + virtual int GiveAmmo( int iAmount, char *szName, int iMax ) { return -1; }; + virtual float GetDelay( void ) { return 0; } + virtual int IsMoving( void ) { return pev->velocity != g_vecZero; } + virtual void OverrideReset( void ) {} + virtual int DamageDecal( int bitsDamageType ); + // This is ONLY used by the node graph to test movement through a door + virtual void SetToggleState( int state ) {} + virtual void StartSneaking( void ) {} + virtual void StopSneaking( void ) {} + virtual BOOL OnControls( entvars_t *pev ) { return FALSE; } + virtual BOOL IsSneaking( void ) { return FALSE; } + virtual BOOL IsAlive( void ) const { return (pev->deadflag == DEAD_NO) && pev->health > 0; } + virtual BOOL IsBSPModel( void ) { return pev->solid == SOLID_BSP || pev->movetype == MOVETYPE_PUSHSTEP; } + virtual BOOL ReflectGauss( void ) { return ( IsBSPModel() && !pev->takedamage ); } + virtual BOOL HasTarget( string_t targetname ) { return FStrEq(STRING(targetname), STRING(pev->targetname) ); } + virtual BOOL IsInWorld( void ); + virtual BOOL IsPlayer( void ) { return FALSE; } + virtual BOOL IsNetClient( void ) { return FALSE; } + virtual char* TeamID( void ) { return ""; } + + // Added to allow resetting of entities when a game starts + virtual void AddChecksum(Checksum& inChecksum); + virtual void ResetEntity(void) {} + +// virtual void SetActivator( CBaseEntity *pActivator ) {} + virtual CBaseEntity *GetNextTarget( void ); + + // fundamental callbacks + void (CBaseEntity ::*m_pfnThink)(void); + void (CBaseEntity ::*m_pfnTouch)( CBaseEntity *pOther ); + void (CBaseEntity ::*m_pfnUse)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void (CBaseEntity ::*m_pfnBlocked)( CBaseEntity *pOther ); + + virtual void Think( void ) { if (m_pfnThink) (this->*m_pfnThink)(); }; + virtual void Touch( CBaseEntity *pOther ) { if (m_pfnTouch) (this->*m_pfnTouch)( pOther ); }; + virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) + { + if (m_pfnUse) + (this->*m_pfnUse)( pActivator, pCaller, useType, value ); + } + virtual void Blocked( CBaseEntity *pOther ) { if (m_pfnBlocked) (this->*m_pfnBlocked)( pOther ); }; + + // allow engine to allocate instance data + void *operator new( size_t stAllocateBlock, entvars_t *pev ) + { + return (void *)ALLOC_PRIVATE(ENT(pev), (int)stAllocateBlock); + }; + + // don't use this. +#if _MSC_VER >= 1200 // only build this code if MSVC++ 6.0 or higher + void operator delete(void *pMem, entvars_t *pev) + { + pev->flags |= FL_KILLME; + }; +#endif + + virtual void UpdateOnRemove( void ); + + // common member functions + void EXPORT SUB_Remove( void ); + void EXPORT SUB_DoNothing( void ); + void EXPORT SUB_StartFadeOut ( void ); + void EXPORT SUB_FadeOut ( void ); + void EXPORT SUB_CallUseToggle( void ) { this->Use( this, this, USE_TOGGLE, 0 ); } + int ShouldToggle( USE_TYPE useType, BOOL currentState ); + void FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL, int inDamageType = DMG_BULLET | DMG_NEVERGIB); + Vector FireBulletsPlayer( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL, int shared_rand = 0 ); + + virtual CBaseEntity *Respawn( void ) { return NULL; } + + void SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ); + // Do the bounding boxes of these two intersect? + int Intersects( CBaseEntity *pOther ); + void MakeDormant( void ); + int IsDormant( void ); + BOOL IsLockedByMaster( void ) { return FALSE; } + + static CBaseEntity *Instance( edict_t *pent ) + { + if ( !pent ) + pent = ENT(0); + CBaseEntity *pEnt = (CBaseEntity *)GET_PRIVATE(pent); + return pEnt; + } + + static CBaseEntity *Instance( entvars_t *pev ) { return Instance( ENT( pev ) ); } + static CBaseEntity *Instance( int eoffset) { return Instance( ENT( eoffset) ); } + + CBaseMonster *GetMonsterPointer( entvars_t *pevMonster ) + { + CBaseEntity *pEntity = Instance( pevMonster ); + if ( pEntity ) + return pEntity->MyMonsterPointer(); + return NULL; + } + CBaseMonster *GetMonsterPointer( edict_t *pentMonster ) + { + CBaseEntity *pEntity = Instance( pentMonster ); + if ( pEntity ) + return pEntity->MyMonsterPointer(); + return NULL; + } + + + // Ugly code to lookup all functions to make sure they are exported when set. +#ifdef _DEBUG + void FunctionCheck( void *pFunction, char *name ) + { +//#ifdef _WIN32 +// if (pFunction && !NAME_FOR_FUNCTION((unsigned long)(pFunction)) ) +// ALERT( at_error, "No EXPORT: %s:%s (%08lx)\n", STRING(pev->classname), name, (unsigned long)pFunction ); +//#endif // _WIN32 + } + +#pragma warning(push) +#pragma warning(disable: 312) + + BASEPTR ThinkSet( BASEPTR func, char *name ) + { + m_pfnThink = func; + FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnThink)))), name ); + return func; + } + ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name ) + { + m_pfnTouch = func; + FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnTouch)))), name ); + return func; + } + USEPTR UseSet( USEPTR func, char *name ) + { + m_pfnUse = func; + FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnUse)))), name ); + return func; + } + ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, char *name ) + { + m_pfnBlocked = func; + FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnBlocked)))), name ); + return func; + } + +#pragma + +#endif + + + // virtual functions used by a few classes + + // used by monsters that are created by the MonsterMaker + virtual void UpdateOwner( void ) { return; }; + + + // + static CBaseEntity *Create( const char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner = NULL ); + + virtual BOOL FBecomeProne( void ) {return FALSE;}; + edict_t *edict() { return ENT( pev ); }; + EOFFSET eoffset( ) { return OFFSET( pev ); }; + int entindex( ) { return ENTINDEX( edict() ); }; + + virtual Vector Center( ) { return (pev->absmax + pev->absmin) * 0.5; }; // center point of entity + virtual Vector EyePosition( ) { return pev->origin + pev->view_ofs; }; // position of eyes + virtual Vector EarPosition( ) { return pev->origin + pev->view_ofs; }; // position of ears + virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ); }; // position to shoot at + + virtual int Illumination( ) { return GETENTITYILLUM( ENT( pev ) ); }; + + virtual BOOL FVisible ( CBaseEntity *pEntity ); + virtual BOOL FVisible ( const Vector &vecOrigin ); + + //We use this variables to store each ammo count. + int ammo_9mm; + int ammo_357; + int ammo_bolts; + int ammo_buckshot; + int ammo_rockets; + int ammo_uranium; + int ammo_hornets; + int ammo_argrens; + //Special stuff for grenades and satchels. + float m_flStartThrow; + float m_flReleaseThrow; + int m_chargeReady; + int m_fInAttack; + + enum EGON_FIRESTATE { FIRE_OFF, FIRE_CHARGE }; + int m_fireState; +}; + + + +// Ugly technique to override base member functions +// Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a +// member function of a base class. static_cast is a sleezy way around that problem. + +#ifdef _DEBUG + +#define SetThink( a ) ThinkSet( static_cast (a), #a ) +#define SetTouch( a ) TouchSet( static_cast (a), #a ) +#define SetUse( a ) UseSet( static_cast (a), #a ) +#define SetBlocked( a ) BlockedSet( static_cast (a), #a ) + +#else + +#define SetThink( a ) m_pfnThink = static_cast (a) +#define SetTouch( a ) m_pfnTouch = static_cast (a) +#define SetUse( a ) m_pfnUse = static_cast (a) +#define SetBlocked( a ) m_pfnBlocked = static_cast (a) + +#endif + + +class CPointEntity : public CBaseEntity +{ +public: + void Spawn( void ); + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } +private: +}; + + +typedef struct locksounds // sounds that doors and buttons make when locked/unlocked +{ + string_t sLockedSound; // sound a door makes when it's locked + string_t sLockedSentence; // sentence group played when door is locked + string_t sUnlockedSound; // sound a door makes when it's unlocked + string_t sUnlockedSentence; // sentence group played when door is unlocked + + int iLockedSentence; // which sentence in sentence group to play next + int iUnlockedSentence; // which sentence in sentence group to play next + + float flwaitSound; // time delay between playing consecutive 'locked/unlocked' sounds + float flwaitSentence; // time delay between playing consecutive sentences + BYTE bEOFLocked; // true if hit end of list of locked sentences + BYTE bEOFUnlocked; // true if hit end of list of unlocked sentences +} locksound_t; + +void PlayLockSounds(entvars_t *pev, locksound_t *pls, int flocked, int fbutton); + +// +// MultiSouce +// + +#define MAX_MULTI_TARGETS 16 // maximum number of targets a single multi_manager entity may be assigned. +#define MS_MAX_TARGETS 32 + +class CMultiSource : public CPointEntity +{ +public: + void Spawn( ); + void KeyValue( KeyValueData *pkvd ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + int ObjectCaps( void ) { return (CPointEntity::ObjectCaps() | FCAP_MASTER); } + BOOL IsTriggered( CBaseEntity *pActivator ); + void EXPORT Register( void ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + + EHANDLE m_rgEntities[MS_MAX_TARGETS]; + int m_rgTriggered[MS_MAX_TARGETS]; + + int m_iTotal; + string_t m_globalstate; +}; + + +// +// generic Delay entity. +// +class CBaseDelay : public CBaseEntity +{ +public: + float m_flDelay; + int m_iszKillTarget; + + virtual void KeyValue( KeyValueData* pkvd); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + // common member functions + void SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ); + void EXPORT DelayThink( void ); +}; + + +class CBaseAnimating : public CBaseDelay +{ +public: + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + + // Basic Monster Animation functions + float StudioFrameAdvance( float flInterval = 0.0 ); // accumulate animation frame time from last time called until now + int GetSequenceFlags( void ); + int LookupActivity ( int activity ); + int LookupActivityHeaviest ( int activity ); + int LookupSequence ( const char *label, int queue = 0 ); + const char* LookupSequence(int inSequence); + void ResetSequenceInfo ( ); + void DispatchAnimEvents ( float flFutureInterval = 0.1 ); // Handle events that have happend since last time called up until X seconds into the future + virtual void HandleAnimEvent( MonsterEvent_t *pEvent ) { return; }; + virtual float SetBoneController ( int iController, float flValue ); + void InitBoneControllers ( void ); + float SetBlending ( int iBlender, float flValue ); + void GetBonePosition ( int iBone, Vector &origin, Vector &angles ); + void GetAutomovement( Vector &origin, Vector &angles, float flInterval = 0.1 ); + int FindTransition( int iEndingSequence, int iGoalSequence, int *piDir ); + void GetAttachment ( int iAttachment, Vector &origin, Vector &angles ); + void SetBodygroup( int iGroup, int iValue ); + int GetBodygroup( int iGroup ); + int ExtractBbox( int sequence, float *mins, float *maxs ); + void SetSequenceBox( void ); + + // animation needs + float m_flFrameRate; // computed FPS for current sequence + float m_flGroundSpeed; // computed linear movement rate for current sequence + float m_flLastEventCheck; // last time the event list was checked + BOOL m_fSequenceFinished;// flag set when StudioAdvanceFrame moves across a frame boundry + BOOL m_fSequenceLoops; // true if the sequence loops + + // For performance gain during LookupSequence + char mPreviousLookupString[3][64]; + int mPreviousLookupSequence[3]; +}; + + +// +// generic Toggle entity. +// +#define SF_ITEM_USE_ONLY 256 // ITEM_USE_ONLY = BUTTON_USE_ONLY = DOOR_USE_ONLY!!! + +class CBaseToggle : public CBaseAnimating +{ +public: + void KeyValue( KeyValueData *pkvd ); + + TOGGLE_STATE m_toggle_state; + float m_flActivateFinished;//like attack_finished, but for doors + float m_flMoveDistance;// how far a door should slide or rotate + float m_flWait; + float m_flLip; + float m_flTWidth;// for plats + float m_flTLength;// for plats + + Vector m_vecPosition1; + Vector m_vecPosition2; + Vector m_vecAngle1; + Vector m_vecAngle2; + + int m_cTriggersLeft; // trigger_counter only, # of activations remaining + float m_flHeight; + EHANDLE m_hActivator; + void (CBaseToggle::*m_pfnCallWhenMoveDone)(void); + Vector m_vecFinalDest; + Vector m_vecFinalAngle; + + int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + + virtual int GetToggleState( void ) { return m_toggle_state; } + virtual float GetDelay( void ) { return m_flWait; } + + // common member functions + void LinearMove( Vector vecDest, float flSpeed ); + void EXPORT LinearMoveDone( void ); + void AngularMove( Vector vecDestAngle, float flSpeed ); + void EXPORT AngularMoveDone( void ); + BOOL IsLockedByMaster( void ); + + virtual void SaveDataForReset(); + virtual void ResetEntity(); + + static float AxisValue( int flags, const Vector &angles ); + static void AxisDir( entvars_t *pev ); + static float AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ); + + string_t m_sMaster; // If this button has a master switch, this is the targetname. + // A master switch must be of the multisource type. If all + // of the switches in the multisource have been triggered, then + // the button will be allowed to operate. Otherwise, it will be + // deactivated. +private: + vec3_t mSavedOrigin; + float mSavedWait; + float mSavedLip; +}; +#define SetMoveDone( a ) m_pfnCallWhenMoveDone = static_cast (a) + + +// people gib if their health is <= this at the time of death +#define GIB_HEALTH_VALUE -30 + +#define ROUTE_SIZE 8 // how many waypoints a monster can store at one time +#define MAX_OLD_ENEMIES 4 // how many old enemies to remember + +#define bits_CAP_DUCK ( 1 << 0 )// crouch +#define bits_CAP_JUMP ( 1 << 1 )// jump/leap +#define bits_CAP_STRAFE ( 1 << 2 )// strafe ( walk/run sideways) +#define bits_CAP_SQUAD ( 1 << 3 )// can form squads +#define bits_CAP_SWIM ( 1 << 4 )// proficiently navigate in water +#define bits_CAP_CLIMB ( 1 << 5 )// climb ladders/ropes +#define bits_CAP_USE ( 1 << 6 )// open doors/push buttons/pull levers +#define bits_CAP_HEAR ( 1 << 7 )// can hear forced sounds +#define bits_CAP_AUTO_DOORS ( 1 << 8 )// can trigger auto doors +#define bits_CAP_OPEN_DOORS ( 1 << 9 )// can open manual doors +#define bits_CAP_TURN_HEAD ( 1 << 10)// can turn head, always bone controller 0 + +#define bits_CAP_RANGE_ATTACK1 ( 1 << 11)// can do a range attack 1 +#define bits_CAP_RANGE_ATTACK2 ( 1 << 12)// can do a range attack 2 +#define bits_CAP_MELEE_ATTACK1 ( 1 << 13)// can do a melee attack 1 +#define bits_CAP_MELEE_ATTACK2 ( 1 << 14)// can do a melee attack 2 + +#define bits_CAP_FLY ( 1 << 15)// can fly, move all around + +#define bits_CAP_DOORS_GROUP (bits_CAP_USE | bits_CAP_AUTO_DOORS | bits_CAP_OPEN_DOORS) + +// used by suit voice to indicate damage sustained and repaired type to player + +// instant damage + +// time-based damage +//#define DMG_TIMEBASED (~(0x3fff)) // mask for time-based damage +#include "common/damagetypes.h" + +#define DMG_PARALYZE (1 << 15) // slows affected creature down +#define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad +#define DMG_POISON (1 << 17) // blood poisioning +#define DMG_RADIATION (1 << 18) // radiation exposure +#define DMG_DROWNRECOVER (1 << 19) // drowning recovery +#define DMG_ACID (1 << 20) // toxic chemicals or acid burns +#define DMG_SLOWBURN (1 << 21) // in an oven +#define DMG_SLOWFREEZE (1 << 22) // in a subzero freezer +#define DMG_MORTAR (1 << 23) // Hit by air raid (done to distinguish grenade from mortar) + +// these are the damage types that are allowed to gib corpses +#define DMG_GIB_CORPSE ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB ) + +// these are the damage types that have client hud art +#define DMG_SHOWNHUD (DMG_POISON | /*DMG_ACID |*/ DMG_FREEZE | DMG_SLOWFREEZE /*| DMG_DROWN*/ | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION /*| DMG_SHOCK*/) + +// NOTE: tweak these values based on gameplay feedback: + +#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage +#define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval + +#define NERVEGAS_DURATION 2 +#define NERVEGAS_DAMAGE 5.0 + +#define POISON_DURATION 5 +#define POISON_DAMAGE 2.0 + +#define RADIATION_DURATION 2 +#define RADIATION_DAMAGE 1.0 + +#define ACID_DURATION 2 +#define ACID_DAMAGE 5.0 + +#define SLOWBURN_DURATION 2 +#define SLOWBURN_DAMAGE 1.0 + +#define SLOWFREEZE_DURATION 2 +#define SLOWFREEZE_DAMAGE 1.0 + + +#define itbd_Paralyze 0 +#define itbd_NerveGas 1 +#define itbd_Poison 2 +#define itbd_Radiation 3 +#define itbd_DrownRecover 4 +#define itbd_Acid 5 +#define itbd_SlowBurn 6 +#define itbd_SlowFreeze 7 +#define CDMG_TIMEBASED 8 + +// when calling KILLED(), a value that governs gib behavior is expected to be +// one of these three values +#define GIB_NORMAL 0// gib if entity was overkilled +#define GIB_NEVER 1// never gib, no matter how much death damage is done ( freezing, etc ) +#define GIB_ALWAYS 2// always gib ( Houndeye Shock, Barnacle Bite ) +// : 980 +// Use gib paramater to control death of recycled buildings +#define GIB_RECYCLED 3// always gib ( Houndeye Shock, Barnacle Bite ) +// : + +class CBaseMonster; +class CCineMonster; +class CSound; + +#include "basemonster.h" + + +char *ButtonSound( int sound ); // get string of button sound number + + +// +// Generic Button +// +class CBaseButton : public CBaseToggle +{ +public: + void Spawn( void ); + virtual void Precache( void ); + void RotSpawn( void ); + virtual void KeyValue( KeyValueData* pkvd); + + void ButtonActivate( ); + void SparkSoundCache( void ); + + void EXPORT ButtonShot( void ); + void EXPORT ButtonTouch( CBaseEntity *pOther ); + void EXPORT ButtonSpark ( void ); + void EXPORT TriggerAndWait( void ); + void EXPORT ButtonReturn( void ); + void EXPORT ButtonBackHome( void ); + void EXPORT ButtonUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + virtual void SaveDataForReset(); + virtual void ResetEntity(); + + enum BUTTON_CODE { BUTTON_NOTHING, BUTTON_ACTIVATE, BUTTON_RETURN }; + BUTTON_CODE ButtonResponseToTouch( void ); + + static TYPEDESCRIPTION m_SaveData[]; + // Buttons that don't take damage can be IMPULSE used + virtual int ObjectCaps( void ) { return (CBaseToggle:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | (pev->takedamage?0:FCAP_IMPULSE_USE); } + + BOOL m_fStayPushed; // button stays pushed in until touched again? + BOOL m_fRotating; // a rotating button? default is a sliding button. + + string_t m_strChangeTarget; // if this field is not null, this is an index into the engine string array. + // when this button is touched, it's target entity's TARGET field will be set + // to the button's ChangeTarget. This allows you to make a func_train switch paths, etc. + + locksound_t m_ls; // door lock sounds + + BYTE m_bLockedSound; // ordinals from entity selection + BYTE m_bLockedSentence; + BYTE m_bUnlockedSound; + BYTE m_bUnlockedSentence; + int m_sounds; + +private: + float mSavedSpeed; + float mSavedHealth; + vec3_t mSavedMoveDir; + int mSavedSpawnFlags; + BOOL mSavedStayPushed; + float mSavedRotation; + int mSavedFrame; + int mSavedSkin; +}; + +// +// Weapons +// + +#define BAD_WEAPON 0x00007FFF + +// +// Converts a entvars_t * to a class pointer +// It will allocate the class and entity if necessary +// +template T * GetClassPtr( T *a ) +{ + entvars_t *pev = (entvars_t *)a; + + // allocate entity if necessary + if (pev == NULL) + pev = VARS(CREATE_ENTITY()); + + // get the private data + a = (T *)GET_PRIVATE(ENT(pev)); + + if (a == NULL) + { + // allocate private data + a = new(pev) T; + a->pev = pev; + } + return a; +} + + +/* +bit_PUSHBRUSH_DATA | bit_TOGGLE_DATA +bit_MONSTER_DATA +bit_DELAY_DATA +bit_TOGGLE_DATA | bit_DELAY_DATA | bit_MONSTER_DATA +bit_PLAYER_DATA | bit_MONSTER_DATA +bit_MONSTER_DATA | CYCLER_DATA +bit_LIGHT_DATA +path_corner_data +bit_MONSTER_DATA | wildcard_data +bit_MONSTER_DATA | bit_GROUP_DATA +boid_flock_data +boid_data +CYCLER_DATA +bit_ITEM_DATA +bit_ITEM_DATA | func_hud_data +bit_TOGGLE_DATA | bit_ITEM_DATA +EOFFSET +env_sound_data +env_sound_data +push_trigger_data +*/ + +#define TRACER_FREQ 4 // Tracers fire every 4 bullets + +typedef struct _SelAmmo +{ + BYTE Ammo1Type; + BYTE Ammo1; + BYTE Ammo2Type; + BYTE Ammo2; +} SelAmmo; + + +// this moved here from world.cpp, to allow classes to be derived from it +//======================= +// CWorld +// +// This spawns first when each level begins. +//======================= +class CWorld : public CBaseEntity +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); +}; + +typedef vector BaseEntityListType; + #endif \ No newline at end of file diff --git a/main/source/dlls/client.cpp b/main/source/dlls/client.cpp index bbc4895..20ab795 100644 --- a/main/source/dlls/client.cpp +++ b/main/source/dlls/client.cpp @@ -1,2861 +1,2929 @@ -// -// Copyright (c) 1999, Valve LLC. All rights reserved. -// -// This product contains software technology licensed from Id -// Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -// All Rights Reserved. -// -// Use, distribution, and modification of this source code and/or resulting -// object code is restricted to non-commercial enhancements to products from -// Valve LLC. All other use, distribution, or modification is prohibited -// without written permission from Valve LLC. -// -// Robin, 4-22-98: Moved set_suicide_frame() here from player.cpp to allow us to -// have one without a hardcoded player.mdl in tf_client.cpp -// -//===== client.cpp ======================================================== -// -// client/server game specific stuff -// -// $Workfile: client.cpp $ -// $Date: 2002/11/22 21:09:09 $ -// -//------------------------------------------------------------------------------- -// $Log: client.cpp,v $ -// Revision 1.69 2002/11/22 21:09:09 Flayra -// - Added "lastinv" command back in -// - mp_consistency changes -// -// Revision 1.68 2002/11/15 23:31:01 Flayra -// - Added "ready" verification for tourny mode -// -// Revision 1.67 2002/11/15 05:08:31 Flayra -// - Little tweak for refinery -// -// Revision 1.66 2002/11/15 04:48:40 Flayra -// - Oops -// -// Revision 1.65 2002/11/15 04:41:32 Flayra -// - Big performance improvements for AddToFullPack (whew) -// -// Revision 1.64 2002/11/12 02:17:48 Flayra -// - Renamed "avhplayer" to "player" -// - Tweaked enhanced sight to actually be useful at lower levels -// -// Revision 1.63 2002/11/03 04:53:20 Flayra -// - AddToFullPack optimizations and cleanup -// -// Revision 1.62 2002/10/25 21:50:01 Flayra -// - New attempted fix for overflows. Propagate skin and playerclass in new way, so entities outside the ready room aren't propagated to all clients that go back to the ready room immediately on game end. -// -// Revision 1.61 2002/10/24 21:16:10 Flayra -// - Log Sys_Errors -// - Allow players to change their name before they first leave the ready room -// -// Revision 1.60 2002/10/20 17:25:04 Flayra -// - Redid performance improvement (agh) -// -// Revision 1.59 2002/10/20 16:34:09 Flayra -// - Returned code back to normal -// -// Revision 1.58 2002/10/19 22:33:48 Flayra -// - Various server optimizations -// -// Revision 1.57 2002/10/18 22:14:23 Flayra -// - Server optimizations (untested though, may need backing out) -// -// Revision 1.56 2002/10/16 00:38:50 Flayra -// - Queue up name change until end of game -// - Removed precaching of unneeded sounds -// - Optimized AvHDetermineVisibility (being called 1000s of times per tick) -// -// Revision 1.55 2002/10/03 18:27:39 Flayra -// - Moved order completion sounds into HUD sounds -// -// Revision 1.54 2002/09/25 20:37:53 Flayra -// - Precache more sayings -// -// Revision 1.53 2002/09/23 22:01:02 Flayra -// - Propagate skin -// - Don't treat unclaimed hives at world objects (even though team is 0) for visibility purposes -// - Added heavy model -// - Removed old mapper build preprocessor directives -// -// Revision 1.52 2002/08/31 18:01:17 Flayra -// - Work at VALVe -// -// Revision 1.51 2002/08/16 02:24:31 Flayra -// - Removed "give" cheat -// -// Revision 1.50 2002/08/09 00:15:46 Flayra -// - Removed behavior where "drop" would drop alien weapons in a disconcerting manner -// -// Revision 1.49 2002/07/25 16:50:15 flayra -// - Linux build changes -// -// Revision 1.48 2002/07/24 18:46:19 Flayra -// - Linux and scripting changes -// -// Revision 1.47 2002/07/23 16:46:38 Flayra -// - Added power-armor drawing, tweaked invul drawing -// -// Revision 1.46 2002/07/10 14:36:21 Flayra -// - .mp3 and particle fixes -// -// Revision 1.45 2002/07/08 16:38:57 Flayra -// - Draw invulnerable players differently -// -//=============================================================================== -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "saverestore.h" -#include "player.h" -#include "spectator.h" -#include "client.h" -#include "soundent.h" -#include "gamerules.h" -#include "game.h" -#include "engine/customentity.h" -#include "weapons.h" -#include "common/weaponinfo.h" -#include "common/usercmd.h" -#include "common/netadr.h" -#include "util/nowarnings.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHSoundListManager.h" -#include "game.h" -#include "mod/AvHParticleTemplateServer.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHSpecials.h" -#include "util/Zassert.h" -#include "dlls/cbasedoor.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHMovementUtil.h" -#include "mod/AvHSoundConstants.h" -#include "mod/AvHHulls.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "dlls/triggers.h" -#include "util/MathUtil.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHCloakable.h" -#include "mod/AvHAlienAbilityConstants.h" -#include "mod/AvHNetworkMessages.h" -#include "mod/AvHNexusServer.h" - -#include "game_shared/voice_gamemgr.h" -extern CVoiceGameMgr g_VoiceGameMgr; - -extern AvHSoundListManager gSoundListManager; - -extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; -extern DLL_GLOBAL BOOL g_fGameOver; -extern DLL_GLOBAL int g_iSkillLevel; -extern DLL_GLOBAL ULONG g_ulFrameCount; - -extern void CopyToBodyQue(entvars_t* pev); -extern int g_teamplay; - -/* - * used by kill command and disconnect command - * ROBIN: Moved here from player.cpp, to allow multiple player models - */ -void set_suicide_frame(entvars_t* pev) -{ - if (!FStrEq(STRING(pev->model), "models/player.mdl")) - return; // allready gibbed - - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_TOSS; - pev->deadflag = DEAD_DEAD; - pev->nextthink = -1; -} - - -/* -=========== -LogStringForPlayer - -generates string for player event logging - KGP -============ -*/ -std::string GetLogStringForPlayer( edict_t *pEntity ) -{ - // outputs "netname" if g_teamplay - // or "netname" if not g_teamplay - std::string result = "\""; - result += STRING( pEntity->v.netname ); - result += "<"; - result += MakeStringFromInt( GETPLAYERUSERID( pEntity ) ); - result += "><"; - result += AvHNexus::getNetworkID( pEntity ).c_str(); - result += "><"; - result += AvHSUGetTeamName( pEntity->v.team ); - result += ">\""; - return result; -} - -/* -=========== -ClientConnect - -called when a player connects to a server -============ -*/ -BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) -{ - return g_pGameRules->ClientConnected( pEntity, pszName, pszAddress, szRejectReason ); - - // a client connecting during an intermission can cause problems - // if (intermission_running) - // ExitIntermission (); - -} - - -/* -=========== -ClientDisconnect - - called when a player disconnects from a server - - GLOBALS ASSUMED SET: g_fGameOver - ============ -*/ -void ClientDisconnect( edict_t *pEntity ) -{ - if (g_fGameOver) - return; - - char text[256]; - sprintf( text, "- %s has left the game\n", STRING(pEntity->v.netname) ); - - CBaseEntity* theBaseEntity = CBaseEntity::Instance(ENT(pEntity)); - UTIL_SayTextAll(text, theBaseEntity); - - CSound *pSound; - pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEntity ) ); - { - // since this client isn't around to think anymore, reset their sound. - if ( pSound ) - { - pSound->Reset(); - } - } - - // since the edict doesn't get deleted, fix it so it doesn't interfere. - pEntity->v.takedamage = DAMAGE_NO;// don't attract autoaim - pEntity->v.solid = SOLID_NOT;// nonsolid - UTIL_SetOrigin ( &pEntity->v, pEntity->v.origin ); - - g_pGameRules->ClientDisconnected( pEntity ); - - //voogru: If this isnt set, clients around this player will crash. - pEntity->v.effects |= EF_NODRAW; -} - - -// called by ClientKill and DeadThink -void respawn(entvars_t* pev, BOOL fCopyCorpse) -{ - // AVH isn't dm, coop or single-player. - //if (gpGlobals->coop || gpGlobals->deathmatch) - //{ - if ( fCopyCorpse ) - { - // make a copy of the dead body for appearances sake - CopyToBodyQue(pev); - } - - // respawn player - GetClassPtr( (AvHPlayer *)pev)->Spawn( ); - //} - //else - //{ // restart the entire server - // SERVER_COMMAND("reload\n"); - //} -} - -/* -============ -ClientKill - -Player entered the suicide command - -GLOBALS ASSUMED SET: g_ulModelIndexPlayer -============ -*/ -void ClientKill( edict_t *pEntity ) -{ - entvars_t *pev = &pEntity->v; - - CBasePlayer *pl = (CBasePlayer*) CBasePlayer::Instance( pev ); - - //if(GetGameRules()->GetCheatsEnabled()) - //{ - if ( pl->m_fNextSuicideTime > gpGlobals->time ) - return; // prevent suiciding too ofter - - pl->m_fNextSuicideTime = gpGlobals->time + 1; // don't let them suicide for 5 seconds after suiciding - - bool theIsCombatModeForSuicide = GetGameRules()->GetIsCombatMode(); - - #ifdef DEBUG - if(GetGameRules()->GetIsCombatMode()) - { - theIsCombatModeForSuicide = false; - } - #endif - - bool theCanSuicide = GetGameRules()->CanPlayerBeKilled(pl) && !theIsCombatModeForSuicide; - - if(theCanSuicide) - { - pl->Suicide(); - - // pev->modelindex = g_ulModelIndexPlayer; - // pev->frags -= 2; // extra penalty - // respawn( pev ); - } - //} -} - -/* -=========== -ClientPutInServer - -called each time a player is spawned -============ -*/ -void ClientPutInServer( edict_t *pEntity ) -{ - //CBasePlayer *pPlayer; - AvHPlayer *pPlayer; - - entvars_t *pev = &pEntity->v; - - if(pev->flags & FL_PROXY) - { - ALERT(at_logged, "Player with FL_PROXY put in server.\n"); - } - - if(pev->flags & FL_PROXY) - { - pev->flags |= FL_PROXY; - } - - pPlayer = GetClassPtr((AvHPlayer *)pev); - pPlayer->SetCustomDecalFrames(-1); // Assume none; - - // Allocate a CBasePlayer for pev, and call spawn - pPlayer->SetPlayMode(PLAYMODE_READYROOM); - //pPlayer->Spawn(); - - // Reset interpolation during first frame - pPlayer->pev->effects |= EF_NOINTERP; -} - -//// HOST_SAY -// String comes in as -// say blah blah blah -// or as -// blah blah blah -// -void Host_Say( edict_t *pEntity, int teamonly ) -{ - AvHPlayer* client; - AvHPlayer* theTalkingPlayer = dynamic_cast(CBaseEntity::Instance(pEntity)); - int j; - char* p; - char text[256]; - char szTemp[256]; - const char* cpSay = "say"; - const char* cpSayTeam = "say_team"; - const char* pcmd = CMD_ARGV(0); - bool theTalkerInReadyRoom = theTalkingPlayer->GetInReadyRoom(); - //bool theTalkerIsObserver = (theTalkingPlayer->GetPlayMode() == PLAYMODE_OBSERVER) || (theTalkingPlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT); - - // We can get a raw string now, without the "say " prepended - if ( CMD_ARGC() == 0 ) - return; - - //Not yet. - if ( theTalkingPlayer->m_flNextChatTime > gpGlobals->time ) - return; - - if ( !stricmp( pcmd, cpSay) || !stricmp( pcmd, cpSayTeam ) ) - { - if ( CMD_ARGC() >= 2 ) - { - p = (char *)CMD_ARGS(); - - if(GetGameRules()->GetIsTournamentMode() && !GetGameRules()->GetGameStarted()) - { - if(!strcmp(CMD_ARGV(1), kReadyNotification)) - { - // Team is ready - AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)(pEntity->v.team)); - if(theTeam && !theTeam->GetIsReady()) - { - theTeam->SetIsReady(); - } - } - else if (!strcmp(CMD_ARGV(1), kNotReadyNotification)) - { - // Team is no longer ready - AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)(pEntity->v.team)); - if(theTeam && theTeam->GetIsReady()) - { - theTeam->SetIsReady(false); - } - } - } - } - else - { - // say with a blank message, nothing to do - return; - } - } - else // Raw text, need to prepend argv[0] - { - if ( CMD_ARGC() >= 2 ) - { - sprintf( szTemp, "%s %s", ( char * )pcmd, (char *)CMD_ARGS() ); - } - else - { - // Just a one word command, use the first word...sigh - sprintf( szTemp, "%s", ( char * )pcmd ); - } - p = szTemp; - } - -// remove quotes if present - if (*p == '"') - { - p++; - p[strlen(p)-1] = 0; - } - -// make sure the text has content - char *pc=p; - for ( pc = p; pc != NULL && *pc != 0; pc++ ) - { - if ( isprint( *pc ) && !isspace( *pc ) ) - { - pc = NULL; // we've found an alphanumeric character, so text is valid - break; - } - } - if ( pc != NULL ) - return; // no character found, so say nothing - -// turn on color set 2 (color on, no sound) - if ( teamonly ) - sprintf( text, "%c(TEAM) %s: ", 2, STRING( pEntity->v.netname ) ); - else - sprintf( text, "%c%s: ", 2, STRING( pEntity->v.netname ) ); - - j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator - if ( (int)strlen(p) > j ) - p[j] = 0; - - strcat( text, p ); - strcat( text, "\n" ); - - theTalkingPlayer->m_flNextChatTime = gpGlobals->time + CHAT_INTERVAL; - // loop through all players - // Start with the first player. - // This may return the world in single player if the client types something between levels or during spawn - // so check it, or it will infinite loop - - client = NULL; - while ( ((client = (AvHPlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) - { - if ( !client->pev ) - continue; - - if ( client->edict() == pEntity ) - continue; - - if ( !(client->IsNetClient()) ) // Not a client ? (should never be true) - continue; - - // can the receiver hear the sender? or has he muted him? - if ( g_VoiceGameMgr.PlayerHasBlockedPlayer( client, theTalkingPlayer ) ) - continue; - - // Don't differentiate between team and non-team when not playing - bool theTalkingPlayerIsPlaying = ((theTalkingPlayer->GetPlayMode() == PLAYMODE_PLAYING) || (theTalkingPlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) || (theTalkingPlayer->GetPlayMode() == PLAYMODE_REINFORCING)); - bool theClientIsPlaying = ((client->GetPlayMode() == PLAYMODE_PLAYING) || (client->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) || (client->GetPlayMode() == PLAYMODE_REINFORCING)); - bool theTalkerIsObserver = theTalkingPlayer->IsObserver(); - bool theClientIsObserver = client->IsObserver(); - - bool theClientInReadyRoom = client->GetInReadyRoom(); - - if (theClientInReadyRoom != theTalkerInReadyRoom) - { - continue; - } - - if (!theClientIsObserver || theClientIsPlaying) // Non-playing Observers hear everything. - { - - if ( theTalkingPlayerIsPlaying && teamonly && g_pGameRules->PlayerRelationship(client, CBaseEntity::Instance(pEntity)) != GR_TEAMMATE ) - continue; - - // chat can never go between play area and non-play area - if(theTalkingPlayerIsPlaying != theClientIsPlaying) - continue; - - // chat of any kind doesn't go from ready room to play area in tournament mode - if(theTalkerInReadyRoom && GetGameRules()->GetIsTournamentMode() && theClientIsPlaying) - continue; - - } - - UTIL_SayText(text, client, ENTINDEX(pEntity)); - - } - - // print to the sending client - UTIL_SayText(text, CBaseEntity::Instance(ENT(pEntity))); - - // echo to server console - g_engfuncs.pfnServerPrint( text ); - - char * temp; - if ( teamonly ) - temp = "say_team"; - else - temp = "say"; - - UTIL_LogPrintf( "%s %s \"%s\"\n", GetLogStringForPlayer( pEntity ).c_str(), temp, p ); -} - - -/* -=========== -ClientCommand -called each time a player uses a "cmd" command -============ -*/ - -// Use CMD_ARGV, CMD_ARGV, and CMD_ARGC to get pointers the character string command. -void ClientCommand( edict_t *pEntity ) -{ - const char *pcmd = CMD_ARGV(0); - const char *pstr; - - // Is the client spawned yet? - if ( !pEntity->pvPrivateData ) - return; - - entvars_t *pev = &pEntity->v; - - AvHPlayer* thePlayer = GetClassPtr((AvHPlayer *)pev); - bool thePlayerCanAct = thePlayer->GetIsAbleToAct(); - - if ( FStrEq(pcmd, "say" ) ) - { - Host_Say( pEntity, 0 ); - } - else if ( FStrEq(pcmd, "say_team" ) ) - { - Host_Say( pEntity, 1 ); - } - else if ( FStrEq(pcmd, "fullupdate" ) ) - { - GetClassPtr((CBasePlayer *)pev)->ForceClientDllUpdate(); - } - else if ( FStrEq(pcmd, "give" ) ) - { - if(GetGameRules()->GetCheatsEnabled()) - { - int iszItem = ALLOC_STRING( CMD_ARGV(1) ); // Make a copy of the classname - GetClassPtr((CBasePlayer *)pev)->GiveNamedItem( STRING(iszItem) ); - } - } - - else if ( FStrEq(pcmd, "drop" )) - { - if (thePlayerCanAct) - { - // player is dropping an item. - GetClassPtr((AvHPlayer *)pev)->DropItem((char *)CMD_ARGV(1)); - } - } - else if ( FStrEq(pcmd, "fov" ) ) - { - if (GetGameRules()->GetCheatsEnabled() && (CMD_ARGC() > 1)) - { - GetClassPtr((CBasePlayer *)pev)->m_iFOV = atoi( CMD_ARGV(1) ); - } - else - { - CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"fov\" is \"%d\"\n", (int)GetClassPtr((CBasePlayer *)pev)->m_iFOV ) ); - } - } - else if ( FStrEq(pcmd, "use" )) - { - if (thePlayerCanAct) - { - GetClassPtr((CBasePlayer *)pev)->SelectItem((char *)CMD_ARGV(1)); - } - } - else if (((pstr = strstr(pcmd, "weapon_")) != NULL) && (pstr == pcmd)) - { - if (thePlayerCanAct) - { - GetClassPtr((CBasePlayer *)pev)->SelectItem(pcmd); - } - } - else if ( g_pGameRules->ClientCommand( GetClassPtr((CBasePlayer *)pev), pcmd ) ) - { - // MenuSelect returns true only if the command is properly handled, so don't print a warning - } - else if ( FStrEq( pcmd, "specmode" ) ) // new spectator mode - { - CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); - if ( pPlayer->IsObserver() ) - { - int theMode = atoi(CMD_ARGV(1)); - pPlayer->Observer_SetMode(theMode); - pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2); - } - } - else if (FStrEq( pcmd, "follow")) // follow a specific player - { - CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); - if ( pPlayer->IsObserver() ) - { - pPlayer->Observer_SpectatePlayer(atoi( CMD_ARGV(1) )); - pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2); - } - } - else if ( FStrEq( pcmd, "follownext" ) ) // follow next player - { - CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); - if ( pPlayer->IsObserver() ) - { - pPlayer->Observer_FindNextPlayer(atoi( CMD_ARGV(1) )); - pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2); - } - } - else - { - // tell the user they entered an unknown command - char command[128]; - - // check the length of the command (prevents crash) - // max total length is 192 ...and we're adding a string below ("Unknown command: %s\n") - strncpy( command, pcmd, 127 ); - command[127] = '\0'; - // puzl: 1071 - // Remove printf formatting - for ( int i=0; i < 127; i++ ) { - if ( command[i] == '%' ) { - command[i] = ' '; - } - } - // :puzl - // tell the user they entered an unknown command - ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, UTIL_VarArgs( "Unknown command: %s\n", command ) ); - } - //else if(!AvHClientCommand(pEntity)) - //{ - // // tell the user they entered an unknown command - // ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, UTIL_VarArgs( "Unknown command: %s\n", pcmd ) ); - //} -} - -//bool AvHClientCommand( edict_t *pEntity ) -//{ -//} - -/* -======================== -ClientUserInfoChanged - -called after the player changes -userinfo - gives dll a chance to modify it before -it gets sent into the rest of the engine. -======================== -*/ -void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer ) -{ - // Is the client spawned yet? - if ( !pEntity->pvPrivateData ) - return; - - const char* theCurrentNetName = STRING(pEntity->v.netname); - const char* theNameKeyValue = g_engfuncs.pfnInfoKeyValue(infobuffer, "name"); - - // msg everyone if someone changes their name, and it isn't the first time (changing no name to current name) - if ( pEntity->v.netname && STRING(pEntity->v.netname)[0] != 0 && !FStrEq(theCurrentNetName, theNameKeyValue) ) - { - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(pEntity)); - - // Don't change player info after match has started unless player hasn't left ready room - if(!GetGameRules()->GetArePlayersAllowedToJoinImmediately() && thePlayer && theNameKeyValue && thePlayer->GetHasLeftReadyRoom()) - { - thePlayer->SetDesiredNetName(string(theNameKeyValue)); - - char text[256]; - sprintf( text, "* Name will be changed to %s next game.\n", theNameKeyValue); - UTIL_SayText(text, CBaseEntity::Instance(ENT(pEntity))); - - // Restore name key value changed by engine - char theName[256]; - strcpy(theName, theCurrentNetName); - g_engfuncs.pfnSetClientKeyValue( ENTINDEX(pEntity), infobuffer, "name", theName); - } - else - { - char sName[256]; - char *pName = g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ); - strncpy( sName, pName, sizeof(sName) - 1 ); - sName[ sizeof(sName) - 1 ] = '\0'; - - // First parse the name and remove any %'s - for ( char *pApersand = sName; pApersand != NULL && *pApersand != 0; pApersand++ ) - { - // Replace it with a space - if ( *pApersand == '%' ) - *pApersand = ' '; - } - - // Set the name - g_engfuncs.pfnSetClientKeyValue( ENTINDEX(pEntity), infobuffer, "name", sName ); - //thePlayer->SetDesiredNetName(MAKE_STRING(sName)); - - char text[256]; - sprintf( text, "* %s changed name to %s\n", STRING(pEntity->v.netname), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ) ); - - UTIL_SayTextAll(text, CBaseEntity::Instance(ENT(pEntity))); - - UTIL_LogPrintf( "%s changed name to \"%s\"\n", GetLogStringForPlayer( pEntity ).c_str(), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ) ); - } - } - - g_pGameRules->ClientUserInfoChanged( GetClassPtr((CBasePlayer *)&pEntity->v), infobuffer ); -} - -static int g_serveractive = 0; - -void ServerDeactivate( void ) -{ - // It's possible that the engine will call this function more times than is necessary - // Therefore, only run it one time for each call to ServerActivate - if ( g_serveractive != 1 ) - { - return; - } - - g_serveractive = 0; - - // Peform any shutdown operations here... - // -} - -void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) -{ - int i; - CBaseEntity *pClass; - - // Every call to ServerActivate should be matched by a call to ServerDeactivate - g_serveractive = 1; - - // Clients have not been initialized yet - for ( i = 0; i < edictCount; i++ ) - { - if ( pEdictList[i].free ) - continue; - - // Clients aren't necessarily initialized until ClientPutInServer() - if ( i < clientMax || !pEdictList[i].pvPrivateData ) - continue; - - pClass = CBaseEntity::Instance( &pEdictList[i] ); - // Activate this entity if it's got a class & isn't dormant - if ( pClass && !(pClass->pev->flags & FL_DORMANT) ) - { - pClass->Activate(); - } - else - { - ALERT( at_console, "Can't instance %s\n", STRING(pEdictList[i].v.classname) ); - } - } - - Net_InitializeMessages(); -} - - -/* -================ -PlayerPreThink - -Called every frame before physics are run -================ -*/ -void PlayerPreThink( edict_t *pEntity ) -{ - entvars_t *pev = &pEntity->v; - CBasePlayer *pPlayer = (CBasePlayer *)GET_PRIVATE(pEntity); - - if (pPlayer) - pPlayer->PreThink( ); -} - -/* -================ -PlayerPostThink - -Called every frame after physics are run -================ -*/ -void PlayerPostThink( edict_t *pEntity ) -{ - entvars_t *pev = &pEntity->v; - CBasePlayer *pPlayer = (CBasePlayer *)GET_PRIVATE(pEntity); - - if (pPlayer) - pPlayer->PostThink( ); -} - - - -void ParmsNewLevel( void ) -{ -} - - -void ParmsChangeLevel( void ) -{ - // retrieve the pointer to the save data - SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData; - - if ( pSaveData ) - pSaveData->connectionCount = BuildChangeList( pSaveData->levelList, MAX_LEVEL_CONNECTIONS ); -} - -void ShowMenu(entvars_s *pev, int ValidSlots, int DisplayTime, BOOL ShowLater, char Menu[500]) -{ - NetMsg_ShowMenu( pev, ValidSlots, DisplayTime, ShowLater ? 1 : 0, string(Menu) ); -} - -// -// GLOBALS ASSUMED SET: g_ulFrameCount -// -void StartFrame( void ) -{ - if ( g_pGameRules ) - g_pGameRules->Think(); - - if ( g_fGameOver ) - return; - - gpGlobals->teamplay = teamplay.value; - - g_ulFrameCount++; -} - -void ClientPrecache( void ) -{ - // Precache sound lists for all player types - gSoundListManager.Clear(); - - int i=0; - for(i = ((int)AVH_USER3_NONE + 1); i < (int)AVH_USER3_ALIEN_EMBRYO; i++) - { - char theSoundListName[256]; - bool theIsAlien = (i > 3); - AvHUser3 theUser3 = (AvHUser3)(i); - - bool theLoadSounds = false; - - // No squad leader and commander sounds - switch(theUser3) - { - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: - theLoadSounds = true; - } - - if(theLoadSounds) - { - // Die sounds - sprintf(theSoundListName, kPlayerLevelDieSoundList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - - // Spawn sounds - sprintf(theSoundListName, kPlayerLevelSpawnSoundList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - - // Pain sounds - sprintf(theSoundListName, kPlayerLevelPainSoundList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - - // Wound sounds - sprintf(theSoundListName, kPlayerLevelWoundSoundList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - - // Attack sounds (aliens only) - if(theIsAlien) - { - sprintf(theSoundListName, kPlayerLevelAttackSoundList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - - // Idle sounds - sprintf(theSoundListName, kPlayerLevelIdleSoundList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - - // Movement sounds - sprintf(theSoundListName, kPlayerLevelMoveSoundList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - } - } - } - - // Precache misc. alien sounds - //PRECACHE_SOUND(kAdrenalineSound); - PRECACHE_UNMODIFIED_SOUND(kGestationSound); - PRECACHE_UNMODIFIED_SOUND(kEggDestroyedSound); - PRECACHE_UNMODIFIED_SOUND(kEggIdleSound); - PRECACHE_UNMODIFIED_SOUND(kPrimalScreamResponseSound); - - // Preacache sayings sound list - for(i = 1; i <= 9; i++) - { - char theSoundListName[256]; - - sprintf(theSoundListName, kSoldierSayingList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - - sprintf(theSoundListName, kCommanderSayingList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - - sprintf(theSoundListName, kAlienSayingList, i); - gSoundListManager.PrecacheSoundList(theSoundListName); - } - - // Precache order sounds - gSoundListManager.PrecacheSoundList(kSoldierOrderRequestList); - gSoundListManager.PrecacheSoundList(kSoldierOrderAckList); - - // Precache other sound lists - gSoundListManager.PrecacheSoundList(kResourceTowerSoundList); - gSoundListManager.PrecacheSoundList(kAlienResourceTowerSoundList); - gSoundListManager.PrecacheSoundList(kHiveWoundSoundList); - gSoundListManager.PrecacheSoundList(kMarineConstructionSoundList); - gSoundListManager.PrecacheSoundList(kElectricSparkSoundList); - gSoundListManager.PrecacheSoundList(kAlienConstructionSoundList); - - // Other equipment and effect sounds - PRECACHE_UNMODIFIED_SOUND(kMarineBuildingDeploy); - PRECACHE_UNMODIFIED_SOUND(kJetpackSound); - PRECACHE_UNMODIFIED_SOUND(kWeldingSound); - PRECACHE_UNMODIFIED_SOUND(kWeldingHitSound); - //PRECACHE_UNMODIFIED_SOUND(kOverwatchStartSound); - //PRECACHE_UNMODIFIED_SOUND(kOverwatchEndSound); - PRECACHE_UNMODIFIED_SOUND(kRegenerationSound); - PRECACHE_UNMODIFIED_SOUND(kStartCloakSound); - PRECACHE_UNMODIFIED_SOUND(kEndCloakSound); - //PRECACHE_UNMODIFIED_SOUND(kWallJumpSound); - PRECACHE_UNMODIFIED_SOUND(kWingFlapSound1); - PRECACHE_UNMODIFIED_SOUND(kWingFlapSound2); - PRECACHE_UNMODIFIED_SOUND(kWingFlapSound3); - PRECACHE_UNMODIFIED_SOUND(kSiegeHitSound1); - PRECACHE_UNMODIFIED_SOUND(kSiegeHitSound2); - - // setup precaches always needed - PRECACHE_UNMODIFIED_SOUND("player/sprayer.wav"); // spray paint sound for PreAlpha - - // PRECACHE_SOUND("player/pl_jumpland2.wav"); // UNDONE: play 2x step sound - - // Alien step sounds - PRECACHE_UNMODIFIED_SOUND("player/pl_step1-a.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step2-a.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step3-a.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step4-a.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_step1-a1.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step2-a1.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step3-a1.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step4-a1.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_step1-a5.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step2-a5.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step3-a5.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step4-a5.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_step1-h.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step2-h.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step3-h.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step4-h.wav"); - - // Marine step sounds - PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-1.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-4.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-5.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-6.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-7.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-8.wav"); - PRECACHE_UNMODIFIED_SOUND(kDigestingSound); - - PRECACHE_UNMODIFIED_SOUND("player/pl_step1.wav"); // walk on concrete - PRECACHE_UNMODIFIED_SOUND("player/pl_step2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_step4.wav"); - - PRECACHE_SOUND("common/npc_step1.wav"); // NPC walk on concrete - PRECACHE_SOUND("common/npc_step2.wav"); - PRECACHE_SOUND("common/npc_step3.wav"); - PRECACHE_SOUND("common/npc_step4.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_metal1.wav"); // walk on metal - PRECACHE_UNMODIFIED_SOUND("player/pl_metal2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_metal3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_metal4.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_dirt1.wav"); // walk on dirt - PRECACHE_UNMODIFIED_SOUND("player/pl_dirt2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_dirt3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_dirt4.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_duct1.wav"); // walk in duct - PRECACHE_UNMODIFIED_SOUND("player/pl_duct2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_duct3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_duct4.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_grate1.wav"); // walk on grate - PRECACHE_UNMODIFIED_SOUND("player/pl_grate2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_grate3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_grate4.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_slosh1.wav"); // walk in shallow water - PRECACHE_UNMODIFIED_SOUND("player/pl_slosh2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_slosh3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_slosh4.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_tile1.wav"); // walk on tile - PRECACHE_UNMODIFIED_SOUND("player/pl_tile2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_tile3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_tile4.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_tile5.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_swim1.wav"); // breathe bubbles - PRECACHE_UNMODIFIED_SOUND("player/pl_swim2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_swim3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_swim4.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_ladder1.wav"); // climb ladder rung - PRECACHE_UNMODIFIED_SOUND("player/pl_ladder2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_ladder3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_ladder4.wav"); - - PRECACHE_UNMODIFIED_SOUND("player/pl_wade1.wav"); // wade in water - PRECACHE_UNMODIFIED_SOUND("player/pl_wade2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_wade3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_wade4.wav"); - - PRECACHE_SOUND("debris/wood1.wav"); // hit wood texture - PRECACHE_SOUND("debris/wood2.wav"); - PRECACHE_SOUND("debris/wood3.wav"); - - PRECACHE_UNMODIFIED_SOUND("plats/train_use1.wav"); // use a train - - PRECACHE_UNMODIFIED_SOUND("buttons/spark5.wav"); // hit computer texture - PRECACHE_UNMODIFIED_SOUND("buttons/spark6.wav"); - PRECACHE_SOUND("debris/glass1.wav"); - PRECACHE_SOUND("debris/glass2.wav"); - PRECACHE_SOUND("debris/glass3.wav"); - - PRECACHE_UNMODIFIED_SOUND( SOUND_FLASHLIGHT_ON ); - PRECACHE_UNMODIFIED_SOUND( SOUND_FLASHLIGHT_OFF ); - -// player gib sounds - PRECACHE_SOUND("common/bodysplat.wav"); - -// player pain sounds - PRECACHE_UNMODIFIED_SOUND("player/pl_pain2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_pain4.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_pain5.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_pain6.wav"); - PRECACHE_UNMODIFIED_SOUND("player/pl_pain7.wav"); - - PRECACHE_UNMODIFIED_MODEL("models/player.mdl"); - PRECACHE_UNMODIFIED_MODEL(kReadyRoomModel); - - PRECACHE_UNMODIFIED_MODEL(kMarineSoldierModel); - PRECACHE_UNMODIFIED_MODEL(kHeavySoldierModel); - PRECACHE_UNMODIFIED_MODEL(kAlienLevelOneModel); - - PRECACHE_UNMODIFIED_MODEL(kAlienLevelTwoModel); - PRECACHE_UNMODIFIED_MODEL(kAlienLevelThreeModel); - PRECACHE_UNMODIFIED_MODEL(kAlienLevelFourModel); - PRECACHE_UNMODIFIED_MODEL(kAlienLevelFiveModel); - - PRECACHE_UNMODIFIED_MODEL(kMarineCommanderModel); - PRECACHE_UNMODIFIED_MODEL(kAlienGestateModel); - - - // hud sounds, for marines and aliens (change AvHSharedUtil::AvHSHUGetCommonSoundName if these sound names change) - PRECACHE_UNMODIFIED_SOUND("common/wpn_hudoff.wav"); - PRECACHE_UNMODIFIED_SOUND("common/wpn_hudon.wav"); - PRECACHE_UNMODIFIED_SOUND("common/wpn_moveselect.wav"); - PRECACHE_UNMODIFIED_SOUND("common/wpn_select.wav"); - PRECACHE_UNMODIFIED_SOUND("common/wpn_denyselect.wav"); - - PRECACHE_UNMODIFIED_SOUND("common/wpn_hudoff-a.wav"); - PRECACHE_UNMODIFIED_SOUND("common/wpn_hudon-a.wav"); - PRECACHE_UNMODIFIED_SOUND("common/wpn_moveselect-a.wav"); - - PRECACHE_UNMODIFIED_SOUND("common/wpn_select-a.wav"); - PRECACHE_UNMODIFIED_SOUND("common/wpn_denyselect-a.wav"); - - // geiger sounds - PRECACHE_UNMODIFIED_SOUND("player/geiger6.wav"); - PRECACHE_UNMODIFIED_SOUND("player/geiger5.wav"); - PRECACHE_UNMODIFIED_SOUND("player/geiger4.wav"); - PRECACHE_UNMODIFIED_SOUND("player/geiger3.wav"); - PRECACHE_UNMODIFIED_SOUND("player/geiger2.wav"); - PRECACHE_UNMODIFIED_SOUND("player/geiger1.wav"); - - PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash1.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash2.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash3.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/digesting.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/membrane.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/hera_fog.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/spore.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/spore2.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/umbra.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/umbra2.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/webstrand.spr"); -} - -/* -================ -Sys_Error - - Engine is going to shut down, allows setting a breakpoint in game .dll to catch that occasion - ================ - */ - void Sys_Error( const char *error_string ) - { -#ifdef DEBUG -#ifdef WIN32 - // Default case, do nothing. MOD AUTHORS: Add code ( e.g., _asm { int 3 }; here to cause a breakpoint for debugging your game .dlls - _asm { int 3 }; -#endif -#else - char theDebugString[2056]; - sprintf(theDebugString, "Sys_Error: %s\n", error_string); - ALERT(at_error, theDebugString); -#endif - -} - -/* -=============== -GetGameDescription - -Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2 -=============== -*/ -const char *GetGameDescription() -{ - if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized - return g_pGameRules->GetGameDescription(); - else - return "Half-Life"; -} - -/* -================ -PlayerCustomization - -A new player customization has been registered on the server -UNDONE: This only sets the # of frames of the spray can logo -animation right now. -================ -*/ -void PlayerCustomization( edict_t *pEntity, customization_t *pCust ) -{ - entvars_t *pev = &pEntity->v; - CBasePlayer *pPlayer = (CBasePlayer *)GET_PRIVATE(pEntity); - - if (!pPlayer) - { - ALERT(at_console, "PlayerCustomization: Couldn't get player!\n"); - return; - } - - if (!pCust) - { - ALERT(at_console, "PlayerCustomization: NULL customization!\n"); - return; - } - - switch (pCust->resource.type) - { - case t_decal: - pPlayer->SetCustomDecalFrames(pCust->nUserData2); // Second int is max # of frames. - break; - case t_sound: - case t_skin: - case t_model: - // Ignore for now. - break; - default: - ALERT(at_console, "PlayerCustomization: Unknown customization type!\n"); - break; - } -} - -/* -================ -SpectatorConnect - -A spectator has joined the game -================ -*/ -void SpectatorConnect( edict_t *pEntity ) -{ - entvars_t *pev = &pEntity->v; - CBaseSpectator *pPlayer = (CBaseSpectator *)GET_PRIVATE(pEntity); - - if (pPlayer) - pPlayer->SpectatorConnect( ); -} - -/* -================ -SpectatorConnect - -A spectator has left the game -================ -*/ -void SpectatorDisconnect( edict_t *pEntity ) -{ - entvars_t *pev = &pEntity->v; - CBaseSpectator *pPlayer = (CBaseSpectator *)GET_PRIVATE(pEntity); - - if (pPlayer) - pPlayer->SpectatorDisconnect( ); -} - -/* -================ -SpectatorConnect - -A spectator has sent a usercmd -================ -*/ -void SpectatorThink( edict_t *pEntity ) -{ - entvars_t *pev = &pEntity->v; - CBaseSpectator *pPlayer = (CBaseSpectator *)GET_PRIVATE(pEntity); - - if (pPlayer) - pPlayer->SpectatorThink( ); -} - -//////////////////////////////////////////////////////// -// PAS and PVS routines for client messaging -// - -/* -================ -SetupVisibility - -A client can have a separate "view entity" indicating that his/her view should depend on the origin of that -view entity. If that's the case, then pViewEntity will be non-NULL and will be used. Otherwise, the current -entity's origin is used. Either is offset by the view_ofs to get the eye position. - -From the eye position, we set up the PAS and PVS to use for filtering network messages to the client. At this point, we could - override the actual PAS or PVS values, or use a different origin. - -NOTE: Do not cache the values of pas and pvs, as they depend on reusable memory in the engine, they are only good for this one frame -================ -*/ -void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char **pvs, unsigned char **pas ) -{ - Vector org; - edict_t *pView = pClient; - bool theUseSpecialPASOrigin = false; - Vector theSpecialPASOrigin; - - // Find the client's PVS - if ( pViewEntity ) - { - pView = pViewEntity; - } - - // Proxy sees and hears all - if ( (pClient->v.flags & FL_PROXY) || GetGameRules()->GetIsCheatEnabled(kcViewAll)) - { - //ALERT(at_logged, "Setting PVS and PAS to NULL for FL_PROXY\n"); - *pvs = NULL; // the spectator proxy sees - *pas = NULL; // and hears everything - return; - } - - // Tracking Spectators use the visibility of their target - CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient ); - if ( (pPlayer->pev->iuser2 != 0) && (pPlayer->m_hObserverTarget != NULL) && (pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER) && pPlayer->IsAlive()) - { - pView = pPlayer->m_hObserverTarget->edict(); - UTIL_SetOrigin( pPlayer->pev, pPlayer->m_hObserverTarget->pev->origin ); - } - - // Tracking Spectators use the visibility of their target -// AvHPlayer *thePlayer = dynamic_cast(CBaseEntity::Instance(pClient)); - - // cgc - something could be wrong here, crashing from Robin's spectator code...did I do something wrong? -// if(thePlayer) -// { -// if ( (thePlayer->pev->iuser2 != 0) && (thePlayer->m_hObserverTarget != NULL) ) -// { -// pView = thePlayer->m_hObserverTarget->edict(); -// } -// -// if(thePlayer->GetRole() == ROLE_COMMANDER) -// { -// thePlayer->GetSpecialPASOrigin(theSpecialPASOrigin); -// theUseSpecialPASOrigin = true; -// } -// } - - org = pView->v.origin + pView->v.view_ofs; - if ( pView->v.flags & FL_DUCKING ) - { - org = org + ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN ); - } - - *pvs = ENGINE_SET_PVS ( (float *)&org ); - - if(theUseSpecialPASOrigin) - { - *pas = ENGINE_SET_PAS ( (float *)&theSpecialPASOrigin ); - } - else - { - *pas = ENGINE_SET_PAS ( (float *)&org ); - } -} - -#include "common/entity_state.h" - -//CachedEntity gCachedEntities[kMaxPlayers][kMaxEntities]; -// -//void ResetCachedEntities() -//{ -// for(int i = 0; i < kMaxPlayers; i++) -// { -// for(int j = 0; j < kMaxEntities; j++) -// { -// gCachedEntities[i][j].ResetCachedEntity(); -// } -// } -//} - - - -// Array sizes ( memory is 5624 * 32 = 179968 bytes ) -#define MAX_CLIENTS ( 32 ) -#define MAX_ENTITIES ( 900 + MAX_CLIENTS * 15 ) - -// Re-check entities only this often once they are found to be in the PVS -// NOTE: We could have a separate timeout for players if we wanted to tweak the coherency time for them ( you'd have -// to check the entitynum against 1 to gpGlobals->maxclients ), etc. -//#define PVS_COHERENCY_TIME 1.0f - -extern float kPVSCoherencyTime; - -typedef struct -{ - bool mLastVisibility; - float m_fTimeEnteredPVS; -} ENTITYPVSSTATUS; - -typedef struct -{ - ENTITYPVSSTATUS m_Status[ MAX_ENTITIES ]; - - // Remember player leaf data so we can invalidate pvs history when leaf changes - int headnode; // -1 to use normal leaf check - int num_leafs; - short leafnums[ MAX_ENT_LEAFS ]; - -} PLAYERPVSSTATUS; - -static PLAYERPVSSTATUS g_PVSStatus[ MAX_CLIENTS ]; - -void ResetPlayerPVS( edict_t *client, int clientnum ) -{ - ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); - - PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; - - memset( pvs, 0, sizeof( PLAYERPVSSTATUS ) ); - - // Copy leaf data in right away - pvs->headnode = client->headnode; - pvs->num_leafs = client->num_leafs; - memcpy( pvs->leafnums, client->leafnums, sizeof( pvs->leafnums ) ); -} - -bool TimeToResetPVS( edict_t *client, int clientnum ) -{ - ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); - - PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; - - if ( (pvs->headnode != client->headnode) || (pvs->num_leafs != client->num_leafs)) - return true; - - if ( client->num_leafs > 0 ) - { - for ( int i = 0; i < client->num_leafs; i++ ) - { - if ( client->leafnums[ i ] != pvs->leafnums[ i ] ) - { - return true; - } - } - } - - // Still the same - return false; -} - -void MarkEntityInPVS( int clientnum, int entitynum, float time, bool inpvs ) -{ - ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); - ASSERT( entitynum >= 0 && entitynum < MAX_ENTITIES ); - - PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; - ENTITYPVSSTATUS *es = &pvs->m_Status[ entitynum ]; - - es->m_fTimeEnteredPVS = time; - es->mLastVisibility = inpvs; -} - -bool GetTimeToRecompute( int clientnum, int entitynum, float currenttime, bool& outCachedVisibility) -{ - ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); - ASSERT( entitynum >= 0 && entitynum < MAX_ENTITIES ); - - PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; - ENTITYPVSSTATUS *es = &pvs->m_Status[ entitynum ]; - - bool theTimeToRecompute = false; - - int theUseCaching = BALANCE_VAR(kDebugServerCaching); - if(theUseCaching) - { - // Always recompute players? - if((entitynum > 0) && (entitynum < MAX_CLIENTS)) - { - theTimeToRecompute = true; - } - // If we haven't computed for awhile, or our PVS has been reset - else if((currenttime > (es->m_fTimeEnteredPVS + kPVSCoherencyTime)) || (es->m_fTimeEnteredPVS == 0.0f)) - { - theTimeToRecompute = true; - } - else - { - outCachedVisibility = es->mLastVisibility; - } - } - else - { - theTimeToRecompute = true; - } - - return theTimeToRecompute; -} - -bool CheckEntityRecentlyInPVS( int clientnum, int entitynum, float currenttime ) -{ - ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); - ASSERT( entitynum >= 0 && entitynum < MAX_ENTITIES ); - - PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; - ENTITYPVSSTATUS *es = &pvs->m_Status[ entitynum ]; - - int theUseCaching = BALANCE_VAR(kDebugServerCaching); - if(!theUseCaching) - { - return false; - } - - // Wasn't in pvs before, sigh... - if ( es->m_fTimeEnteredPVS == 0.0f ) - { - return false; - } - - // If we've been saying yes for kPVSCoherencyTime, say no now instead so that we'll do a full check - if ( es->m_fTimeEnteredPVS + kPVSCoherencyTime < currenttime ) - { - return false; - } - - // Keep assuming it's in the pvs for a bit of time (trivial inclusion) - return true; -} - - -int kNumReturn0 = 0; -int kNumReturn1 = 0; -int kNumCached = 0; -int kNumComputed = 0; -int kMaxE = 0; -int kNumEntsProcessedForPlayerOne = 0; -int kMaxEProcessedForPlayerOne = 0; -extern int kServerFrameRate; - -// ENGINE_CHECK_VISIBILITY, from Yahn, with his comments: -// -// Here's the engine code. You'll note a slow path if the ent leaf cache is missed, which causes a recomputation via the much more expensive -// CM_HeadnodeVisible call. That does change the cache data on the entity, so that's likely your problem with blinking if you are doing some -// kind of memcpy operation on the old data. -//#ifdef 0 -//int DLL_CALLBACK SV_CheckVisibility( const struct edict_s *entity, unsigned char *pset ) -//{ -// int i; -// qboolean visible; -// edict_t *e; -// -// if ( !pset ) -// return 1; -// -// if ( entity->headnode >= 0 ) // -// { -// // we use num_leafs as a cache/counter, it will circle around, but so what -// // if we don't find the leaf we want here, we'll end up doing the full test anyway -// int leaf; -// i = 0; -// -// do -// { -// leaf = entity->leafnums[i]; -// // Cache is all -1 to start... -// if ( leaf == -1 ) -// break; -// -// if ( pset[ leaf >> 3 ] & (1 << (leaf & 7 ) ) ) -// { -// return 1; -// } -// -// i++; -// if ( i >= MAX_ENT_LEAFS ) -// break; -// -// } while ( 1 ); -// -// // Didn't find it in "cache" -// -// visible = CM_HeadnodeVisible( sv.worldmodel->nodes + entity->headnode, pset, &leaf ); -// if ( !visible ) -// { -// return 0; -// } -// -// // Cache leaf that was visible -// // -// e = ( edict_t * )entity; -// -// e->leafnums[ entity->num_leafs ] = (short)leaf; -// e->num_leafs = ( entity->num_leafs + 1 ) % MAX_ENT_LEAFS; -// -// return 2; -// } -// else -// { -// for (i=0 ; i < entity->num_leafs ; i++) -// { -// if ( pset[entity->leafnums[i] >> 3] & (1 << (entity->leafnums[i]&7) )) -// break; -// } -// -// visible = ( i < entity->num_leafs ); -// if ( !visible ) -// { -// return 0; -// } -// } -// -// return 1; -//} -//#endif - -// defaults for clientinfo messages -#define DEFAULT_VIEWHEIGHT 28 - -/* -=================== -CreateBaseline - -Creates baselines used for network encoding, especially for player data since players are not spawned until connect time. -=================== -*/ -void CreateBaseline( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ) -{ - baseline->origin = entity->v.origin; - baseline->angles = entity->v.angles; - baseline->frame = entity->v.frame; - baseline->skin = (short)entity->v.skin; - - // render information - baseline->rendermode = (byte)entity->v.rendermode; - baseline->renderamt = (byte)entity->v.renderamt; - baseline->rendercolor.r = (byte)entity->v.rendercolor.x; - baseline->rendercolor.g = (byte)entity->v.rendercolor.y; - baseline->rendercolor.b = (byte)entity->v.rendercolor.z; - baseline->renderfx = (byte)entity->v.renderfx; - - if ( player ) - { - baseline->mins = player_mins; - baseline->maxs = player_maxs; - - baseline->colormap = eindex; - baseline->modelindex = playermodelindex; - baseline->friction = 1.0; - baseline->movetype = MOVETYPE_WALK; - - baseline->scale = entity->v.scale; - baseline->solid = SOLID_SLIDEBOX; - baseline->framerate = 1.0; - baseline->gravity = 1.0; - - } - else - { - baseline->mins = entity->v.mins; - baseline->maxs = entity->v.maxs; - - baseline->colormap = 0; - baseline->modelindex = entity->v.modelindex;//SV_ModelIndex(pr_strings + entity->v.model); - baseline->movetype = entity->v.movetype; - - baseline->scale = entity->v.scale; - baseline->solid = entity->v.solid; - baseline->framerate = entity->v.framerate; - baseline->gravity = entity->v.gravity; - } -} - -typedef struct -{ - char name[32]; - int field; -} entity_field_alias_t; - -#define FIELD_ORIGIN0 0 -#define FIELD_ORIGIN1 1 -#define FIELD_ORIGIN2 2 -#define FIELD_ANGLES0 3 -#define FIELD_ANGLES1 4 -#define FIELD_ANGLES2 5 - -static entity_field_alias_t entity_field_alias[]= -{ - { "origin[0]", 0 }, - { "origin[1]", 0 }, - { "origin[2]", 0 }, - { "angles[0]", 0 }, - { "angles[1]", 0 }, - { "angles[2]", 0 }, -}; - -void Entity_FieldInit( struct delta_s *pFields ) -{ - entity_field_alias[ FIELD_ORIGIN0 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ORIGIN0 ].name ); - entity_field_alias[ FIELD_ORIGIN1 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ORIGIN1 ].name ); - entity_field_alias[ FIELD_ORIGIN2 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ORIGIN2 ].name ); - entity_field_alias[ FIELD_ANGLES0 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ANGLES0 ].name ); - entity_field_alias[ FIELD_ANGLES1 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ANGLES1 ].name ); - entity_field_alias[ FIELD_ANGLES2 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ANGLES2 ].name ); -} - -/* -================== -Entity_Encode - -Callback for sending entity_state_t info over network. -FIXME: Move to script -================== -*/ -void Entity_Encode( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) -{ - entity_state_t *f, *t; - int localplayer = 0; - static int initialized = 0; - - if ( !initialized ) - { - Entity_FieldInit( pFields ); - initialized = 1; - } - - f = (entity_state_t *)from; - t = (entity_state_t *)to; - - // Never send origin to local player, it's sent with more resolution in clientdata_t structure - localplayer = ( t->number - 1 ) == ENGINE_CURRENT_PLAYER(); - if ( localplayer ) - { - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); - } - - if ( ( t->impacttime != 0 ) && ( t->starttime != 0 ) ) - { - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); - - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES0 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES1 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES2 ].field ); - } - - if ( ( t->movetype == MOVETYPE_FOLLOW ) && - ( t->aiment != 0 ) ) - { - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); - } - else if ( t->aiment != f->aiment ) - { - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); - } -} - -static entity_field_alias_t player_field_alias[]= -{ - { "origin[0]", 0 }, - { "origin[1]", 0 }, - { "origin[2]", 0 }, -}; - -void Player_FieldInit( struct delta_s *pFields ) -{ - player_field_alias[ FIELD_ORIGIN0 ].field = DELTA_FINDFIELD( pFields, player_field_alias[ FIELD_ORIGIN0 ].name ); - player_field_alias[ FIELD_ORIGIN1 ].field = DELTA_FINDFIELD( pFields, player_field_alias[ FIELD_ORIGIN1 ].name ); - player_field_alias[ FIELD_ORIGIN2 ].field = DELTA_FINDFIELD( pFields, player_field_alias[ FIELD_ORIGIN2 ].name ); -} - -/* -================== -Player_Encode - -Callback for sending entity_state_t for players info over network. -================== -*/ -void Player_Encode( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) -{ - entity_state_t *f, *t; - int localplayer = 0; - static int initialized = 0; - - if ( !initialized ) - { - Player_FieldInit( pFields ); - initialized = 1; - } - - f = (entity_state_t *)from; - t = (entity_state_t *)to; - - // Never send origin to local player, it's sent with more resolution in clientdata_t structure - localplayer = ( t->number - 1 ) == ENGINE_CURRENT_PLAYER(); - if ( localplayer ) - { - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); - } - - if ( ( t->movetype == MOVETYPE_FOLLOW ) && - ( t->aiment != 0 ) ) - { - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); - } - else if ( t->aiment != f->aiment ) - { - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); - } -} - -#define CUSTOMFIELD_ORIGIN0 0 -#define CUSTOMFIELD_ORIGIN1 1 -#define CUSTOMFIELD_ORIGIN2 2 -#define CUSTOMFIELD_ANGLES0 3 -#define CUSTOMFIELD_ANGLES1 4 -#define CUSTOMFIELD_ANGLES2 5 -#define CUSTOMFIELD_SKIN 6 -#define CUSTOMFIELD_SEQUENCE 7 -#define CUSTOMFIELD_ANIMTIME 8 - -entity_field_alias_t custom_entity_field_alias[]= -{ - { "origin[0]", 0 }, - { "origin[1]", 0 }, - { "origin[2]", 0 }, - { "angles[0]", 0 }, - { "angles[1]", 0 }, - { "angles[2]", 0 }, - { "skin", 0 }, - { "sequence", 0 }, - { "animtime", 0 }, -}; - -void Custom_Entity_FieldInit( struct delta_s *pFields ) -{ - custom_entity_field_alias[ CUSTOMFIELD_ORIGIN0 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN0 ].name ); - custom_entity_field_alias[ CUSTOMFIELD_ORIGIN1 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN1 ].name ); - custom_entity_field_alias[ CUSTOMFIELD_ORIGIN2 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN2 ].name ); - custom_entity_field_alias[ CUSTOMFIELD_ANGLES0 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES0 ].name ); - custom_entity_field_alias[ CUSTOMFIELD_ANGLES1 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES1 ].name ); - custom_entity_field_alias[ CUSTOMFIELD_ANGLES2 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES2 ].name ); - custom_entity_field_alias[ CUSTOMFIELD_SKIN ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_SKIN ].name ); - custom_entity_field_alias[ CUSTOMFIELD_SEQUENCE ].field= DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_SEQUENCE ].name ); - custom_entity_field_alias[ CUSTOMFIELD_ANIMTIME ].field= DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANIMTIME ].name ); -} - -/* -================== -Custom_Encode - -Callback for sending entity_state_t info ( for custom entities ) over network. -FIXME: Move to script -================== -*/ -void Custom_Encode( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) -{ - entity_state_t *f, *t; - int beamType; - static int initialized = 0; - - if ( !initialized ) - { - Custom_Entity_FieldInit( pFields ); - initialized = 1; - } - - f = (entity_state_t *)from; - t = (entity_state_t *)to; - - beamType = t->rendermode & 0x0f; - - if ( beamType != BEAM_POINTS && beamType != BEAM_ENTPOINT ) - { - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN0 ].field ); - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN1 ].field ); - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN2 ].field ); - } - - if ( beamType != BEAM_POINTS ) - { - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES0 ].field ); - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES1 ].field ); - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES2 ].field ); - } - - if ( beamType != BEAM_ENTS && beamType != BEAM_ENTPOINT ) - { - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_SKIN ].field ); - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_SEQUENCE ].field ); - } - - // animtime is compared by rounding first - // see if we really shouldn't actually send it - if ( (int)f->animtime == (int)t->animtime ) - { - DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANIMTIME ].field ); - } -} - -/* -================= -RegisterEncoders - -Allows game .dll to override network encoding of certain types of entities and tweak values, etc. -================= -*/ -void RegisterEncoders( void ) -{ - DELTA_ADDENCODER( "Entity_Encode", Entity_Encode ); - DELTA_ADDENCODER( "Custom_Encode", Custom_Encode ); - DELTA_ADDENCODER( "Player_Encode", Player_Encode ); -} - -int GetWeaponData( struct edict_s *player, struct weapon_data_s *info ) -{ - int i; - weapon_data_t *item; - entvars_t *pev = &player->v; - CBasePlayer *pl = ( CBasePlayer *) CBasePlayer::Instance( pev ); - AvHPlayer* thePlayer = dynamic_cast(pl); - CBasePlayerWeapon *gun; - - ItemInfo II; - - memset( info, 0, 32 * sizeof( weapon_data_t ) ); - - if ( !pl ) - return 1; - - // go through all of the weapons and make a list of the ones to pack - for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - if ( pl->m_rgpPlayerItems[ i ] ) - { - // there's a weapon here. Should I pack it? - CBasePlayerItem *pPlayerItem = pl->m_rgpPlayerItems[ i ]; - - while ( pPlayerItem ) - { - gun = (CBasePlayerWeapon *)pPlayerItem->GetWeaponPtr(); - if ( gun && gun->UseDecrement() ) - { - // Get The ID. - memset( &II, 0, sizeof( II ) ); - gun->GetItemInfo( &II ); - - ASSERT((II.iId >= 0) && (II.iId < 32)); - - if ( II.iId >= 0 && II.iId < 32 ) - { - item = &info[ II.iId ]; - - item->m_iId = II.iId; - item->m_iClip = gun->m_iClip; - - item->m_flTimeWeaponIdle = max( gun->m_flTimeWeaponIdle, -0.001f ); - item->m_flNextPrimaryAttack = max( gun->m_flNextPrimaryAttack, -0.001f ); - item->m_flNextSecondaryAttack = max( gun->m_flNextSecondaryAttack, -0.001f ); - item->m_fInReload = gun->m_fInReload; - - //thePlayer->SetDebugCSP(item); - - //item->m_fInSpecialReload = gun->m_fInSpecialReload; - //item->fuser1 = max( gun->pev->fuser1, -0.001 ); - item->fuser2 = gun->m_flStartThrow; - item->fuser3 = gun->m_flReleaseThrow; - //item->iuser1 = gun->m_chargeReady; - //item->iuser2 = gun->m_fInAttack; - - // Pass along enabled state in iuser3 (for when hives and ensnare enable and disable weapons) - item->iuser3 = gun->pev->iuser3; - - //item->m_flPumpTime = max( gun->m_flPumpTime, -0.001 ); - } - } - pPlayerItem = pPlayerItem->m_pNext; - } - } - } - return 1; -} - -/* -================= -UpdateClientData - -Data sent to current client only -engine sets cd to 0 before calling. -================= -*/ -void UpdateClientData ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ) -{ - cd->flags = ent->v.flags; - cd->health = ent->v.health; - - cd->viewmodel = MODEL_INDEX( STRING( ent->v.viewmodel ) ); - - cd->waterlevel = ent->v.waterlevel; - cd->watertype = ent->v.watertype; - cd->weapons = ent->v.weapons; - - // Vectors - cd->origin = ent->v.origin; - cd->velocity = ent->v.velocity; - cd->view_ofs = ent->v.view_ofs; - cd->punchangle = ent->v.punchangle; - - cd->bInDuck = ent->v.bInDuck; - cd->flTimeStepSound = ent->v.flTimeStepSound; - cd->flDuckTime = ent->v.flDuckTime; - cd->flSwimTime = ent->v.flSwimTime; - cd->waterjumptime = ent->v.teleport_time; - - strcpy( cd->physinfo, ENGINE_GETPHYSINFO( ent ) ); - - cd->maxspeed = ent->v.maxspeed; - cd->fov = ent->v.fov; - cd->weaponanim = ent->v.weaponanim; - - cd->pushmsec = ent->v.pushmsec; - - // Spectator - cd->iuser1 = ent->v.iuser1; - cd->iuser2 = ent->v.iuser2; - - // AvH specials - cd->iuser3 = ent->v.iuser3; - cd->iuser4 = ent->v.iuser4; - cd->fuser1 = ent->v.fuser1; - cd->fuser2 = ent->v.fuser2; - cd->fuser3 = ent->v.fuser3; - - // Added by mmcguire for oriented skulk player models. - cd->vuser1 = ent->v.vuser1; - - // Added for extra player info for first-person spectating - cd->vuser4 = ent->v.vuser4; - - if ( sendweapons ) - { - entvars_t *pev = (entvars_t *)&ent->v; - CBasePlayer *pl = ( CBasePlayer *) CBasePlayer::Instance( pev ); - - if ( pl ) - { - cd->m_flNextAttack = pl->m_flNextAttack; - //cd->fuser2 = pl->m_flNextAmmoBurn; - //cd->fuser3 = pl->m_flAmmoStartCharge; - //cd->vuser1.x = pl->ammo_9mm; - //cd->vuser1.y = pl->ammo_357; - //cd->vuser1.z = pl->ammo_argrens; - //cd->ammo_nails = pl->ammo_bolts; - //cd->ammo_shells = pl->ammo_buckshot; - //cd->ammo_rockets = pl->ammo_rockets; - //cd->ammo_cells = pl->ammo_uranium; - //cd->vuser2.x = pl->ammo_hornets; - - if ( pl->m_pActiveItem ) - { - CBasePlayerWeapon *gun; - gun = (CBasePlayerWeapon *)pl->m_pActiveItem->GetWeaponPtr(); - if ( gun && gun->UseDecrement() ) - { - ItemInfo II; - memset( &II, 0, sizeof( II ) ); - gun->GetItemInfo( &II ); - - cd->m_iId = II.iId; - -// cd->vuser3.z = gun->m_iSecondaryAmmoType; -// cd->vuser4.x = gun->m_iPrimaryAmmoType; -// cd->vuser4.y = pl->m_rgAmmo[gun->m_iPrimaryAmmoType]; -// cd->vuser4.z = pl->m_rgAmmo[gun->m_iSecondaryAmmoType]; -// -// if ( pl->m_pActiveItem->m_iId == WEAPON_RPG ) -// { -// cd->vuser2.y = ( ( CRpg * )pl->m_pActiveItem)->m_fSpotActive; -// cd->vuser2.z = ( ( CRpg * )pl->m_pActiveItem)->m_cActiveRockets; -// } - } - } - } - } -} - -/* -================= -CmdStart - -We're about to run this usercmd for the specified player. We can set up groupinfo and masking here, etc. -This is the time to examine the usercmd for anything extra. This call happens even if think does not. -================= -*/ -void CmdStart( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ) -{ - entvars_t *pev = (entvars_t *)&player->v; - AvHPlayer *pl = ( AvHPlayer *) CBasePlayer::Instance( pev ); - - if( !pl ) - return; - - pl->SetCurrentCommand(cmd); - - pl->SetSizeForUser3(); - - if ( pl->pev->groupinfo != 0 ) - { - UTIL_SetGroupTrace( pl->pev->groupinfo, GROUP_OP_AND ); - } - - pl->random_seed = random_seed; -} - -/* -================= -CmdEnd - -Each cmdstart is exactly matched with a cmd end, clean up any group trace flags, etc. here -================= -*/ -void CmdEnd ( const edict_t *player ) -{ - entvars_t *pev = (entvars_t *)&player->v; - AvHPlayer *pl = ( AvHPlayer *) CBasePlayer::Instance( pev ); - - if( !pl ) - return; - - pl->SetSizeForUser3(); - - if ( pl->pev->groupinfo != 0 ) - { - UTIL_UnsetGroupTrace(); - } -} - -/* -================================ -ConnectionlessPacket - - Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max - size of the response_buffer, so you must zero it out if you choose not to respond. -================================ -*/ -int ConnectionlessPacket( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ) -{ - // Parse stuff from args - int max_buffer_size = *response_buffer_size; - - // Zero it out since we aren't going to respond. - // If we wanted to response, we'd write data into response_buffer - *response_buffer_size = 0; - - // Since we don't listen for anything here, just respond that it's a bogus message - // If we didn't reject the message, we'd return 1 for success instead. - return 0; -} - -/* -================================ -GetHullBounds - - Engine calls this to enumerate player collision hulls, for prediction. Return 0 if the hullnumber doesn't exist. -================================ -*/ -int GetHullBounds( int hullnumber, float *mins, float *maxs ) -{ - int iret = 0; - - switch ( hullnumber ) - { - case 0: - HULL0_MIN.CopyToArray(mins); - HULL0_MAX.CopyToArray(maxs); - iret = 1; - break; - - case 1: - HULL1_MIN.CopyToArray(mins); - HULL1_MAX.CopyToArray(maxs); - iret = 1; - break; - - case 2: - HULL2_MIN.CopyToArray(mins); - HULL2_MAX.CopyToArray(maxs); - iret = 1; - break; - - case 3: - HULL3_MIN.CopyToArray(mins); - HULL3_MAX.CopyToArray(maxs); - iret = 1; - break; - } - - return iret; -} - -/* -================================ -CreateInstancedBaselines - -Create pseudo-baselines for items that aren't placed in the map at spawn time, but which are likely -to be created during play ( e.g., grenades, ammo packs, projectiles, corpses, etc. ) -================================ -*/ -void CreateInstancedBaselines ( void ) -{ - int iret = 0; - entity_state_t state; - - memset( &state, 0, sizeof( state ) ); - - // Create any additional baselines here for things like grendates, etc. - // iret = ENGINE_INSTANCE_BASELINE( pc->pev->classname, &state ); - - // Destroy objects. - //UTIL_Remove( pc ); -} - -/* -================================ -InconsistentFile - -One of the ENGINE_FORCE_UNMODIFIED files failed the consistency check for the specified player - Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters ) -================================ -*/ -int InconsistentFile( const edict_t *player, const char *filename, char *disconnect_message ) -{ - // Server doesn't care? - if ( CVAR_GET_FLOAT( "mp_consistency" ) != 1 ) - return 0; - - // Default behavior is to kick the player - sprintf( disconnect_message, "Server is enforcing file consistency for %s\n", filename ); - - // Kick now with specified disconnect message. - return 1; -} - -/* -================================ -AllowLagCompensation - - The game .dll should return 1 if lag compensation should be allowed ( could also just set - the sv_unlag cvar. - Most games right now should return 0, until client-side weapon prediction code is written - and tested for them ( note you can predict weapons, but not do lag compensation, too, - if you want. -================================ -*/ -int AllowLagCompensation( void ) -{ - return 1; -} - -inline bool AvHDetermineVisibility(struct entity_state_s *state, int e, edict_t *ent, edict_t *host, unsigned char *pSet, bool& outIsParticleSystemEntity, bool& outIsParticleSystemEntityVisible) -{ - bool theIsVisible = false; - bool theIsParticleSystemEntity = (ent->v.classname == MAKE_STRING(kesParticlesCustom)); - outIsParticleSystemEntity = theIsParticleSystemEntity; - outIsParticleSystemEntityVisible = false; - - // Always propagate ourself - if(ent != host) - { - //if(GET_RUN_CODE(256)) - - bool theEntityIsAWeldable = ent->v.iuser3 == AVH_USER3_WELD;//(ent->v.classname == MAKE_STRING(kesWeldable)); - bool theIsNoBuild = ent->v.iuser3 == AVH_USER3_NOBUILD;//(ent->v.classname == MAKE_STRING(kesNoBuild)); - - // Don't send if EF_NODRAW objects that aren't the host (with some exceptions) - // This is a little convoluted because of performance (this function gets called many thousands of time per server tick) - - // Elven - edited out the !(ent->v.effects & EF_NODRAW) because it will always evaluate to true. - // Reasoning: if (v.effects & EF_NODRAW) and (ent!=host) were ever true, there would have been a return call in - // AddToFullPack before this function was called. - // Therefore, (ent->v.effects & EF_NODRAW) will always be false and !false will always be true. - // puzl - undid this change as it was causing problems in comm mode. Structures, players etc. were not visible to the comm. - if(!(ent->v.effects & EF_NODRAW) || theEntityIsAWeldable || theIsNoBuild || (ent->v.classname == MAKE_STRING(kesMP3Audio))) - { - // This is duplicated from the above line for possible efficiency - bool theIsMp3Entity = (ent->v.classname == MAKE_STRING(kesMP3Audio)); - - // Ignore ents without valid / visible models - // ...but don't cull out particle systems ever. - // This should use an approximate bounding radius and cull it with the PVS normally but - // I can't figure out how to make it work. This would only cause more net traffic if - // the particle system entity was out of sight and moving around due to map triggers, not - // through normal operation - if(ent->v.modelindex || STRING(ent->v.model) || theIsParticleSystemEntity || theIsMp3Entity) - { - if(theIsMp3Entity) - { - CBaseEntity* theEntity = CBaseEntity::Instance(ent); - AvHMP3Audio* theMP3Audio = dynamic_cast(theEntity); - if(theMP3Audio) - { - float theDistanceToEntity = VectorDistance(host->v.origin, ent->v.origin); - int theFadeDistance = theMP3Audio->GetFadeDistance(); - if((theFadeDistance <= 0) || (theDistanceToEntity <= theFadeDistance)) - { - theIsVisible = true; - } - } - } - - // If we're in top down - bool thePlayerInTopDown = (host->v.iuser4 & MASK_TOPDOWN); - if(thePlayerInTopDown) - { - // Possible visible entities are world entities, sighted enemy entities, or entities on our team - bool theEntityIsWorldEntity = (ent->v.team == 0) && (ent->v.iuser3 != AVH_USER3_HIVE); - bool theIsSighted = (ent->v.iuser4 & MASK_VIS_SIGHTED); - bool theOnSameTeam = (host->v.team == ent->v.team); - - if(theEntityIsWorldEntity || theIsSighted || theOnSameTeam) - { - CBaseEntity* theEntity = CBaseEntity::Instance(ent); - bool theIsDoor = (dynamic_cast(theEntity) != NULL); - - // If entity is in front of the player and within visibility range - vec3_t theEntityOrigin = ent->v.origin; - float theMagnitude = theEntityOrigin.x + theEntityOrigin.y + theEntityOrigin.z; - if((theMagnitude < 5.0f) || theIsDoor) - { - theEntityOrigin.x = (ent->v.absmin.x + ent->v.absmax.x)/2.0f; - theEntityOrigin.y = (ent->v.absmin.y + ent->v.absmax.y)/2.0f; - theEntityOrigin.z = (ent->v.absmin.z + ent->v.absmax.z)/2.0f; - } - bool theIsRelevantForTopDownPlayer = AvHSUGetIsRelevantForTopDownPlayer(host->v.origin, theEntityOrigin); - if(!theIsRelevantForTopDownPlayer) - { - AvHPlayer* theReceivingPlayer = dynamic_cast(CBaseEntity::Instance(host)); - - // If the entity is selected, it's relevant - if(theEntity && theReceivingPlayer && theReceivingPlayer->GetIsSelected(theEntity->entindex())) - { - theIsRelevantForTopDownPlayer = true; - } - } - if(theIsRelevantForTopDownPlayer) - { - theIsVisible = true; - if(theIsParticleSystemEntity) - { - outIsParticleSystemEntityVisible = true; - } - } - } - } -// else -// { -// // Check visibility with the engine -// if(ENGINE_CHECK_VISIBILITY((const struct edict_s *)ent, pSet)) -// { -// theIsVisible = true; -// -// // If it's a particle system entity, save visibility for it -// if(theIsParticleSystemEntity) -// { -// outIsParticleSystemEntityVisible = true; -// } -// } -// else if(theIsParticleSystemEntity) -// { -// outIsParticleSystemEntityVisible = false; -// } -// } - } - } - } - else - { - theIsVisible = true; - } - - return theIsVisible; -} - -/* -AddToFullPack - -Return 1 if the entity state has been filled in for the ent and the entity will be propagated to the client, 0 otherwise - -state is the server maintained copy of the state info that is transmitted to the client -a MOD could alter values copied into state to send the "host" a different look for a particular entity update, etc. -e and ent are the entity that is being added to the update, if 1 is returned -host is the player's edict of the player whom we are sending the update to -player is 1 if the ent/e is a player and 0 otherwise -pSet is either the PAS or PVS that we previous set up. We can use it to ask the engine to filter the entity against the PAS or PVS. -we could also use the pas/ pvs that we set in SetupVisibility, if we wanted to. Caching the value is valid in that case, but still only for the current frame -*/ -int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ) -{ - int i; - - if(e > kMaxEProcessedForPlayerOne) - { - kNumEntsProcessedForPlayerOne++; - kMaxEProcessedForPlayerOne = e; - } - - if((ent != host) && !(GET_RUN_CODE(512))) - { - kNumReturn0++; - return 0; - } - - // This is a common case - // Ignore ents without valid / visible models - if ( !ent->v.modelindex || !STRING( ent->v.model ) ) - { - kNumReturn0++; - return 0; - } - - // don't send if flagged for NODRAW and it's not the host getting the message - if ( ( ent->v.effects & EF_NODRAW ) && - ( ent != host ) ) - { - kNumReturn0++; - return 0; - } - - // Don't send spectators to other players - if ( ( ent->v.flags & FL_SPECTATOR ) && ( ent != host ) ) - { - kNumReturn0++; - return 0; - } - - AvHUser3 theUser3 = (AvHUser3)ent->v.iuser3; - bool theCanBeTargetted = ent->v.targetname != 0; - - //if(!strcmp(theEntNameString, "func_illusionary")) - if(theUser3 == AVH_USER3_FUNC_ILLUSIONARY) - { - // Check invisibility flags - if(host->v.iuser4 & MASK_TOPDOWN) - { - if(FBitSet(ent->v.spawnflags, 1)) - { - if(pSet != NULL) - { - kNumReturn0++; - return 0; - } - } - } - // If we're not commander, and "invisible for player" is set - else - { - if(FBitSet(ent->v.spawnflags, 2)) - { - if(pSet != NULL) - { - kNumReturn0++; - return 0; - } - } - } - } - - - // Some entities can be determined to be visible without the engine check - bool theIsParticleSystemEntity = false; - bool theIsParticleSystemEntityVisible = false; - bool theIsVisible = AvHDetermineVisibility(state, e, ent, host, pSet, theIsParticleSystemEntity, theIsParticleSystemEntityVisible); - if(!theIsVisible) - { - // Check to see if the player has left their previous leaf. If so, reset player's PVS info. - int hostnum = ENTINDEX( host ) - 1; - if ( TimeToResetPVS( host, hostnum ) ) - { - ResetPlayerPVS( host, hostnum ); - } - - // Ignore if not the host and not touching a PVS/PAS leaf - // If pSet is NULL, then the test will always succeed and the entity will be added to the update - if ( ent != host ) - { - if(GetTimeToRecompute( hostnum, e, gpGlobals->time, theIsVisible)) - { - // Do time consuming check - theIsVisible = ENGINE_CHECK_VISIBILITY( (const struct edict_s *)ent, pSet ); - - MarkEntityInPVS( hostnum, e, gpGlobals->time, theIsVisible); - - kNumComputed++; - } - - if(!theIsVisible) - { - kNumReturn0++; - return 0; - } - } - - // Don't send entity to local client if the client says it's predicting the entity itself. - if ( ent->v.flags & FL_SKIPLOCALHOST ) - { - if ( ( hostflags & 1 ) && ( ent->v.owner == host ) ) - { - kNumReturn0++; - return 0; - } - } - - if ( host->v.groupinfo ) - { - UTIL_SetGroupTrace( host->v.groupinfo, GROUP_OP_AND ); - - // Should always be set, of course - if ( ent->v.groupinfo ) - { - if ( g_groupop == GROUP_OP_AND ) - { - if ( !(ent->v.groupinfo & host->v.groupinfo ) ) - { - kNumReturn0++; - return 0; - } - } - else if ( g_groupop == GROUP_OP_NAND ) - { - if ( ent->v.groupinfo & host->v.groupinfo ) - { - kNumReturn0++; - return 0; - } - } - } - - UTIL_UnsetGroupTrace(); - } - } - - memset( state, 0, sizeof( *state ) ); - - // Assign index so we can track this entity from frame to frame and - // delta from it. - state->number = e; - state->entityType = ENTITY_NORMAL; - - // Flag custom entities. - if ( ent->v.flags & FL_CUSTOMENTITY ) - { - state->entityType = ENTITY_BEAM; - } - - // - // Copy state data - // - - // Round animtime to nearest millisecond - state->animtime = (int)(1000.0 * ent->v.animtime ) / 1000.0; - - //memcpy( state->origin, ent->v.origin, 3 * sizeof( float ) ); - state->origin = ent->v.origin; - - //memcpy( state->angles, ent->v.angles, 3 * sizeof( float ) ); - state->angles = ent->v.angles; - - //memcpy( state->mins, ent->v.mins, 3 * sizeof( float ) ); - state->mins = ent->v.mins; - - //memcpy( state->maxs, ent->v.maxs, 3 * sizeof( float ) ); - state->maxs = ent->v.maxs; - - //memcpy( state->startpos, ent->v.startpos, 3 * sizeof( float ) ); - state->startpos = ent->v.startpos; - - //memcpy( state->endpos, ent->v.endpos, 3 * sizeof( float ) ); - state->endpos = ent->v.endpos; - - state->impacttime = ent->v.impacttime; - state->starttime = ent->v.starttime; - - state->modelindex = ent->v.modelindex; - - state->frame = ent->v.frame; - - state->skin = ent->v.skin; - state->effects = ent->v.effects; - - // This non-player entity is being moved by the game .dll and not the physics simulation system - // make sure that we interpolate it's position on the client if it moves - if ( !player && - ent->v.animtime && - ent->v.velocity[ 0 ] == 0 && - ent->v.velocity[ 1 ] == 0 && - ent->v.velocity[ 2 ] == 0 ) - { - state->eflags |= EFLAG_SLERP; - } - - state->scale = ent->v.scale; - state->solid = ent->v.solid; - state->colormap = ent->v.colormap; - state->movetype = ent->v.movetype; - state->sequence = ent->v.sequence; - state->framerate = ent->v.framerate; - state->body = ent->v.body; - - for (i = 0; i < 4; i++) - { - state->controller[i] = ent->v.controller[i]; - } - - for (i = 0; i < 2; i++) - { - state->blending[i] = ent->v.blending[i]; - } - - state->rendermode = ent->v.rendermode; - state->renderamt = ent->v.renderamt; - state->renderfx = ent->v.renderfx; - state->rendercolor.r = ent->v.rendercolor[0]; - state->rendercolor.g = ent->v.rendercolor[1]; - state->rendercolor.b = ent->v.rendercolor[2]; - - - // If classname indicates an entity that fades depending on viewing player role - const char* theEntNameString = STRING(ent->v.classname); - - if(!player && AvHSSUGetIsClassNameFadeable(theEntNameString)) - { - CBaseEntity* theSeethrough = CBaseEntity::Instance(ent); - ASSERT(theSeethrough); - - // Default to player is full visibility - state->rendermode = kRenderNormal; - - int theAlphaValue = theSeethrough->pev->fuser2; - if(host->v.iuser4 & MASK_TOPDOWN) - { - theAlphaValue = theSeethrough->pev->fuser1; - } - - if(theAlphaValue != 255) - { - //state->rendermode = kRenderTransAdd; - state->rendermode = kRenderTransTexture; - state->renderamt = theAlphaValue; - if(state->renderamt == 0) - { - state->effects |= EF_NODRAW; - } - - if(host->v.iuser4 & MASK_TOPDOWN) - { - state->solid = SOLID_NOT; - } - - } - - } - - // Inactive hives should be drawn for players on their team - if((ent->v.iuser3 == AVH_USER3_HIVE) && (!GetHasUpgrade(ent->v.iuser4, MASK_BUILDABLE))) - { - // Assumes that aliens of both teams can build on the same hive location - AvHPlayer* theReceivingPlayer = dynamic_cast(CBaseEntity::Instance(host)); - if(theReceivingPlayer && theReceivingPlayer->GetIsAlien()) - { - state->renderamt = 128; - } - } - - // Handle cloakables here - if(!AvHSUGetIsExternalClassName(theEntNameString)) - { - AvHCloakable* theCloakable = dynamic_cast(CBaseEntity::Instance(ent)); - if(theCloakable) - { - // Now updated in gamerules - theCloakable->Update(); - - float theOpacityScalar = theCloakable->GetOpacity(); - if(theOpacityScalar < 1.0f) - { - int theBaseOpacity = kAlienStructureCloakAmount; - int theOpacityRange = 255 - kAlienStructureCloakAmount; - - // Allow spectators to see cloaked entities as if they are the player they are following, or as if they are on the same team as the alien otherwise - bool theCanSeeCloaked = (host->v.team == ent->v.team); - if(!theCanSeeCloaked) - { - int theHostIUserOne = host->v.iuser1; - if((theHostIUserOne == OBS_CHASE_LOCKED) || (theHostIUserOne == OBS_CHASE_FREE) || (theHostIUserOne == OBS_IN_EYE) ) - { - // View entities as player we're tracking - int theHostIUserTwo = host->v.iuser2; - CBaseEntity* theSpectatingEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theHostIUserTwo)); - if(theSpectatingEntity) - { - int theSpectatingEntityTeamNumber = theSpectatingEntity->pev->team; - if((theSpectatingEntityTeamNumber == ent->v.team) || (host->v.team == TEAM_IND)) - { - theCanSeeCloaked = true; - } - } - } - } - - if(theCanSeeCloaked) - { - theBaseOpacity = kAlienSelfCloakingBaseOpacity; - theOpacityRange = 255 - theBaseOpacity; - } - - int theOpacity = theBaseOpacity + theOpacityScalar*theOpacityRange; - theOpacity = max(min(255, theOpacity), 0); - - state->rendermode = kRenderTransTexture; - state->renderamt = theOpacity; - } - } - } - - // Spectator - state->iuser1 = ent->v.iuser1; - state->iuser2 = ent->v.iuser2; - - // AvH specials - state->iuser3 = ent->v.iuser3; - state->iuser4 = ent->v.iuser4; - - // Slightly different processing for particle system entities -// if(theIsParticleSystemEntity) -// { -// // propagate weapon model as custom data) -// state->weaponmodel = ent->v.weaponmodel; -// } - - state->fuser1 = ent->v.fuser1; - state->fuser2 = ent->v.fuser2; - state->fuser3 = ent->v.fuser3; - state->vuser4 = ent->v.vuser4; - - - - - - - state->aiment = 0; - if ( ent->v.aiment ) - { - state->aiment = ENTINDEX( ent->v.aiment ); - } - - state->owner = 0; - if ( ent->v.owner ) - { - int owner = ENTINDEX( ent->v.owner ); - - // Only care if owned by a player - if ( owner >= 1 && owner <= gpGlobals->maxClients ) - { - state->owner = owner; - } - } - - // HACK: Somewhat... - // Class is overridden for non-players to signify a breakable glass object ( sort of a class? ) - if ( !player ) - { - state->playerclass = ent->v.playerclass; - } - - // Propagate team for all entities (mainly needed for client-side lasso selection) - state->team = ent->v.team; - - // Check special vision mode - AvHPlayer* theReceivingPlayer = dynamic_cast(CBaseEntity::Instance(host)); - if(theReceivingPlayer && (theReceivingPlayer->GetIsAlienSightActive())) - { - bool marineGlow = false; - if (player) - { - // Uncomment below to enable range for the alien flashlight - // vec3_t theDistanceVec; - // VectorSubtract(theReceivingPlayer->pev->origin, ent->v.origin, theDistanceVec); - - // int theDistance = theDistanceVec[0] * theDistanceVec[0] + theDistanceVec[1] * theDistanceVec[1] + theDistanceVec[2] * theDistanceVec[2]; - // int theRange = BALANCE_VAR(kAlienFlashlightRange); - // marineGlow = (theDistance < ( theRange * theRange)); - marineGlow = true; - } - - if(( marineGlow || (ent->v.team == theReceivingPlayer->pev->team)) && (ent != theReceivingPlayer->edict()) && (ent->v.team != 0)) - { - state->rendermode = kRenderTransAdd; - state->renderamt = 150; - } - } - - // Special stuff for players only - if(player) - { - state->basevelocity = ent->v.basevelocity; - - state->weaponmodel = MODEL_INDEX( STRING( ent->v.weaponmodel ) ); - state->gaitsequence = ent->v.gaitsequence; - state->spectator = ent->v.flags & FL_SPECTATOR; - state->friction = ent->v.friction; - - state->gravity = ent->v.gravity; - - // New SDK doesn't propagate team for a reason? - //state->playerclass = ent->v.playerclass; - //state->team = ent->v.team; - - bool theIsDucking = ent->v.flags & FL_DUCKING; - state->usehull = AvHMUGetHull(theIsDucking, ent->v.iuser3); - - // Propagate "energy level" - state->fuser3 = ent->v.fuser3; - - // For skulk oriented player model - state->vuser1 = ent->v.vuser1; - - // For weapons - state->vuser4 = ent->v.vuser4; - - //state->skin = theTargetPlayer->GetSkin(); - state->skin = ent->v.skin; - state->playerclass = ent->v.playerclass; - // state->armorvalue = ent->v.armorvalue; - - AvHPlayer* thePlayerEntity = dynamic_cast(CBaseEntity::Instance(ent)); - if(thePlayerEntity && thePlayerEntity->GetIsTemporarilyInvulnerable()) - { - int theTeamIndex = ent->v.team; - ASSERT(theTeamIndex >= 0); - ASSERT(theTeamIndex < iNumberOfTeamColors); - - state->renderfx = kRenderFxGlowShell; - state->rendercolor.r = kTeamColors[theTeamIndex][0]; - state->rendercolor.g = kTeamColors[theTeamIndex][1]; - state->rendercolor.b = kTeamColors[theTeamIndex][2]; - state->renderamt = kInvulShellRenderAmount; - } - } - - state->health = ent->v.health; - - kNumReturn1++; - - return 1; -} - +// +// Copyright (c) 1999, Valve LLC. All rights reserved. +// +// This product contains software technology licensed from Id +// Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +// All Rights Reserved. +// +// Use, distribution, and modification of this source code and/or resulting +// object code is restricted to non-commercial enhancements to products from +// Valve LLC. All other use, distribution, or modification is prohibited +// without written permission from Valve LLC. +// +// Robin, 4-22-98: Moved set_suicide_frame() here from player.cpp to allow us to +// have one without a hardcoded player.mdl in tf_client.cpp +// +//===== client.cpp ======================================================== +// +// client/server game specific stuff +// +// $Workfile: client.cpp $ +// $Date: 2002/11/22 21:09:09 $ +// +//------------------------------------------------------------------------------- +// $Log: client.cpp,v $ +// Revision 1.69 2002/11/22 21:09:09 Flayra +// - Added "lastinv" command back in +// - mp_consistency changes +// +// Revision 1.68 2002/11/15 23:31:01 Flayra +// - Added "ready" verification for tourny mode +// +// Revision 1.67 2002/11/15 05:08:31 Flayra +// - Little tweak for refinery +// +// Revision 1.66 2002/11/15 04:48:40 Flayra +// - Oops +// +// Revision 1.65 2002/11/15 04:41:32 Flayra +// - Big performance improvements for AddToFullPack (whew) +// +// Revision 1.64 2002/11/12 02:17:48 Flayra +// - Renamed "avhplayer" to "player" +// - Tweaked enhanced sight to actually be useful at lower levels +// +// Revision 1.63 2002/11/03 04:53:20 Flayra +// - AddToFullPack optimizations and cleanup +// +// Revision 1.62 2002/10/25 21:50:01 Flayra +// - New attempted fix for overflows. Propagate skin and playerclass in new way, so entities outside the ready room aren't propagated to all clients that go back to the ready room immediately on game end. +// +// Revision 1.61 2002/10/24 21:16:10 Flayra +// - Log Sys_Errors +// - Allow players to change their name before they first leave the ready room +// +// Revision 1.60 2002/10/20 17:25:04 Flayra +// - Redid performance improvement (agh) +// +// Revision 1.59 2002/10/20 16:34:09 Flayra +// - Returned code back to normal +// +// Revision 1.58 2002/10/19 22:33:48 Flayra +// - Various server optimizations +// +// Revision 1.57 2002/10/18 22:14:23 Flayra +// - Server optimizations (untested though, may need backing out) +// +// Revision 1.56 2002/10/16 00:38:50 Flayra +// - Queue up name change until end of game +// - Removed precaching of unneeded sounds +// - Optimized AvHDetermineVisibility (being called 1000s of times per tick) +// +// Revision 1.55 2002/10/03 18:27:39 Flayra +// - Moved order completion sounds into HUD sounds +// +// Revision 1.54 2002/09/25 20:37:53 Flayra +// - Precache more sayings +// +// Revision 1.53 2002/09/23 22:01:02 Flayra +// - Propagate skin +// - Don't treat unclaimed hives at world objects (even though team is 0) for visibility purposes +// - Added heavy model +// - Removed old mapper build preprocessor directives +// +// Revision 1.52 2002/08/31 18:01:17 Flayra +// - Work at VALVe +// +// Revision 1.51 2002/08/16 02:24:31 Flayra +// - Removed "give" cheat +// +// Revision 1.50 2002/08/09 00:15:46 Flayra +// - Removed behavior where "drop" would drop alien weapons in a disconcerting manner +// +// Revision 1.49 2002/07/25 16:50:15 flayra +// - Linux build changes +// +// Revision 1.48 2002/07/24 18:46:19 Flayra +// - Linux and scripting changes +// +// Revision 1.47 2002/07/23 16:46:38 Flayra +// - Added power-armor drawing, tweaked invul drawing +// +// Revision 1.46 2002/07/10 14:36:21 Flayra +// - .mp3 and particle fixes +// +// Revision 1.45 2002/07/08 16:38:57 Flayra +// - Draw invulnerable players differently +// +//=============================================================================== +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "saverestore.h" +#include "player.h" +#include "spectator.h" +#include "client.h" +#include "soundent.h" +#include "gamerules.h" +#include "game.h" +#include "engine/customentity.h" +#include "weapons.h" +#include "common/weaponinfo.h" +#include "common/usercmd.h" +#include "common/netadr.h" +#include "util/nowarnings.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHSoundListManager.h" +#include "game.h" +#include "mod/AvHParticleTemplateServer.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHSpecials.h" +#include "util/Zassert.h" +#include "dlls/cbasedoor.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHSoundConstants.h" +#include "mod/AvHHulls.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "dlls/triggers.h" +#include "util/MathUtil.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHCloakable.h" +#include "mod/AvHAlienAbilityConstants.h" +#include "mod/AvHNetworkMessages.h" +#include "mod/AvHNexusServer.h" + +#include "game_shared/voice_gamemgr.h" +extern CVoiceGameMgr g_VoiceGameMgr; + +extern AvHSoundListManager gSoundListManager; + +extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; +extern DLL_GLOBAL BOOL g_fGameOver; +extern DLL_GLOBAL int g_iSkillLevel; +extern DLL_GLOBAL ULONG g_ulFrameCount; + +extern void CopyToBodyQue(entvars_t* pev); +extern int g_teamplay; + +/* + * used by kill command and disconnect command + * ROBIN: Moved here from player.cpp, to allow multiple player models + */ +void set_suicide_frame(entvars_t* pev) +{ + if (!FStrEq(STRING(pev->model), "models/player.mdl")) + return; // allready gibbed + + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_TOSS; + pev->deadflag = DEAD_DEAD; + pev->nextthink = -1; +} + + +/* +=========== +LogStringForPlayer + +generates string for player event logging - KGP +============ +*/ +std::string GetLogStringForPlayer( edict_t *pEntity ) +{ + // outputs "netname" if g_teamplay + // or "netname" if not g_teamplay + std::string result = "\""; + result += STRING( pEntity->v.netname ); + result += "<"; + result += MakeStringFromInt( GETPLAYERUSERID( pEntity ) ); + result += "><"; + result += AvHNexus::getNetworkID( pEntity ).c_str(); + result += "><"; + result += AvHSUGetTeamName( pEntity->v.team ); + result += ">\""; + return result; +} + +/* +=========== +ClientConnect + +called when a player connects to a server +============ +*/ +BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) +{ + return g_pGameRules->ClientConnected( pEntity, pszName, pszAddress, szRejectReason ); + + // a client connecting during an intermission can cause problems + // if (intermission_running) + // ExitIntermission (); + +} + + +/* +=========== +ClientDisconnect + + called when a player disconnects from a server + + GLOBALS ASSUMED SET: g_fGameOver + ============ +*/ +void ClientDisconnect( edict_t *pEntity ) +{ + if (g_fGameOver) + return; + + char text[256]; + sprintf( text, "- %s has left the game\n", STRING(pEntity->v.netname) ); + + CBaseEntity* theBaseEntity = CBaseEntity::Instance(ENT(pEntity)); + UTIL_SayTextAll(text, theBaseEntity); + + CSound *pSound; + pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEntity ) ); + { + // since this client isn't around to think anymore, reset their sound. + if ( pSound ) + { + pSound->Reset(); + } + } + + // since the edict doesn't get deleted, fix it so it doesn't interfere. + pEntity->v.takedamage = DAMAGE_NO;// don't attract autoaim + pEntity->v.solid = SOLID_NOT;// nonsolid + UTIL_SetOrigin ( &pEntity->v, pEntity->v.origin ); + + g_pGameRules->ClientDisconnected( pEntity ); + + //: If this isnt set, clients around this player will crash. + pEntity->v.effects |= EF_NODRAW; +} + + +// called by ClientKill and DeadThink +void respawn(entvars_t* pev, BOOL fCopyCorpse) +{ + // AVH isn't dm, coop or single-player. + //if (gpGlobals->coop || gpGlobals->deathmatch) + //{ + if ( fCopyCorpse ) + { + // make a copy of the dead body for appearances sake + CopyToBodyQue(pev); + } + + // respawn player + GetClassPtr( (AvHPlayer *)pev)->Spawn( ); + //} + //else + //{ // restart the entire server + // SERVER_COMMAND("reload\n"); + //} +} + +/* +============ +ClientKill + +Player entered the suicide command + +GLOBALS ASSUMED SET: g_ulModelIndexPlayer +============ +*/ +void ClientKill( edict_t *pEntity ) +{ + entvars_t *pev = &pEntity->v; + + CBasePlayer *pl = (CBasePlayer*) CBasePlayer::Instance( pev ); + + //if(GetGameRules()->GetCheatsEnabled()) + //{ + if ( pl->m_fNextSuicideTime > gpGlobals->time ) + return; // prevent suiciding too ofter + + pl->m_fNextSuicideTime = gpGlobals->time + 1; // don't let them suicide for 5 seconds after suiciding + + bool theIsCombatModeForSuicide = GetGameRules()->GetIsCombatMode(); + + #ifdef DEBUG + if(GetGameRules()->GetIsCombatMode()) + { + theIsCombatModeForSuicide = false; + } + #endif + + bool theCanSuicide = GetGameRules()->CanPlayerBeKilled(pl) && !theIsCombatModeForSuicide; + + if(theCanSuicide) + { + pl->Suicide(); + + // pev->modelindex = g_ulModelIndexPlayer; + // pev->frags -= 2; // extra penalty + // respawn( pev ); + } + //} +} + +/* +=========== +ClientPutInServer + +called each time a player is spawned +============ +*/ +void ClientPutInServer( edict_t *pEntity ) +{ + //CBasePlayer *pPlayer; + AvHPlayer *pPlayer; + + entvars_t *pev = &pEntity->v; + + if(pev->flags & FL_PROXY) + { + ALERT(at_logged, "Player with FL_PROXY put in server.\n"); + } + + if(pev->flags & FL_PROXY) + { + pev->flags |= FL_PROXY; + } + + pPlayer = GetClassPtr((AvHPlayer *)pev); + pPlayer->SetCustomDecalFrames(-1); // Assume none; + + // Allocate a CBasePlayer for pev, and call spawn + pPlayer->SetPlayMode(PLAYMODE_READYROOM); + //pPlayer->Spawn(); + + // Reset interpolation during first frame + pPlayer->pev->effects |= EF_NOINTERP; +} + +//// HOST_SAY +// String comes in as +// say blah blah blah +// or as +// blah blah blah +// +void Host_Say( edict_t *pEntity, int teamonly ) +{ + AvHPlayer* client; + AvHPlayer* theTalkingPlayer = dynamic_cast(CBaseEntity::Instance(pEntity)); + int j; + char* p; + char text[256]; + char szTemp[256]; + const char* cpSay = "say"; + const char* cpSayTeam = "say_team"; + const char* pcmd = CMD_ARGV(0); + bool theTalkerInReadyRoom = theTalkingPlayer->GetInReadyRoom(); + //bool theTalkerIsObserver = (theTalkingPlayer->GetPlayMode() == PLAYMODE_OBSERVER) || (theTalkingPlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT); + + // We can get a raw string now, without the "say " prepended + if ( CMD_ARGC() == 0 ) + return; + + //Not yet. + if ( theTalkingPlayer->m_flNextChatTime > gpGlobals->time ) + return; + + if ( !stricmp( pcmd, cpSay) || !stricmp( pcmd, cpSayTeam ) ) + { + if ( CMD_ARGC() >= 2 ) + { + p = (char *)CMD_ARGS(); + + if(GetGameRules()->GetIsTournamentMode() && !GetGameRules()->GetGameStarted()) + { + if(!strcmp(CMD_ARGV(1), kReadyNotification)) + { + // Team is ready + AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)(pEntity->v.team)); + if(theTeam && !theTeam->GetIsReady()) + { + theTeam->SetIsReady(); + } + } + else if (!strcmp(CMD_ARGV(1), kNotReadyNotification)) + { + // Team is no longer ready + AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)(pEntity->v.team)); + if(theTeam && theTeam->GetIsReady()) + { + theTeam->SetIsReady(false); + } + } + } + } + else + { + // say with a blank message, nothing to do + return; + } + } + else // Raw text, need to prepend argv[0] + { + if ( CMD_ARGC() >= 2 ) + { + sprintf( szTemp, "%s %s", ( char * )pcmd, (char *)CMD_ARGS() ); + } + else + { + // Just a one word command, use the first word...sigh + sprintf( szTemp, "%s", ( char * )pcmd ); + } + p = szTemp; + } + +// remove quotes if present + if (*p == '"') + { + p++; + p[strlen(p)-1] = 0; + } + +// make sure the text has content + char *pc=p; + for ( pc = p; pc != NULL && *pc != 0; pc++ ) + { + if ( isprint( *pc ) && !isspace( *pc ) ) + { + pc = NULL; // we've found an alphanumeric character, so text is valid + break; + } + } + if ( pc != NULL ) + return; // no character found, so say nothing + +// turn on color set 2 (color on, no sound) + if ( teamonly ) + sprintf( text, "%c(TEAM) %s: ", 2, STRING( pEntity->v.netname ) ); + else + sprintf( text, "%c%s: ", 2, STRING( pEntity->v.netname ) ); + + j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator + if ( (int)strlen(p) > j ) + p[j] = 0; + + strcat( text, p ); + strcat( text, "\n" ); + + theTalkingPlayer->m_flNextChatTime = gpGlobals->time + CHAT_INTERVAL; + // loop through all players + // Start with the first player. + // This may return the world in single player if the client types something between levels or during spawn + // so check it, or it will infinite loop + + client = NULL; + while ( ((client = (AvHPlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) + { + if ( !client->pev ) + continue; + + if ( client->edict() == pEntity ) + continue; + + if ( !(client->IsNetClient()) ) // Not a client ? (should never be true) + continue; + + // can the receiver hear the sender? or has he muted him? + if ( g_VoiceGameMgr.PlayerHasBlockedPlayer( client, theTalkingPlayer ) ) + continue; + + // Don't differentiate between team and non-team when not playing + bool theTalkingPlayerIsPlaying = ((theTalkingPlayer->GetPlayMode() == PLAYMODE_PLAYING) || (theTalkingPlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) || (theTalkingPlayer->GetPlayMode() == PLAYMODE_REINFORCING)); + bool theClientIsPlaying = ((client->GetPlayMode() == PLAYMODE_PLAYING) || (client->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) || (client->GetPlayMode() == PLAYMODE_REINFORCING)); + bool theTalkerIsObserver = theTalkingPlayer->IsObserver(); + bool theClientIsObserver = client->IsObserver(); + bool theClientIsHLTV = (client->pev->flags & FL_PROXY); + + bool theClientInReadyRoom = client->GetInReadyRoom(); + + if (theClientInReadyRoom != theTalkerInReadyRoom && !theClientIsHLTV) + { + continue; + } + + if (!theClientIsObserver || theClientIsPlaying ) // Non-playing Observers hear everything. + { + + if ( theTalkingPlayerIsPlaying && teamonly && g_pGameRules->PlayerRelationship(client, CBaseEntity::Instance(pEntity)) != GR_TEAMMATE ) + continue; + + // chat can never go between play area and non-play area + if(theTalkingPlayerIsPlaying != theClientIsPlaying && !theClientIsHLTV ) + continue; + + // chat of any kind doesn't go from ready room to play area in tournament mode + if(theTalkerInReadyRoom && GetGameRules()->GetIsTournamentMode() && theClientIsPlaying && !theClientIsHLTV) + continue; + + } + + UTIL_SayText(text, client, ENTINDEX(pEntity)); + + } + + // print to the sending client + UTIL_SayText(text, CBaseEntity::Instance(ENT(pEntity))); + + // echo to server console + g_engfuncs.pfnServerPrint( text ); + + char * temp; + if ( teamonly ) + temp = "say_team"; + else + temp = "say"; + + UTIL_LogPrintf( "%s %s \"%s\"\n", GetLogStringForPlayer( pEntity ).c_str(), temp, p ); +} + + +/* +=========== +ClientCommand +called each time a player uses a "cmd" command +============ +*/ + +// Use CMD_ARGV, CMD_ARGV, and CMD_ARGC to get pointers the character string command. +void ClientCommand( edict_t *pEntity ) +{ + const char *pcmd = CMD_ARGV(0); + const char *pstr; + + // Is the client spawned yet? + if ( !pEntity->pvPrivateData ) + return; + + entvars_t *pev = &pEntity->v; + + AvHPlayer* thePlayer = GetClassPtr((AvHPlayer *)pev); + bool thePlayerCanAct = thePlayer->GetIsAbleToAct(); + + if ( FStrEq(pcmd, "say" ) ) + { + Host_Say( pEntity, 0 ); + } + else if ( FStrEq(pcmd, "say_team" ) ) + { + Host_Say( pEntity, 1 ); + } + else if ( FStrEq(pcmd, "fullupdate" ) ) + { + GetClassPtr((CBasePlayer *)pev)->ForceClientDllUpdate(); + } + else if ( FStrEq(pcmd, "give" ) ) + { + if(GetGameRules()->GetCheatsEnabled()) + { + int iszItem = ALLOC_STRING( CMD_ARGV(1) ); // Make a copy of the classname + GetClassPtr((CBasePlayer *)pev)->GiveNamedItem( STRING(iszItem) ); + } + } + + else if ( FStrEq(pcmd, "drop" )) + { + if (thePlayerCanAct) + { + // player is dropping an item. + GetClassPtr((AvHPlayer *)pev)->DropItem((char *)CMD_ARGV(1)); + } + } + else if ( FStrEq(pcmd, "fov" ) ) + { + if (GetGameRules()->GetCheatsEnabled() && (CMD_ARGC() > 1)) + { + GetClassPtr((CBasePlayer *)pev)->m_iFOV = atoi( CMD_ARGV(1) ); + } + else + { + CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"fov\" is \"%d\"\n", (int)GetClassPtr((CBasePlayer *)pev)->m_iFOV ) ); + } + } + else if ( FStrEq(pcmd, "use" )) + { + if (thePlayerCanAct) + { + GetClassPtr((CBasePlayer *)pev)->SelectItem((char *)CMD_ARGV(1)); + } + } + else if (((pstr = strstr(pcmd, "weapon_")) != NULL) && (pstr == pcmd)) + { + if (thePlayerCanAct) + { + GetClassPtr((CBasePlayer *)pev)->SelectItem(pcmd); + } + } + else if ( g_pGameRules->ClientCommand( GetClassPtr((CBasePlayer *)pev), pcmd ) ) + { + // MenuSelect returns true only if the command is properly handled, so don't print a warning + } + else if ( FStrEq( pcmd, "specmode" ) ) // new spectator mode + { + CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); + if ( pPlayer->IsObserver() ) + { + int theMode = atoi(CMD_ARGV(1)); + pPlayer->Observer_SetMode(theMode); + pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2); + } + } + else if (FStrEq( pcmd, "follow")) // follow a specific player + { + CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); + if ( pPlayer->IsObserver() ) + { + pPlayer->Observer_SpectatePlayer(atoi( CMD_ARGV(1) )); + pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2); + } + } + else if ( FStrEq( pcmd, "follownext" ) ) // follow next player + { + CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); + if ( pPlayer->IsObserver() ) + { + pPlayer->Observer_FindNextPlayer(atoi( CMD_ARGV(1) )); + pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2); + } + } + else + { + // tell the user they entered an unknown command + char command[128]; + + // check the length of the command (prevents crash) + // max total length is 192 ...and we're adding a string below ("Unknown command: %s\n") + strncpy( command, pcmd, 127 ); + command[127] = '\0'; + // : 1071 + // Remove printf formatting + for ( int i=0; i < 127; i++ ) { + if ( command[i] == '%' ) { + command[i] = ' '; + } + } + // : + // tell the user they entered an unknown command + ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, UTIL_VarArgs( "Unknown command: %s\n", command ) ); + } + //else if(!AvHClientCommand(pEntity)) + //{ + // // tell the user they entered an unknown command + // ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, UTIL_VarArgs( "Unknown command: %s\n", pcmd ) ); + //} +} + +//bool AvHClientCommand( edict_t *pEntity ) +//{ +//} + +/* +======================== +ClientUserInfoChanged + +called after the player changes +userinfo - gives dll a chance to modify it before +it gets sent into the rest of the engine. +======================== +*/ +void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer ) +{ + // Is the client spawned yet? + if ( !pEntity->pvPrivateData ) + return; + + const char* theCurrentNetName = STRING(pEntity->v.netname); + const char* theNameKeyValue = g_engfuncs.pfnInfoKeyValue(infobuffer, "name"); + + // msg everyone if someone changes their name, and it isn't the first time (changing no name to current name) + if ( pEntity->v.netname && STRING(pEntity->v.netname)[0] != 0 && !FStrEq(theCurrentNetName, theNameKeyValue) ) + { + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(pEntity)); + + // Don't change player info after match has started unless player hasn't left ready room + if(!GetGameRules()->GetArePlayersAllowedToJoinImmediately() && thePlayer && theNameKeyValue && thePlayer->GetHasLeftReadyRoom()) + { + thePlayer->SetDesiredNetName(string(theNameKeyValue)); + + char text[256]; + sprintf( text, "* Name will be changed to %s next game.\n", theNameKeyValue); + UTIL_SayText(text, CBaseEntity::Instance(ENT(pEntity))); + + // Restore name key value changed by engine + char theName[256]; + strcpy(theName, theCurrentNetName); + g_engfuncs.pfnSetClientKeyValue( ENTINDEX(pEntity), infobuffer, "name", theName); + } + else + { + char sName[256]; + char *pName = g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ); + strncpy( sName, pName, sizeof(sName) - 1 ); + sName[ sizeof(sName) - 1 ] = '\0'; + + // First parse the name and remove any %'s + for ( char *pApersand = sName; pApersand != NULL && *pApersand != 0; pApersand++ ) + { + // Replace it with a space + if ( *pApersand == '%' ) + *pApersand = ' '; + } + + // Set the name + g_engfuncs.pfnSetClientKeyValue( ENTINDEX(pEntity), infobuffer, "name", sName ); + //thePlayer->SetDesiredNetName(MAKE_STRING(sName)); + + char text[256]; + sprintf( text, "* %s changed name to %s\n", STRING(pEntity->v.netname), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ) ); + + UTIL_SayTextAll(text, CBaseEntity::Instance(ENT(pEntity))); + + UTIL_LogPrintf( "%s changed name to \"%s\"\n", GetLogStringForPlayer( pEntity ).c_str(), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ) ); + } + } + + g_pGameRules->ClientUserInfoChanged( GetClassPtr((CBasePlayer *)&pEntity->v), infobuffer ); +} + +static int g_serveractive = 0; + +void ServerDeactivate( void ) +{ + // It's possible that the engine will call this function more times than is necessary + // Therefore, only run it one time for each call to ServerActivate + if ( g_serveractive != 1 ) + { + return; + } + + g_serveractive = 0; + + // Peform any shutdown operations here... + // +} + +void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) +{ + int i; + CBaseEntity *pClass; + + // Every call to ServerActivate should be matched by a call to ServerDeactivate + g_serveractive = 1; + + // Clients have not been initialized yet + for ( i = 0; i < edictCount; i++ ) + { + if ( pEdictList[i].free ) + continue; + + // Clients aren't necessarily initialized until ClientPutInServer() + if ( i < clientMax || !pEdictList[i].pvPrivateData ) + continue; + + pClass = CBaseEntity::Instance( &pEdictList[i] ); + // Activate this entity if it's got a class & isn't dormant + if ( pClass && !(pClass->pev->flags & FL_DORMANT) ) + { + pClass->Activate(); + } + else + { + ALERT( at_console, "Can't instance %s\n", STRING(pEdictList[i].v.classname) ); + } + } + + Net_InitializeMessages(); +} + + +/* +================ +PlayerPreThink + +Called every frame before physics are run +================ +*/ +void PlayerPreThink( edict_t *pEntity ) +{ + entvars_t *pev = &pEntity->v; + CBasePlayer *pPlayer = (CBasePlayer *)GET_PRIVATE(pEntity); + + if (pPlayer) + pPlayer->PreThink( ); +} + +/* +================ +PlayerPostThink + +Called every frame after physics are run +================ +*/ +void PlayerPostThink( edict_t *pEntity ) +{ + entvars_t *pev = &pEntity->v; + CBasePlayer *pPlayer = (CBasePlayer *)GET_PRIVATE(pEntity); + + if (pPlayer) + pPlayer->PostThink( ); +} + + + +void ParmsNewLevel( void ) +{ +} + + +void ParmsChangeLevel( void ) +{ + // retrieve the pointer to the save data + SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData; + + if ( pSaveData ) + pSaveData->connectionCount = BuildChangeList( pSaveData->levelList, MAX_LEVEL_CONNECTIONS ); +} + +void ShowMenu(entvars_s *pev, int ValidSlots, int DisplayTime, BOOL ShowLater, char Menu[500]) +{ + NetMsg_ShowMenu( pev, ValidSlots, DisplayTime, ShowLater ? 1 : 0, string(Menu) ); +} + +// +// GLOBALS ASSUMED SET: g_ulFrameCount +// +void StartFrame( void ) +{ + if ( g_pGameRules ) + g_pGameRules->Think(); + + if ( g_fGameOver ) + return; + + gpGlobals->teamplay = teamplay.value; + + g_ulFrameCount++; +} + +void ClientPrecache( void ) +{ + // Precache sound lists for all player types + gSoundListManager.Clear(); + + int i=0; + for(i = ((int)AVH_USER3_NONE + 1); i < (int)AVH_USER3_ALIEN_EMBRYO; i++) + { + char theSoundListName[256]; + bool theIsAlien = (i > 3); + AvHUser3 theUser3 = (AvHUser3)(i); + + bool theLoadSounds = false; + + // No squad leader and commander sounds + switch(theUser3) + { + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: + theLoadSounds = true; + } + + if(theLoadSounds) + { + // Die sounds + sprintf(theSoundListName, kPlayerLevelDieSoundList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + + // Spawn sounds + sprintf(theSoundListName, kPlayerLevelSpawnSoundList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + + // Pain sounds + sprintf(theSoundListName, kPlayerLevelPainSoundList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + + // Wound sounds + sprintf(theSoundListName, kPlayerLevelWoundSoundList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + + // Attack sounds (aliens only) + if(theIsAlien) + { + sprintf(theSoundListName, kPlayerLevelAttackSoundList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + + // Idle sounds + sprintf(theSoundListName, kPlayerLevelIdleSoundList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + + // Movement sounds + sprintf(theSoundListName, kPlayerLevelMoveSoundList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + } + } + } + + // Precache misc. alien sounds + //PRECACHE_SOUND(kAdrenalineSound); + PRECACHE_UNMODIFIED_SOUND(kGestationSound); + PRECACHE_UNMODIFIED_SOUND(kEggDestroyedSound); + PRECACHE_UNMODIFIED_SOUND(kEggIdleSound); + PRECACHE_UNMODIFIED_SOUND(kPrimalScreamResponseSound); + + // Preacache sayings sound list + for(i = 1; i <= 9; i++) + { + char theSoundListName[256]; + + sprintf(theSoundListName, kSoldierSayingList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + + sprintf(theSoundListName, kCommanderSayingList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + + sprintf(theSoundListName, kAlienSayingList, i); + gSoundListManager.PrecacheSoundList(theSoundListName); + } + + // Precache order sounds + gSoundListManager.PrecacheSoundList(kSoldierOrderRequestList); + gSoundListManager.PrecacheSoundList(kSoldierOrderAckList); + + // Precache other sound lists + gSoundListManager.PrecacheSoundList(kResourceTowerSoundList); + gSoundListManager.PrecacheSoundList(kAlienResourceTowerSoundList); + gSoundListManager.PrecacheSoundList(kHiveWoundSoundList); + gSoundListManager.PrecacheSoundList(kMarineConstructionSoundList); + gSoundListManager.PrecacheSoundList(kElectricSparkSoundList); + gSoundListManager.PrecacheSoundList(kAlienConstructionSoundList); + + // Other equipment and effect sounds + PRECACHE_UNMODIFIED_SOUND(kMarineBuildingDeploy); + PRECACHE_UNMODIFIED_SOUND(kJetpackSound); + PRECACHE_UNMODIFIED_SOUND(kWeldingSound); + PRECACHE_UNMODIFIED_SOUND(kWeldingHitSound); + //PRECACHE_UNMODIFIED_SOUND(kOverwatchStartSound); + //PRECACHE_UNMODIFIED_SOUND(kOverwatchEndSound); + PRECACHE_UNMODIFIED_SOUND(kRegenerationSound); + PRECACHE_UNMODIFIED_SOUND(kStartCloakSound); + PRECACHE_UNMODIFIED_SOUND(kEndCloakSound); + //PRECACHE_UNMODIFIED_SOUND(kWallJumpSound); + PRECACHE_UNMODIFIED_SOUND(kWingFlapSound1); + PRECACHE_UNMODIFIED_SOUND(kWingFlapSound2); + PRECACHE_UNMODIFIED_SOUND(kWingFlapSound3); + PRECACHE_UNMODIFIED_SOUND(kSiegeHitSound1); + PRECACHE_UNMODIFIED_SOUND(kSiegeHitSound2); + + // setup precaches always needed + PRECACHE_UNMODIFIED_SOUND("player/sprayer.wav"); // spray paint sound for PreAlpha + + // PRECACHE_SOUND("player/pl_jumpland2.wav"); // UNDONE: play 2x step sound + + // Alien step sounds + PRECACHE_UNMODIFIED_SOUND("player/pl_step1-a.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step2-a.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step3-a.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step4-a.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_step1-a1.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step2-a1.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step3-a1.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step4-a1.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_step1-a5.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step2-a5.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step3-a5.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step4-a5.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_step1-h.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step2-h.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step3-h.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step4-h.wav"); + + // Marine step sounds + PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-1.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-4.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-5.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-6.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-7.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_fallpain3-8.wav"); + PRECACHE_UNMODIFIED_SOUND(kDigestingSound); + + PRECACHE_UNMODIFIED_SOUND("player/pl_step1.wav"); // walk on concrete + PRECACHE_UNMODIFIED_SOUND("player/pl_step2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_step4.wav"); + + PRECACHE_SOUND("common/npc_step1.wav"); // NPC walk on concrete + PRECACHE_SOUND("common/npc_step2.wav"); + PRECACHE_SOUND("common/npc_step3.wav"); + PRECACHE_SOUND("common/npc_step4.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_metal1.wav"); // walk on metal + PRECACHE_UNMODIFIED_SOUND("player/pl_metal2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_metal3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_metal4.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_dirt1.wav"); // walk on dirt + PRECACHE_UNMODIFIED_SOUND("player/pl_dirt2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_dirt3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_dirt4.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_duct1.wav"); // walk in duct + PRECACHE_UNMODIFIED_SOUND("player/pl_duct2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_duct3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_duct4.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_grate1.wav"); // walk on grate + PRECACHE_UNMODIFIED_SOUND("player/pl_grate2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_grate3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_grate4.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_slosh1.wav"); // walk in shallow water + PRECACHE_UNMODIFIED_SOUND("player/pl_slosh2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_slosh3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_slosh4.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_tile1.wav"); // walk on tile + PRECACHE_UNMODIFIED_SOUND("player/pl_tile2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_tile3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_tile4.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_tile5.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_swim1.wav"); // breathe bubbles + PRECACHE_UNMODIFIED_SOUND("player/pl_swim2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_swim3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_swim4.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_ladder1.wav"); // climb ladder rung + PRECACHE_UNMODIFIED_SOUND("player/pl_ladder2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_ladder3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_ladder4.wav"); + + PRECACHE_UNMODIFIED_SOUND("player/pl_wade1.wav"); // wade in water + PRECACHE_UNMODIFIED_SOUND("player/pl_wade2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_wade3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_wade4.wav"); + + PRECACHE_SOUND("debris/wood1.wav"); // hit wood texture + PRECACHE_SOUND("debris/wood2.wav"); + PRECACHE_SOUND("debris/wood3.wav"); + + PRECACHE_UNMODIFIED_SOUND("plats/train_use1.wav"); // use a train + + PRECACHE_UNMODIFIED_SOUND("buttons/spark5.wav"); // hit computer texture + PRECACHE_UNMODIFIED_SOUND("buttons/spark6.wav"); + PRECACHE_SOUND("debris/glass1.wav"); + PRECACHE_SOUND("debris/glass2.wav"); + PRECACHE_SOUND("debris/glass3.wav"); + + PRECACHE_UNMODIFIED_SOUND( SOUND_FLASHLIGHT_ON ); + PRECACHE_UNMODIFIED_SOUND( SOUND_FLASHLIGHT_OFF ); + +// player gib sounds + PRECACHE_SOUND("common/bodysplat.wav"); + +// player pain sounds + PRECACHE_UNMODIFIED_SOUND("player/pl_pain2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_pain4.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_pain5.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_pain6.wav"); + PRECACHE_UNMODIFIED_SOUND("player/pl_pain7.wav"); + + PRECACHE_UNMODIFIED_MODEL("models/player.mdl"); + PRECACHE_UNMODIFIED_MODEL(kReadyRoomModel); + + PRECACHE_UNMODIFIED_MODEL(kMarineSoldierModel); + PRECACHE_UNMODIFIED_MODEL(kHeavySoldierModel); + PRECACHE_UNMODIFIED_MODEL(kAlienLevelOneModel); + + PRECACHE_UNMODIFIED_MODEL(kAlienLevelTwoModel); + PRECACHE_UNMODIFIED_MODEL(kAlienLevelThreeModel); + PRECACHE_UNMODIFIED_MODEL(kAlienLevelFourModel); + PRECACHE_UNMODIFIED_MODEL(kAlienLevelFiveModel); + + PRECACHE_UNMODIFIED_MODEL(kMarineCommanderModel); + PRECACHE_UNMODIFIED_MODEL(kAlienGestateModel); + + + // hud sounds, for marines and aliens (change AvHSharedUtil::AvHSHUGetCommonSoundName if these sound names change) + PRECACHE_UNMODIFIED_SOUND("common/wpn_hudoff.wav"); + PRECACHE_UNMODIFIED_SOUND("common/wpn_hudon.wav"); + PRECACHE_UNMODIFIED_SOUND("common/wpn_moveselect.wav"); + PRECACHE_UNMODIFIED_SOUND("common/wpn_select.wav"); + PRECACHE_UNMODIFIED_SOUND("common/wpn_denyselect.wav"); + + PRECACHE_UNMODIFIED_SOUND("common/wpn_hudoff-a.wav"); + PRECACHE_UNMODIFIED_SOUND("common/wpn_hudon-a.wav"); + PRECACHE_UNMODIFIED_SOUND("common/wpn_moveselect-a.wav"); + + PRECACHE_UNMODIFIED_SOUND("common/wpn_select-a.wav"); + PRECACHE_UNMODIFIED_SOUND("common/wpn_denyselect-a.wav"); + + // geiger sounds + PRECACHE_UNMODIFIED_SOUND("player/geiger6.wav"); + PRECACHE_UNMODIFIED_SOUND("player/geiger5.wav"); + PRECACHE_UNMODIFIED_SOUND("player/geiger4.wav"); + PRECACHE_UNMODIFIED_SOUND("player/geiger3.wav"); + PRECACHE_UNMODIFIED_SOUND("player/geiger2.wav"); + PRECACHE_UNMODIFIED_SOUND("player/geiger1.wav"); + + PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash1.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash2.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash3.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/digesting.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/membrane.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/hera_fog.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/spore.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/spore2.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/umbra.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/umbra2.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/webstrand.spr"); + + PRECACHE_UNMODIFIED_GENERIC("ns.wad"); + PRECACHE_UNMODIFIED_GENERIC("ns2.wad"); + PRECACHE_UNMODIFIED_GENERIC("v_wad.wad"); +/* PRECACHE_UNMODIFIED_GENERIC("maps/co_angst_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_core_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_daimos_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_ether_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_faceoff_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_kestrel_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_niveus_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_pulse_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_sava_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_ulysses_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/co_umbra_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_altair_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_ayumi_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_bast_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_caged_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_eclipse_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_eon_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_hera_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_lost_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_lucid_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_machina_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_metal_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_nancy_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_nothing_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_origin_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_prometheus_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_shiva_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_tanith_detail.txt"); + PRECACHE_UNMODIFIED_GENERIC("maps/ns_veil_detail.txt"); + + CStringList list; + string modDir=string(getModDirectory()) + "/"; + if(BuildFileList(modDir, "maps/", "*_detail.txt", list)) + { + for(CStringList::iterator it=list.begin(); it != list.end(); it++ ) { + int iString = ALLOC_STRING((char*)it);//: We cant do "(char*)theSoundToPrecache" directly cause it causes some wierd problems. + PRECACHE_UNMODIFIED_GENERIC((char*)STRING(iString)); + } + } + */ +} + +/* +================ +Sys_Error + + Engine is going to shut down, allows setting a breakpoint in game .dll to catch that occasion + ================ + */ + void Sys_Error( const char *error_string ) + { +#ifdef DEBUG +#ifdef WIN32 + // Default case, do nothing. MOD AUTHORS: Add code ( e.g., _asm { int 3 }; here to cause a breakpoint for debugging your game .dlls + _asm { int 3 }; +#endif +#else + char theDebugString[2056]; + sprintf(theDebugString, "Sys_Error: %s\n", error_string); + ALERT(at_error, theDebugString); +#endif + +} + +/* +=============== +GetGameDescription + +Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2 +=============== +*/ +const char *GetGameDescription() +{ + if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized + return g_pGameRules->GetGameDescription(); + else + return "Half-Life"; +} + +/* +================ +PlayerCustomization + +A new player customization has been registered on the server +UNDONE: This only sets the # of frames of the spray can logo +animation right now. +================ +*/ +void PlayerCustomization( edict_t *pEntity, customization_t *pCust ) +{ + entvars_t *pev = &pEntity->v; + CBasePlayer *pPlayer = (CBasePlayer *)GET_PRIVATE(pEntity); + + if (!pPlayer) + { + ALERT(at_console, "PlayerCustomization: Couldn't get player!\n"); + return; + } + + if (!pCust) + { + ALERT(at_console, "PlayerCustomization: NULL customization!\n"); + return; + } + + switch (pCust->resource.type) + { + case t_decal: + pPlayer->SetCustomDecalFrames(pCust->nUserData2); // Second int is max # of frames. + break; + case t_sound: + case t_skin: + case t_model: + // Ignore for now. + break; + default: + ALERT(at_console, "PlayerCustomization: Unknown customization type!\n"); + break; + } +} + +/* +================ +SpectatorConnect + +A spectator has joined the game +================ +*/ +void SpectatorConnect( edict_t *pEntity ) +{ + entvars_t *pev = &pEntity->v; + CBaseSpectator *pPlayer = (CBaseSpectator *)GET_PRIVATE(pEntity); + + if (pPlayer) + pPlayer->SpectatorConnect( ); +} + +/* +================ +SpectatorConnect + +A spectator has left the game +================ +*/ +void SpectatorDisconnect( edict_t *pEntity ) +{ + entvars_t *pev = &pEntity->v; + CBaseSpectator *pPlayer = (CBaseSpectator *)GET_PRIVATE(pEntity); + + if (pPlayer) + pPlayer->SpectatorDisconnect( ); +} + +/* +================ +SpectatorConnect + +A spectator has sent a usercmd +================ +*/ +void SpectatorThink( edict_t *pEntity ) +{ + entvars_t *pev = &pEntity->v; + CBaseSpectator *pPlayer = (CBaseSpectator *)GET_PRIVATE(pEntity); + + if (pPlayer) + pPlayer->SpectatorThink( ); +} + +//////////////////////////////////////////////////////// +// PAS and PVS routines for client messaging +// + +/* +================ +SetupVisibility + +A client can have a separate "view entity" indicating that his/her view should depend on the origin of that +view entity. If that's the case, then pViewEntity will be non-NULL and will be used. Otherwise, the current +entity's origin is used. Either is offset by the view_ofs to get the eye position. + +From the eye position, we set up the PAS and PVS to use for filtering network messages to the client. At this point, we could + override the actual PAS or PVS values, or use a different origin. + +NOTE: Do not cache the values of pas and pvs, as they depend on reusable memory in the engine, they are only good for this one frame +================ +*/ +void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char **pvs, unsigned char **pas ) +{ + Vector org; + edict_t *pView = pClient; + bool theUseSpecialPASOrigin = false; + Vector theSpecialPASOrigin; + + // Find the client's PVS + if ( pViewEntity ) + { + pView = pViewEntity; + } + + // Proxy sees and hears all + if ( (pClient->v.flags & FL_PROXY) || GetGameRules()->GetIsCheatEnabled(kcViewAll)) + { + //ALERT(at_logged, "Setting PVS and PAS to NULL for FL_PROXY\n"); + *pvs = NULL; // the spectator proxy sees + *pas = NULL; // and hears everything + return; + } + + // Tracking Spectators use the visibility of their target + CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient ); + if ( (pPlayer->pev->iuser2 != 0) && (pPlayer->m_hObserverTarget != NULL) && (pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER) && pPlayer->IsAlive()) + { + pView = pPlayer->m_hObserverTarget->edict(); + UTIL_SetOrigin( pPlayer->pev, pPlayer->m_hObserverTarget->pev->origin ); + } + + // Tracking Spectators use the visibility of their target +// AvHPlayer *thePlayer = dynamic_cast(CBaseEntity::Instance(pClient)); + + // cgc - something could be wrong here, crashing from Robin's spectator code...did I do something wrong? +// if(thePlayer) +// { +// if ( (thePlayer->pev->iuser2 != 0) && (thePlayer->m_hObserverTarget != NULL) ) +// { +// pView = thePlayer->m_hObserverTarget->edict(); +// } +// +// if(thePlayer->GetRole() == ROLE_COMMANDER) +// { +// thePlayer->GetSpecialPASOrigin(theSpecialPASOrigin); +// theUseSpecialPASOrigin = true; +// } +// } + + org = pView->v.origin + pView->v.view_ofs; + if ( pView->v.flags & FL_DUCKING ) + { + org = org + ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN ); + } + + *pvs = ENGINE_SET_PVS ( (float *)&org ); + + if(theUseSpecialPASOrigin) + { + *pas = ENGINE_SET_PAS ( (float *)&theSpecialPASOrigin ); + } + else + { + *pas = ENGINE_SET_PAS ( (float *)&org ); + } +} + +#include "common/entity_state.h" + +//CachedEntity gCachedEntities[kMaxPlayers][kMaxEntities]; +// +//void ResetCachedEntities() +//{ +// for(int i = 0; i < kMaxPlayers; i++) +// { +// for(int j = 0; j < kMaxEntities; j++) +// { +// gCachedEntities[i][j].ResetCachedEntity(); +// } +// } +//} + + + +// Array sizes ( memory is 5624 * 32 = 179968 bytes ) +#define MAX_CLIENTS ( 32 ) +#define MAX_ENTITIES ( 900 + MAX_CLIENTS * 15 ) + +// Re-check entities only this often once they are found to be in the PVS +// NOTE: We could have a separate timeout for players if we wanted to tweak the coherency time for them ( you'd have +// to check the entitynum against 1 to gpGlobals->maxclients ), etc. +//#define PVS_COHERENCY_TIME 1.0f + +extern float kPVSCoherencyTime; + +typedef struct +{ + bool mLastVisibility; + float m_fTimeEnteredPVS; +} ENTITYPVSSTATUS; + +typedef struct +{ + ENTITYPVSSTATUS m_Status[ MAX_ENTITIES ]; + + // Remember player leaf data so we can invalidate pvs history when leaf changes + int headnode; // -1 to use normal leaf check + int num_leafs; + short leafnums[ MAX_ENT_LEAFS ]; + +} PLAYERPVSSTATUS; + +static PLAYERPVSSTATUS g_PVSStatus[ MAX_CLIENTS ]; + +void ResetPlayerPVS( edict_t *client, int clientnum ) +{ + ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); + + PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; + + memset( pvs, 0, sizeof( PLAYERPVSSTATUS ) ); + + // Copy leaf data in right away + pvs->headnode = client->headnode; + pvs->num_leafs = client->num_leafs; + memcpy( pvs->leafnums, client->leafnums, sizeof( pvs->leafnums ) ); +} + +bool TimeToResetPVS( edict_t *client, int clientnum ) +{ + ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); + + PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; + + if ( (pvs->headnode != client->headnode) || (pvs->num_leafs != client->num_leafs)) + return true; + + if ( client->num_leafs > 0 ) + { + for ( int i = 0; i < client->num_leafs; i++ ) + { + if ( client->leafnums[ i ] != pvs->leafnums[ i ] ) + { + return true; + } + } + } + + // Still the same + return false; +} + +void MarkEntityInPVS( int clientnum, int entitynum, float time, bool inpvs ) +{ + ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); + ASSERT( entitynum >= 0 && entitynum < MAX_ENTITIES ); + + PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; + ENTITYPVSSTATUS *es = &pvs->m_Status[ entitynum ]; + + es->m_fTimeEnteredPVS = time; + es->mLastVisibility = inpvs; +} + +bool GetTimeToRecompute( int clientnum, int entitynum, float currenttime, bool& outCachedVisibility) +{ + ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); + ASSERT( entitynum >= 0 && entitynum < MAX_ENTITIES ); + + PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; + ENTITYPVSSTATUS *es = &pvs->m_Status[ entitynum ]; + + bool theTimeToRecompute = false; + + int theUseCaching = BALANCE_VAR(kDebugServerCaching); + if(theUseCaching) + { + // Always recompute players? + if((entitynum > 0) && (entitynum < MAX_CLIENTS)) + { + theTimeToRecompute = true; + } + // If we haven't computed for awhile, or our PVS has been reset + else if((currenttime > (es->m_fTimeEnteredPVS + kPVSCoherencyTime)) || (es->m_fTimeEnteredPVS == 0.0f)) + { + theTimeToRecompute = true; + } + else + { + outCachedVisibility = es->mLastVisibility; + } + } + else + { + theTimeToRecompute = true; + } + + return theTimeToRecompute; +} + +bool CheckEntityRecentlyInPVS( int clientnum, int entitynum, float currenttime ) +{ + ASSERT( clientnum >=0 && clientnum < MAX_CLIENTS ); + ASSERT( entitynum >= 0 && entitynum < MAX_ENTITIES ); + + PLAYERPVSSTATUS *pvs = &g_PVSStatus[ clientnum ]; + ENTITYPVSSTATUS *es = &pvs->m_Status[ entitynum ]; + + int theUseCaching = BALANCE_VAR(kDebugServerCaching); + if(!theUseCaching) + { + return false; + } + + // Wasn't in pvs before, sigh... + if ( es->m_fTimeEnteredPVS == 0.0f ) + { + return false; + } + + // If we've been saying yes for kPVSCoherencyTime, say no now instead so that we'll do a full check + if ( es->m_fTimeEnteredPVS + kPVSCoherencyTime < currenttime ) + { + return false; + } + + // Keep assuming it's in the pvs for a bit of time (trivial inclusion) + return true; +} + + +int kNumReturn0 = 0; +int kNumReturn1 = 0; +int kNumCached = 0; +int kNumComputed = 0; +int kMaxE = 0; +int kNumEntsProcessedForPlayerOne = 0; +int kMaxEProcessedForPlayerOne = 0; +extern int kServerFrameRate; + +// ENGINE_CHECK_VISIBILITY, from Yahn, with his comments: +// +// Here's the engine code. You'll note a slow path if the ent leaf cache is missed, which causes a recomputation via the much more expensive +// CM_HeadnodeVisible call. That does change the cache data on the entity, so that's likely your problem with blinking if you are doing some +// kind of memcpy operation on the old data. +//#ifdef 0 +//int DLL_CALLBACK SV_CheckVisibility( const struct edict_s *entity, unsigned char *pset ) +//{ +// int i; +// qboolean visible; +// edict_t *e; +// +// if ( !pset ) +// return 1; +// +// if ( entity->headnode >= 0 ) // +// { +// // we use num_leafs as a cache/counter, it will circle around, but so what +// // if we don't find the leaf we want here, we'll end up doing the full test anyway +// int leaf; +// i = 0; +// +// do +// { +// leaf = entity->leafnums[i]; +// // Cache is all -1 to start... +// if ( leaf == -1 ) +// break; +// +// if ( pset[ leaf >> 3 ] & (1 << (leaf & 7 ) ) ) +// { +// return 1; +// } +// +// i++; +// if ( i >= MAX_ENT_LEAFS ) +// break; +// +// } while ( 1 ); +// +// // Didn't find it in "cache" +// +// visible = CM_HeadnodeVisible( sv.worldmodel->nodes + entity->headnode, pset, &leaf ); +// if ( !visible ) +// { +// return 0; +// } +// +// // Cache leaf that was visible +// // +// e = ( edict_t * )entity; +// +// e->leafnums[ entity->num_leafs ] = (short)leaf; +// e->num_leafs = ( entity->num_leafs + 1 ) % MAX_ENT_LEAFS; +// +// return 2; +// } +// else +// { +// for (i=0 ; i < entity->num_leafs ; i++) +// { +// if ( pset[entity->leafnums[i] >> 3] & (1 << (entity->leafnums[i]&7) )) +// break; +// } +// +// visible = ( i < entity->num_leafs ); +// if ( !visible ) +// { +// return 0; +// } +// } +// +// return 1; +//} +//#endif + +// defaults for clientinfo messages +#define DEFAULT_VIEWHEIGHT 28 + +/* +=================== +CreateBaseline + +Creates baselines used for network encoding, especially for player data since players are not spawned until connect time. +=================== +*/ +void CreateBaseline( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ) +{ + baseline->origin = entity->v.origin; + baseline->angles = entity->v.angles; + baseline->frame = entity->v.frame; + baseline->skin = (short)entity->v.skin; + + // render information + baseline->rendermode = (byte)entity->v.rendermode; + baseline->renderamt = (byte)entity->v.renderamt; + baseline->rendercolor.r = (byte)entity->v.rendercolor.x; + baseline->rendercolor.g = (byte)entity->v.rendercolor.y; + baseline->rendercolor.b = (byte)entity->v.rendercolor.z; + baseline->renderfx = (byte)entity->v.renderfx; + + if ( player ) + { + baseline->mins = player_mins; + baseline->maxs = player_maxs; + + baseline->colormap = eindex; + baseline->modelindex = playermodelindex; + baseline->friction = 1.0; + baseline->movetype = MOVETYPE_WALK; + + baseline->scale = entity->v.scale; + baseline->solid = SOLID_SLIDEBOX; + baseline->framerate = 1.0; + baseline->gravity = 1.0; + + } + else + { + baseline->mins = entity->v.mins; + baseline->maxs = entity->v.maxs; + + baseline->colormap = 0; + baseline->modelindex = entity->v.modelindex;//SV_ModelIndex(pr_strings + entity->v.model); + baseline->movetype = entity->v.movetype; + + baseline->scale = entity->v.scale; + baseline->solid = entity->v.solid; + baseline->framerate = entity->v.framerate; + baseline->gravity = entity->v.gravity; + } +} + +typedef struct +{ + char name[32]; + int field; +} entity_field_alias_t; + +#define FIELD_ORIGIN0 0 +#define FIELD_ORIGIN1 1 +#define FIELD_ORIGIN2 2 +#define FIELD_ANGLES0 3 +#define FIELD_ANGLES1 4 +#define FIELD_ANGLES2 5 + +static entity_field_alias_t entity_field_alias[]= +{ + { "origin[0]", 0 }, + { "origin[1]", 0 }, + { "origin[2]", 0 }, + { "angles[0]", 0 }, + { "angles[1]", 0 }, + { "angles[2]", 0 }, +}; + +void Entity_FieldInit( struct delta_s *pFields ) +{ + entity_field_alias[ FIELD_ORIGIN0 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ORIGIN0 ].name ); + entity_field_alias[ FIELD_ORIGIN1 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ORIGIN1 ].name ); + entity_field_alias[ FIELD_ORIGIN2 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ORIGIN2 ].name ); + entity_field_alias[ FIELD_ANGLES0 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ANGLES0 ].name ); + entity_field_alias[ FIELD_ANGLES1 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ANGLES1 ].name ); + entity_field_alias[ FIELD_ANGLES2 ].field = DELTA_FINDFIELD( pFields, entity_field_alias[ FIELD_ANGLES2 ].name ); +} + +/* +================== +Entity_Encode + +Callback for sending entity_state_t info over network. +FIXME: Move to script +================== +*/ +void Entity_Encode( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) +{ + entity_state_t *f, *t; + int localplayer = 0; + static int initialized = 0; + + if ( !initialized ) + { + Entity_FieldInit( pFields ); + initialized = 1; + } + + f = (entity_state_t *)from; + t = (entity_state_t *)to; + + // Never send origin to local player, it's sent with more resolution in clientdata_t structure + localplayer = ( t->number - 1 ) == ENGINE_CURRENT_PLAYER(); + if ( localplayer ) + { + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + } + + if ( ( t->impacttime != 0 ) && ( t->starttime != 0 ) ) + { + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES0 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES1 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES2 ].field ); + } + + if ( ( t->movetype == MOVETYPE_FOLLOW ) && + ( t->aiment != 0 ) ) + { + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + } + else if ( t->aiment != f->aiment ) + { + DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + } +} + +static entity_field_alias_t player_field_alias[]= +{ + { "origin[0]", 0 }, + { "origin[1]", 0 }, + { "origin[2]", 0 }, +}; + +void Player_FieldInit( struct delta_s *pFields ) +{ + player_field_alias[ FIELD_ORIGIN0 ].field = DELTA_FINDFIELD( pFields, player_field_alias[ FIELD_ORIGIN0 ].name ); + player_field_alias[ FIELD_ORIGIN1 ].field = DELTA_FINDFIELD( pFields, player_field_alias[ FIELD_ORIGIN1 ].name ); + player_field_alias[ FIELD_ORIGIN2 ].field = DELTA_FINDFIELD( pFields, player_field_alias[ FIELD_ORIGIN2 ].name ); +} + +/* +================== +Player_Encode + +Callback for sending entity_state_t for players info over network. +================== +*/ +void Player_Encode( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) +{ + entity_state_t *f, *t; + int localplayer = 0; + static int initialized = 0; + + if ( !initialized ) + { + Player_FieldInit( pFields ); + initialized = 1; + } + + f = (entity_state_t *)from; + t = (entity_state_t *)to; + + // Never send origin to local player, it's sent with more resolution in clientdata_t structure + localplayer = ( t->number - 1 ) == ENGINE_CURRENT_PLAYER(); + if ( localplayer ) + { + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + } + + if ( ( t->movetype == MOVETYPE_FOLLOW ) && + ( t->aiment != 0 ) ) + { + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + } + else if ( t->aiment != f->aiment ) + { + DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + } +} + +#define CUSTOMFIELD_ORIGIN0 0 +#define CUSTOMFIELD_ORIGIN1 1 +#define CUSTOMFIELD_ORIGIN2 2 +#define CUSTOMFIELD_ANGLES0 3 +#define CUSTOMFIELD_ANGLES1 4 +#define CUSTOMFIELD_ANGLES2 5 +#define CUSTOMFIELD_SKIN 6 +#define CUSTOMFIELD_SEQUENCE 7 +#define CUSTOMFIELD_ANIMTIME 8 + +entity_field_alias_t custom_entity_field_alias[]= +{ + { "origin[0]", 0 }, + { "origin[1]", 0 }, + { "origin[2]", 0 }, + { "angles[0]", 0 }, + { "angles[1]", 0 }, + { "angles[2]", 0 }, + { "skin", 0 }, + { "sequence", 0 }, + { "animtime", 0 }, +}; + +void Custom_Entity_FieldInit( struct delta_s *pFields ) +{ + custom_entity_field_alias[ CUSTOMFIELD_ORIGIN0 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN0 ].name ); + custom_entity_field_alias[ CUSTOMFIELD_ORIGIN1 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN1 ].name ); + custom_entity_field_alias[ CUSTOMFIELD_ORIGIN2 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN2 ].name ); + custom_entity_field_alias[ CUSTOMFIELD_ANGLES0 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES0 ].name ); + custom_entity_field_alias[ CUSTOMFIELD_ANGLES1 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES1 ].name ); + custom_entity_field_alias[ CUSTOMFIELD_ANGLES2 ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES2 ].name ); + custom_entity_field_alias[ CUSTOMFIELD_SKIN ].field = DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_SKIN ].name ); + custom_entity_field_alias[ CUSTOMFIELD_SEQUENCE ].field= DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_SEQUENCE ].name ); + custom_entity_field_alias[ CUSTOMFIELD_ANIMTIME ].field= DELTA_FINDFIELD( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANIMTIME ].name ); +} + +/* +================== +Custom_Encode + +Callback for sending entity_state_t info ( for custom entities ) over network. +FIXME: Move to script +================== +*/ +void Custom_Encode( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) +{ + entity_state_t *f, *t; + int beamType; + static int initialized = 0; + + if ( !initialized ) + { + Custom_Entity_FieldInit( pFields ); + initialized = 1; + } + + f = (entity_state_t *)from; + t = (entity_state_t *)to; + + beamType = t->rendermode & 0x0f; + + if ( beamType != BEAM_POINTS && beamType != BEAM_ENTPOINT ) + { + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ORIGIN2 ].field ); + } + + if ( beamType != BEAM_POINTS ) + { + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES0 ].field ); + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES1 ].field ); + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANGLES2 ].field ); + } + + if ( beamType != BEAM_ENTS && beamType != BEAM_ENTPOINT ) + { + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_SKIN ].field ); + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_SEQUENCE ].field ); + } + + // animtime is compared by rounding first + // see if we really shouldn't actually send it + if ( (int)f->animtime == (int)t->animtime ) + { + DELTA_UNSETBYINDEX( pFields, custom_entity_field_alias[ CUSTOMFIELD_ANIMTIME ].field ); + } +} + +/* +================= +RegisterEncoders + +Allows game .dll to override network encoding of certain types of entities and tweak values, etc. +================= +*/ +void RegisterEncoders( void ) +{ + DELTA_ADDENCODER( "Entity_Encode", Entity_Encode ); + DELTA_ADDENCODER( "Custom_Encode", Custom_Encode ); + DELTA_ADDENCODER( "Player_Encode", Player_Encode ); +} + +int GetWeaponData( struct edict_s *player, struct weapon_data_s *info ) +{ + int i; + weapon_data_t *item; + entvars_t *pev = &player->v; + CBasePlayer *pl = ( CBasePlayer *) CBasePlayer::Instance( pev ); + AvHPlayer* thePlayer = dynamic_cast(pl); + CBasePlayerWeapon *gun; + + ItemInfo II; + + memset( info, 0, 32 * sizeof( weapon_data_t ) ); + + if ( !pl ) + return 1; + + // go through all of the weapons and make a list of the ones to pack + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + if ( pl->m_rgpPlayerItems[ i ] ) + { + // there's a weapon here. Should I pack it? + CBasePlayerItem *pPlayerItem = pl->m_rgpPlayerItems[ i ]; + + while ( pPlayerItem ) + { + gun = (CBasePlayerWeapon *)pPlayerItem->GetWeaponPtr(); + if ( gun && gun->UseDecrement() ) + { + // Get The ID. + memset( &II, 0, sizeof( II ) ); + gun->GetItemInfo( &II ); + + ASSERT((II.iId >= 0) && (II.iId < 32)); + + if ( II.iId >= 0 && II.iId < 32 ) + { + item = &info[ II.iId ]; + + item->m_iId = II.iId; + item->m_iClip = gun->m_iClip; + + item->m_flTimeWeaponIdle = max( gun->m_flTimeWeaponIdle, -0.001f ); + item->m_flNextPrimaryAttack = max( gun->m_flNextPrimaryAttack, -0.001f ); + item->m_flNextSecondaryAttack = max( gun->m_flNextSecondaryAttack, -0.001f ); + item->m_fInReload = gun->m_fInReload; + + //thePlayer->SetDebugCSP(item); + + //item->m_fInSpecialReload = gun->m_fInSpecialReload; + //item->fuser1 = max( gun->pev->fuser1, -0.001 ); + item->fuser2 = gun->m_flStartThrow; + item->fuser3 = gun->m_flReleaseThrow; + //item->iuser1 = gun->m_chargeReady; + //item->iuser2 = gun->m_fInAttack; + + // Pass along enabled state in iuser3 (for when hives and ensnare enable and disable weapons) + item->iuser3 = gun->pev->iuser3; + + //item->m_flPumpTime = max( gun->m_flPumpTime, -0.001 ); + } + } + pPlayerItem = pPlayerItem->m_pNext; + } + } + } + return 1; +} + +/* +================= +UpdateClientData + +Data sent to current client only +engine sets cd to 0 before calling. +================= +*/ +void UpdateClientData ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ) +{ + cd->flags = ent->v.flags; + cd->health = ent->v.health; + + cd->viewmodel = MODEL_INDEX( STRING( ent->v.viewmodel ) ); + + cd->waterlevel = ent->v.waterlevel; + cd->watertype = ent->v.watertype; + cd->weapons = ent->v.weapons; + + // Vectors + cd->origin = ent->v.origin; + cd->velocity = ent->v.velocity; + cd->view_ofs = ent->v.view_ofs; + cd->punchangle = ent->v.punchangle; + + cd->bInDuck = ent->v.bInDuck; + cd->flTimeStepSound = ent->v.flTimeStepSound; + cd->flDuckTime = ent->v.flDuckTime; + cd->flSwimTime = ent->v.flSwimTime; + cd->waterjumptime = ent->v.teleport_time; + + strcpy( cd->physinfo, ENGINE_GETPHYSINFO( ent ) ); + + cd->maxspeed = ent->v.maxspeed; + cd->fov = ent->v.fov; + cd->weaponanim = ent->v.weaponanim; + + cd->pushmsec = ent->v.pushmsec; + + // Spectator + cd->iuser1 = ent->v.iuser1; + cd->iuser2 = ent->v.iuser2; + + // AvH specials + cd->iuser3 = ent->v.iuser3; + cd->iuser4 = ent->v.iuser4; + cd->fuser1 = ent->v.fuser1; + cd->fuser2 = ent->v.fuser2; + cd->fuser3 = ent->v.fuser3; + + // Added by mmcguire for oriented skulk player models. + cd->vuser1 = ent->v.vuser1; + + // Added for extra player info for first-person spectating + cd->vuser4 = ent->v.vuser4; + + if ( sendweapons ) + { + entvars_t *pev = (entvars_t *)&ent->v; + CBasePlayer *pl = ( CBasePlayer *) CBasePlayer::Instance( pev ); + + if ( pl ) + { + cd->m_flNextAttack = pl->m_flNextAttack; + //cd->fuser2 = pl->m_flNextAmmoBurn; + //cd->fuser3 = pl->m_flAmmoStartCharge; + //cd->vuser1.x = pl->ammo_9mm; + //cd->vuser1.y = pl->ammo_357; + //cd->vuser1.z = pl->ammo_argrens; + //cd->ammo_nails = pl->ammo_bolts; + //cd->ammo_shells = pl->ammo_buckshot; + //cd->ammo_rockets = pl->ammo_rockets; + //cd->ammo_cells = pl->ammo_uranium; + //cd->vuser2.x = pl->ammo_hornets; + + if ( pl->m_pActiveItem ) + { + CBasePlayerWeapon *gun; + gun = (CBasePlayerWeapon *)pl->m_pActiveItem->GetWeaponPtr(); + if ( gun && gun->UseDecrement() ) + { + ItemInfo II; + memset( &II, 0, sizeof( II ) ); + gun->GetItemInfo( &II ); + + cd->m_iId = II.iId; + +// cd->vuser3.z = gun->m_iSecondaryAmmoType; +// cd->vuser4.x = gun->m_iPrimaryAmmoType; +// cd->vuser4.y = pl->m_rgAmmo[gun->m_iPrimaryAmmoType]; +// cd->vuser4.z = pl->m_rgAmmo[gun->m_iSecondaryAmmoType]; +// +// if ( pl->m_pActiveItem->m_iId == WEAPON_RPG ) +// { +// cd->vuser2.y = ( ( CRpg * )pl->m_pActiveItem)->m_fSpotActive; +// cd->vuser2.z = ( ( CRpg * )pl->m_pActiveItem)->m_cActiveRockets; +// } + } + } + } + } +} + +/* +================= +CmdStart + +We're about to run this usercmd for the specified player. We can set up groupinfo and masking here, etc. +This is the time to examine the usercmd for anything extra. This call happens even if think does not. +================= +*/ +void CmdStart( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ) +{ + entvars_t *pev = (entvars_t *)&player->v; + AvHPlayer *pl = ( AvHPlayer *) CBasePlayer::Instance( pev ); + + if( !pl ) + return; + + pl->SetCurrentCommand(cmd); + + pl->SetSizeForUser3(); + + if ( pl->pev->groupinfo != 0 ) + { + UTIL_SetGroupTrace( pl->pev->groupinfo, GROUP_OP_AND ); + } + + pl->random_seed = random_seed; +} + +/* +================= +CmdEnd + +Each cmdstart is exactly matched with a cmd end, clean up any group trace flags, etc. here +================= +*/ +void CmdEnd ( const edict_t *player ) +{ + entvars_t *pev = (entvars_t *)&player->v; + AvHPlayer *pl = ( AvHPlayer *) CBasePlayer::Instance( pev ); + + if( !pl ) + return; + + pl->SetSizeForUser3(); + + if ( pl->pev->groupinfo != 0 ) + { + UTIL_UnsetGroupTrace(); + } +} + +/* +================================ +ConnectionlessPacket + + Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max + size of the response_buffer, so you must zero it out if you choose not to respond. +================================ +*/ +int ConnectionlessPacket( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ) +{ + // Parse stuff from args + int max_buffer_size = *response_buffer_size; + + // Zero it out since we aren't going to respond. + // If we wanted to response, we'd write data into response_buffer + *response_buffer_size = 0; + + // Since we don't listen for anything here, just respond that it's a bogus message + // If we didn't reject the message, we'd return 1 for success instead. + return 0; +} + +/* +================================ +GetHullBounds + + Engine calls this to enumerate player collision hulls, for prediction. Return 0 if the hullnumber doesn't exist. +================================ +*/ +int GetHullBounds( int hullnumber, float *mins, float *maxs ) +{ + int iret = 0; + + switch ( hullnumber ) + { + case 0: + HULL0_MIN.CopyToArray(mins); + HULL0_MAX.CopyToArray(maxs); + iret = 1; + break; + + case 1: + HULL1_MIN.CopyToArray(mins); + HULL1_MAX.CopyToArray(maxs); + iret = 1; + break; + + case 2: + HULL2_MIN.CopyToArray(mins); + HULL2_MAX.CopyToArray(maxs); + iret = 1; + break; + + case 3: + HULL3_MIN.CopyToArray(mins); + HULL3_MAX.CopyToArray(maxs); + iret = 1; + break; + } + + return iret; +} + +/* +================================ +CreateInstancedBaselines + +Create pseudo-baselines for items that aren't placed in the map at spawn time, but which are likely +to be created during play ( e.g., grenades, ammo packs, projectiles, corpses, etc. ) +================================ +*/ +void CreateInstancedBaselines ( void ) +{ + int iret = 0; + entity_state_t state; + + memset( &state, 0, sizeof( state ) ); + + // Create any additional baselines here for things like grendates, etc. + // iret = ENGINE_INSTANCE_BASELINE( pc->pev->classname, &state ); + + // Destroy objects. + //UTIL_Remove( pc ); +} + +/* +================================ +InconsistentFile + +One of the ENGINE_FORCE_UNMODIFIED files failed the consistency check for the specified player + Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters ) +================================ +*/ + +static char *ignoreInConsistencyCheck[] = { + "sound/vox/ssay82.wav", + "sound/vox/ssay83.wav", + 0 +}; + +int InconsistentFile( const edict_t *player, const char *filename, char *disconnect_message ) +{ + // Server doesn't care? + if ( CVAR_GET_FLOAT( "mp_consistency" ) != 1 ) + return 0; + + int i=0; + while ( ignoreInConsistencyCheck[i] != 0 ) { + if ( !strcmp(ignoreInConsistencyCheck[i], filename) ) + return 0; + i++; + } + + /*if ( strstr(filename, "_detail.txt") != NULL ) { + ALERT(at_console, "Checking consistency of %s\n", filename); + string detailFilename=string("maps/") + STRING(gpGlobals->mapname) + string("_detail.txt"); + if ( strcmp(filename, detailFilename.c_str()) != 0 ) { + ALERT(at_console, "Skipping != %s\n", detailFilename.c_str()); + return 0; + } + }*/ + + // Default behavior is to kick the player + sprintf( disconnect_message, "Server is enforcing file consistency for %s\n", filename ); + + // Kick now with specified disconnect message. + return 1; +} + +/* +================================ +AllowLagCompensation + + The game .dll should return 1 if lag compensation should be allowed ( could also just set + the sv_unlag cvar. + Most games right now should return 0, until client-side weapon prediction code is written + and tested for them ( note you can predict weapons, but not do lag compensation, too, + if you want. +================================ +*/ +int AllowLagCompensation( void ) +{ + return 1; +} + +inline bool AvHDetermineVisibility(struct entity_state_s *state, int e, edict_t *ent, edict_t *host, unsigned char *pSet, bool& outIsParticleSystemEntity, bool& outIsParticleSystemEntityVisible) +{ + bool theIsVisible = false; + bool theIsParticleSystemEntity = (ent->v.classname == MAKE_STRING(kesParticlesCustom)); + outIsParticleSystemEntity = theIsParticleSystemEntity; + outIsParticleSystemEntityVisible = false; + + // Always propagate ourself + if(ent != host) + { + //if(GET_RUN_CODE(256)) + + bool theEntityIsAWeldable = ent->v.iuser3 == AVH_USER3_WELD;//(ent->v.classname == MAKE_STRING(kesWeldable)); + bool theIsNoBuild = ent->v.iuser3 == AVH_USER3_NOBUILD;//(ent->v.classname == MAKE_STRING(kesNoBuild)); + + // Don't send if EF_NODRAW objects that aren't the host (with some exceptions) + // This is a little convoluted because of performance (this function gets called many thousands of time per server tick) + + // Elven - edited out the !(ent->v.effects & EF_NODRAW) because it will always evaluate to true. + // Reasoning: if (v.effects & EF_NODRAW) and (ent!=host) were ever true, there would have been a return call in + // AddToFullPack before this function was called. + // Therefore, (ent->v.effects & EF_NODRAW) will always be false and !false will always be true. + // - undid this change as it was causing problems in comm mode. Structures, players etc. were not visible to the comm. + if(!(ent->v.effects & EF_NODRAW) || theEntityIsAWeldable || theIsNoBuild || (ent->v.classname == MAKE_STRING(kesMP3Audio))) + { + // This is duplicated from the above line for possible efficiency + bool theIsMp3Entity = (ent->v.classname == MAKE_STRING(kesMP3Audio)); + + // Ignore ents without valid / visible models + // ...but don't cull out particle systems ever. + // This should use an approximate bounding radius and cull it with the PVS normally but + // I can't figure out how to make it work. This would only cause more net traffic if + // the particle system entity was out of sight and moving around due to map triggers, not + // through normal operation + if(ent->v.modelindex || STRING(ent->v.model) || theIsParticleSystemEntity || theIsMp3Entity) + { + if(theIsMp3Entity) + { + CBaseEntity* theEntity = CBaseEntity::Instance(ent); + AvHMP3Audio* theMP3Audio = dynamic_cast(theEntity); + if(theMP3Audio) + { + float theDistanceToEntity = VectorDistance(host->v.origin, ent->v.origin); + int theFadeDistance = theMP3Audio->GetFadeDistance(); + if((theFadeDistance <= 0) || (theDistanceToEntity <= theFadeDistance)) + { + theIsVisible = true; + } + } + } + + // If we're in top down + bool thePlayerInTopDown = (host->v.iuser4 & MASK_TOPDOWN); + if(thePlayerInTopDown) + { + // Possible visible entities are world entities, sighted enemy entities, or entities on our team + bool theEntityIsWorldEntity = (ent->v.team == 0) && (ent->v.iuser3 != AVH_USER3_HIVE); + bool theIsSighted = (ent->v.iuser4 & MASK_VIS_SIGHTED); + bool theOnSameTeam = (host->v.team == ent->v.team); + + if(theEntityIsWorldEntity || theIsSighted || theOnSameTeam) + { + CBaseEntity* theEntity = CBaseEntity::Instance(ent); + bool theIsDoor = (dynamic_cast(theEntity) != NULL); + + // If entity is in front of the player and within visibility range + vec3_t theEntityOrigin = ent->v.origin; + float theMagnitude = theEntityOrigin.x + theEntityOrigin.y + theEntityOrigin.z; + if((theMagnitude < 5.0f) || theIsDoor) + { + theEntityOrigin.x = (ent->v.absmin.x + ent->v.absmax.x)/2.0f; + theEntityOrigin.y = (ent->v.absmin.y + ent->v.absmax.y)/2.0f; + theEntityOrigin.z = (ent->v.absmin.z + ent->v.absmax.z)/2.0f; + } + bool theIsRelevantForTopDownPlayer = AvHSUGetIsRelevantForTopDownPlayer(host->v.origin, theEntityOrigin); + if(!theIsRelevantForTopDownPlayer) + { + AvHPlayer* theReceivingPlayer = dynamic_cast(CBaseEntity::Instance(host)); + + // If the entity is selected, it's relevant + if(theEntity && theReceivingPlayer && theReceivingPlayer->GetIsSelected(theEntity->entindex())) + { + theIsRelevantForTopDownPlayer = true; + } + } + if(theIsRelevantForTopDownPlayer) + { + theIsVisible = true; + if(theIsParticleSystemEntity) + { + outIsParticleSystemEntityVisible = true; + } + } + } + } +// else +// { +// // Check visibility with the engine +// if(ENGINE_CHECK_VISIBILITY((const struct edict_s *)ent, pSet)) +// { +// theIsVisible = true; +// +// // If it's a particle system entity, save visibility for it +// if(theIsParticleSystemEntity) +// { +// outIsParticleSystemEntityVisible = true; +// } +// } +// else if(theIsParticleSystemEntity) +// { +// outIsParticleSystemEntityVisible = false; +// } +// } + } + } + } + else + { + theIsVisible = true; + } + + return theIsVisible; +} + +/* +AddToFullPack + +Return 1 if the entity state has been filled in for the ent and the entity will be propagated to the client, 0 otherwise + +state is the server maintained copy of the state info that is transmitted to the client +a MOD could alter values copied into state to send the "host" a different look for a particular entity update, etc. +e and ent are the entity that is being added to the update, if 1 is returned +host is the player's edict of the player whom we are sending the update to +player is 1 if the ent/e is a player and 0 otherwise +pSet is either the PAS or PVS that we previous set up. We can use it to ask the engine to filter the entity against the PAS or PVS. +we could also use the pas/ pvs that we set in SetupVisibility, if we wanted to. Caching the value is valid in that case, but still only for the current frame +*/ +int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ) +{ + int i; + + if(e > kMaxEProcessedForPlayerOne) + { + kNumEntsProcessedForPlayerOne++; + kMaxEProcessedForPlayerOne = e; + } + + if((ent != host) && !(GET_RUN_CODE(512))) + { + kNumReturn0++; + return 0; + } + + // This is a common case + // Ignore ents without valid / visible models + if ( !ent->v.modelindex || !STRING( ent->v.model ) ) + { + kNumReturn0++; + return 0; + } + + // don't send if flagged for NODRAW and it's not the host getting the message + if ( ( ent->v.effects & EF_NODRAW ) && + ( ent != host ) ) + { + kNumReturn0++; + return 0; + } + + // Don't send spectators to other players + if ( ( ent->v.flags & FL_SPECTATOR ) && ( ent != host ) ) + { + kNumReturn0++; + return 0; + } + + AvHUser3 theUser3 = (AvHUser3)ent->v.iuser3; + bool theCanBeTargetted = ent->v.targetname != 0; + + //if(!strcmp(theEntNameString, "func_illusionary")) + if(theUser3 == AVH_USER3_FUNC_ILLUSIONARY) + { + // Check invisibility flags + if(host->v.iuser4 & MASK_TOPDOWN) + { + if(FBitSet(ent->v.spawnflags, 1)) + { + if(pSet != NULL) + { + kNumReturn0++; + return 0; + } + } + } + // If we're not commander, and "invisible for player" is set + else + { + if(FBitSet(ent->v.spawnflags, 2)) + { + if(pSet != NULL) + { + kNumReturn0++; + return 0; + } + } + } + } + + + // Some entities can be determined to be visible without the engine check + bool theIsParticleSystemEntity = false; + bool theIsParticleSystemEntityVisible = false; + bool theIsVisible = AvHDetermineVisibility(state, e, ent, host, pSet, theIsParticleSystemEntity, theIsParticleSystemEntityVisible); + if(!theIsVisible) + { + // Check to see if the player has left their previous leaf. If so, reset player's PVS info. + int hostnum = ENTINDEX( host ) - 1; + if ( TimeToResetPVS( host, hostnum ) ) + { + ResetPlayerPVS( host, hostnum ); + } + + // Ignore if not the host and not touching a PVS/PAS leaf + // If pSet is NULL, then the test will always succeed and the entity will be added to the update + if ( ent != host ) + { + if(GetTimeToRecompute( hostnum, e, gpGlobals->time, theIsVisible)) + { + // Do time consuming check + theIsVisible = ENGINE_CHECK_VISIBILITY( (const struct edict_s *)ent, pSet ); + + MarkEntityInPVS( hostnum, e, gpGlobals->time, theIsVisible); + + kNumComputed++; + } + + if(!theIsVisible) + { + kNumReturn0++; + return 0; + } + } + + // Don't send entity to local client if the client says it's predicting the entity itself. + if ( ent->v.flags & FL_SKIPLOCALHOST ) + { + if ( ( hostflags & 1 ) && ( ent->v.owner == host ) ) + { + kNumReturn0++; + return 0; + } + } + + if ( host->v.groupinfo ) + { + UTIL_SetGroupTrace( host->v.groupinfo, GROUP_OP_AND ); + + // Should always be set, of course + if ( ent->v.groupinfo ) + { + if ( g_groupop == GROUP_OP_AND ) + { + if ( !(ent->v.groupinfo & host->v.groupinfo ) ) + { + kNumReturn0++; + return 0; + } + } + else if ( g_groupop == GROUP_OP_NAND ) + { + if ( ent->v.groupinfo & host->v.groupinfo ) + { + kNumReturn0++; + return 0; + } + } + } + + UTIL_UnsetGroupTrace(); + } + } + + memset( state, 0, sizeof( *state ) ); + + // Assign index so we can track this entity from frame to frame and + // delta from it. + state->number = e; + state->entityType = ENTITY_NORMAL; + + // Flag custom entities. + if ( ent->v.flags & FL_CUSTOMENTITY ) + { + state->entityType = ENTITY_BEAM; + } + + // + // Copy state data + // + + // Round animtime to nearest millisecond + state->animtime = (int)(1000.0 * ent->v.animtime ) / 1000.0; + + //memcpy( state->origin, ent->v.origin, 3 * sizeof( float ) ); + state->origin = ent->v.origin; + + //memcpy( state->angles, ent->v.angles, 3 * sizeof( float ) ); + state->angles = ent->v.angles; + + //memcpy( state->mins, ent->v.mins, 3 * sizeof( float ) ); + state->mins = ent->v.mins; + + //memcpy( state->maxs, ent->v.maxs, 3 * sizeof( float ) ); + state->maxs = ent->v.maxs; + + //memcpy( state->startpos, ent->v.startpos, 3 * sizeof( float ) ); + state->startpos = ent->v.startpos; + + //memcpy( state->endpos, ent->v.endpos, 3 * sizeof( float ) ); + state->endpos = ent->v.endpos; + + state->impacttime = ent->v.impacttime; + state->starttime = ent->v.starttime; + + state->modelindex = ent->v.modelindex; + + state->frame = ent->v.frame; + + state->skin = ent->v.skin; + state->effects = ent->v.effects; + + // This non-player entity is being moved by the game .dll and not the physics simulation system + // make sure that we interpolate it's position on the client if it moves + if ( !player && + ent->v.animtime && + ent->v.velocity[ 0 ] == 0 && + ent->v.velocity[ 1 ] == 0 && + ent->v.velocity[ 2 ] == 0 ) + { + state->eflags |= EFLAG_SLERP; + } + + state->scale = ent->v.scale; + state->solid = ent->v.solid; + state->colormap = ent->v.colormap; + state->movetype = ent->v.movetype; + state->sequence = ent->v.sequence; + state->framerate = ent->v.framerate; + state->body = ent->v.body; + + for (i = 0; i < 4; i++) + { + state->controller[i] = ent->v.controller[i]; + } + + for (i = 0; i < 2; i++) + { + state->blending[i] = ent->v.blending[i]; + } + + state->rendermode = ent->v.rendermode; + state->renderamt = ent->v.renderamt; + state->renderfx = ent->v.renderfx; + state->rendercolor.r = ent->v.rendercolor[0]; + state->rendercolor.g = ent->v.rendercolor[1]; + state->rendercolor.b = ent->v.rendercolor[2]; + + + // If classname indicates an entity that fades depending on viewing player role + const char* theEntNameString = STRING(ent->v.classname); + + if(!player && AvHSSUGetIsClassNameFadeable(theEntNameString)) + { + CBaseEntity* theSeethrough = CBaseEntity::Instance(ent); + ASSERT(theSeethrough); + + // Default to player is full visibility + state->rendermode = kRenderNormal; + + int theAlphaValue = theSeethrough->pev->fuser2; + if(host->v.iuser4 & MASK_TOPDOWN) + { + theAlphaValue = theSeethrough->pev->fuser1; + } + + if(theAlphaValue != 255) + { + //state->rendermode = kRenderTransAdd; + state->rendermode = kRenderTransTexture; + state->renderamt = theAlphaValue; + if(state->renderamt == 0) + { + state->effects |= EF_NODRAW; + } + + if(host->v.iuser4 & MASK_TOPDOWN) + { + state->solid = SOLID_NOT; + } + + } + + } + + // Inactive hives should be drawn for players on their team + if((ent->v.iuser3 == AVH_USER3_HIVE) && (!GetHasUpgrade(ent->v.iuser4, MASK_BUILDABLE))) + { + // Assumes that aliens of both teams can build on the same hive location + AvHPlayer* theReceivingPlayer = dynamic_cast(CBaseEntity::Instance(host)); + if(theReceivingPlayer && theReceivingPlayer->GetIsAlien()) + { + state->renderamt = 128; + } + } + + // Handle cloakables here + if(!AvHSUGetIsExternalClassName(theEntNameString)) + { + AvHCloakable* theCloakable = dynamic_cast(CBaseEntity::Instance(ent)); + if(theCloakable) + { + // Now updated in gamerules + theCloakable->Update(); + + float theOpacityScalar = theCloakable->GetOpacity(); + if(theOpacityScalar < 1.0f) + { + int theBaseOpacity = kAlienStructureCloakAmount; + int theOpacityRange = 255 - kAlienStructureCloakAmount; + + // Allow spectators to see cloaked entities as if they are the player they are following, or as if they are on the same team as the alien otherwise + bool theCanSeeCloaked = (host->v.team == ent->v.team); + if(!theCanSeeCloaked) + { + int theHostIUserOne = host->v.iuser1; + if((theHostIUserOne == OBS_CHASE_LOCKED) || (theHostIUserOne == OBS_CHASE_FREE) || (theHostIUserOne == OBS_IN_EYE) ) + { + // View entities as player we're tracking + int theHostIUserTwo = host->v.iuser2; + CBaseEntity* theSpectatingEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theHostIUserTwo)); + if(theSpectatingEntity) + { + int theSpectatingEntityTeamNumber = theSpectatingEntity->pev->team; + if((theSpectatingEntityTeamNumber == ent->v.team) || (host->v.team == TEAM_IND)) + { + theCanSeeCloaked = true; + } + } + } + } + + if(theCanSeeCloaked) + { + theBaseOpacity = kAlienSelfCloakingBaseOpacity; + theOpacityRange = 255 - theBaseOpacity; + } + + int theOpacity = theBaseOpacity + theOpacityScalar*theOpacityRange; + theOpacity = max(min(255, theOpacity), 0); + + state->rendermode = kRenderTransTexture; + state->renderamt = theOpacity; + } + } + } + + // Spectator + state->iuser1 = ent->v.iuser1; + state->iuser2 = ent->v.iuser2; + + // AvH specials + state->iuser3 = ent->v.iuser3; + state->iuser4 = ent->v.iuser4; + + // Slightly different processing for particle system entities +// if(theIsParticleSystemEntity) +// { +// // propagate weapon model as custom data) +// state->weaponmodel = ent->v.weaponmodel; +// } + + state->fuser1 = ent->v.fuser1; + state->fuser2 = ent->v.fuser2; + state->fuser3 = ent->v.fuser3; + state->vuser4 = ent->v.vuser4; + + + + + + + state->aiment = 0; + if ( ent->v.aiment ) + { + state->aiment = ENTINDEX( ent->v.aiment ); + } + + state->owner = 0; + if ( ent->v.owner ) + { + int owner = ENTINDEX( ent->v.owner ); + + // Only care if owned by a player + if ( owner >= 1 && owner <= gpGlobals->maxClients ) + { + state->owner = owner; + } + } + + // HACK: Somewhat... + // Class is overridden for non-players to signify a breakable glass object ( sort of a class? ) + if ( !player ) + { + state->playerclass = ent->v.playerclass; + } + + // Propagate team for all entities (mainly needed for client-side lasso selection) + state->team = ent->v.team; + + // Check special vision mode + AvHPlayer* theReceivingPlayer = dynamic_cast(CBaseEntity::Instance(host)); + if(theReceivingPlayer && (theReceivingPlayer->GetIsAlienSightActive())) + { + bool marineGlow = false; + if (player) + { + // Uncomment below to enable range for the alien flashlight + // vec3_t theDistanceVec; + // VectorSubtract(theReceivingPlayer->pev->origin, ent->v.origin, theDistanceVec); + + // int theDistance = theDistanceVec[0] * theDistanceVec[0] + theDistanceVec[1] * theDistanceVec[1] + theDistanceVec[2] * theDistanceVec[2]; + // int theRange = BALANCE_VAR(kAlienFlashlightRange); + // marineGlow = (theDistance < ( theRange * theRange)); + marineGlow = true; + } + + if(( marineGlow || (ent->v.team == theReceivingPlayer->pev->team)) && (ent != theReceivingPlayer->edict()) && (ent->v.team != TEAM_IND) && (ent->v.team != TEAM_SPECT ) && (ent->v.classname != MAKE_STRING(kesTeamWebStrand)) ) + { + state->rendermode = kRenderTransAdd; + state->renderamt = 150; + } + } + + // Special stuff for players only + if(player) + { + state->basevelocity = ent->v.basevelocity; + + state->weaponmodel = MODEL_INDEX( STRING( ent->v.weaponmodel ) ); + state->gaitsequence = ent->v.gaitsequence; + state->spectator = ent->v.flags & FL_SPECTATOR; + state->friction = ent->v.friction; + + state->gravity = ent->v.gravity; + + // New SDK doesn't propagate team for a reason? + //state->playerclass = ent->v.playerclass; + //state->team = ent->v.team; + + bool theIsDucking = ent->v.flags & FL_DUCKING; + state->usehull = AvHMUGetHull(theIsDucking, ent->v.iuser3); + + // Propagate "energy level" + state->fuser3 = ent->v.fuser3; + + // For skulk oriented player model + state->vuser1 = ent->v.vuser1; + + // For weapons + state->vuser4 = ent->v.vuser4; + + //state->skin = theTargetPlayer->GetSkin(); + state->skin = ent->v.skin; + state->playerclass = ent->v.playerclass; + // state->armorvalue = ent->v.armorvalue; + + AvHPlayer* thePlayerEntity = dynamic_cast(CBaseEntity::Instance(ent)); + if(thePlayerEntity && thePlayerEntity->GetIsTemporarilyInvulnerable()) + { + int theTeamIndex = ent->v.team; + ASSERT(theTeamIndex >= 0); + ASSERT(theTeamIndex < iNumberOfTeamColors); + + state->renderfx = kRenderFxGlowShell; + state->rendercolor.r = kTeamColors[theTeamIndex][0]; + state->rendercolor.g = kTeamColors[theTeamIndex][1]; + state->rendercolor.b = kTeamColors[theTeamIndex][2]; + state->renderamt = kInvulShellRenderAmount; + } + } + + state->health = ent->v.health; + + kNumReturn1++; + + return 1; +} + diff --git a/main/source/dlls/combat.cpp b/main/source/dlls/combat.cpp index 46ac8a6..f76bce3 100644 --- a/main/source/dlls/combat.cpp +++ b/main/source/dlls/combat.cpp @@ -1,1891 +1,1914 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -/* - -===== combat.cpp ======================================================== - - functions dealing with damage infliction & death - -*/ -// -// $Workfile: combat.cpp $ -// $Date: 2002/11/22 23:26:35 $ -// -//------------------------------------------------------------------------------- -// $Log: combat.cpp,v $ -// Revision 1.19 2002/11/22 23:26:35 Flayra -// - Skulks had to aim too high to hit structures, so trying this approach to fixing the melee in vents bug -// -// Revision 1.18 2002/11/22 21:09:50 Flayra -// - Explosion fixes (oops!) -// - Fix melee when in vents (I think) -// -// Revision 1.17 2002/11/13 01:49:33 Flayra -// - Proper death message logging for Psychostats -// -// Revision 1.16 2002/11/12 02:18:08 Flayra -// - Pass on inflictor properly for standard logging -// -// Revision 1.15 2002/11/06 01:36:16 Flayra -// - Allow scalar to apply to damage from gamerules (used for friendly fire) -// -// Revision 1.14 2002/10/03 18:26:36 Flayra -// - Removed all pushback and forces when doing damage to players -// -// Revision 1.13 2002/08/16 02:25:21 Flayra -// - New damage types -// -// Revision 1.12 2002/07/10 14:36:13 Flayra -// - Removed major cause of lag: redundant server-side decals. Also removed duplicate texture sound. -// -// Revision 1.11 2002/07/08 16:40:05 Flayra -// - Fix bug where melee attacks couldn't aim up or down, reworked bullet firing to add random spread (bug #236) -// -//=============================================================================== - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "soundent.h" -#include "decals.h" -#include "animation.h" -#include "weapons.h" -#include "func_break.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHTurret.h" -//#include "mod/AvHMovementUtil.h" - -extern DLL_GLOBAL Vector g_vecAttackDir; -extern DLL_GLOBAL int g_iSkillLevel; - -extern Vector VecBModelOrigin( entvars_t* pevBModel ); -extern entvars_t *g_pevLastInflictor; - -#define GERMAN_GIB_COUNT 4 -#define HUMAN_GIB_COUNT 6 -#define ALIEN_GIB_COUNT 4 - - -// HACKHACK -- The gib velocity equations don't work -void CGib :: LimitVelocity( void ) -{ - float length = pev->velocity.Length(); - - // ceiling at 1500. The gib velocity equation is not bounded properly. Rather than tune it - // in 3 separate places again, I'll just limit it here. - if ( length > 1500.0 ) - pev->velocity = pev->velocity.Normalize() * 1500; // This should really be sv_maxvelocity * 0.75 or something -} - - -void CGib :: SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs ) -{ - int i; - - if ( g_Language == LANGUAGE_GERMAN ) - { - // no sticky gibs in germany right now! - return; - } - - for ( i = 0 ; i < cGibs ; i++ ) - { - CGib *pGib = GetClassPtr( (CGib *)NULL ); - - pGib->Spawn( "models/stickygib.mdl" ); - pGib->pev->body = RANDOM_LONG(0,2); - - if ( pevVictim ) - { - pGib->pev->origin.x = vecOrigin.x + RANDOM_FLOAT( -3, 3 ); - pGib->pev->origin.y = vecOrigin.y + RANDOM_FLOAT( -3, 3 ); - pGib->pev->origin.z = vecOrigin.z + RANDOM_FLOAT( -3, 3 ); - - /* - pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) ); - pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) ); - pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ); - */ - - // make the gib fly away from the attack vector - pGib->pev->velocity = g_vecAttackDir * -1; - - // mix in some noise - pGib->pev->velocity.x += RANDOM_FLOAT ( -0.15, 0.15 ); - pGib->pev->velocity.y += RANDOM_FLOAT ( -0.15, 0.15 ); - pGib->pev->velocity.z += RANDOM_FLOAT ( -0.15, 0.15 ); - - pGib->pev->velocity = pGib->pev->velocity * 900; - - pGib->pev->avelocity.x = RANDOM_FLOAT ( 250, 400 ); - pGib->pev->avelocity.y = RANDOM_FLOAT ( 250, 400 ); - - // copy owner's blood color - pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); - - if ( pevVictim->health > -50) - { - pGib->pev->velocity = pGib->pev->velocity * 0.7; - } - else if ( pevVictim->health > -200) - { - pGib->pev->velocity = pGib->pev->velocity * 2; - } - else - { - pGib->pev->velocity = pGib->pev->velocity * 4; - } - - - pGib->pev->movetype = MOVETYPE_TOSS; - pGib->pev->solid = SOLID_BBOX; - UTIL_SetSize ( pGib->pev, Vector ( 0, 0 ,0 ), Vector ( 0, 0, 0 ) ); - pGib->SetTouch ( &CGib::StickyGibTouch ); - pGib->SetThink (NULL); - } - pGib->LimitVelocity(); - } -} - -void CGib :: SpawnHeadGib( entvars_t *pevVictim ) -{ - CGib *pGib = GetClassPtr( (CGib *)NULL ); - - if ( g_Language == LANGUAGE_GERMAN ) - { - pGib->Spawn( "models/germangibs.mdl" );// throw one head - pGib->pev->body = 0; - } - else - { - pGib->Spawn( "models/hgibs.mdl" );// throw one head - pGib->pev->body = 0; - } - - if ( pevVictim ) - { - pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs; - - edict_t *pentPlayer = FIND_CLIENT_IN_PVS( pGib->edict() ); - - if ( RANDOM_LONG ( 0, 100 ) <= 5 && pentPlayer ) - { - // 5% chance head will be thrown at player's face. - entvars_t *pevPlayer; - - pevPlayer = VARS( pentPlayer ); - pGib->pev->velocity = ( ( pevPlayer->origin + pevPlayer->view_ofs ) - pGib->pev->origin ).Normalize() * 300; - pGib->pev->velocity.z += 100; - } - else - { - pGib->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); - } - - - pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); - pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); - - // copy owner's blood color - pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); - - if ( pevVictim->health > -50) - { - pGib->pev->velocity = pGib->pev->velocity * 0.7; - } - else if ( pevVictim->health > -200) - { - pGib->pev->velocity = pGib->pev->velocity * 2; - } - else - { - pGib->pev->velocity = pGib->pev->velocity * 4; - } - } - pGib->LimitVelocity(); -} - -void CGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ) -{ - int cSplat; - - for ( cSplat = 0 ; cSplat < cGibs ; cSplat++ ) - { - CGib *pGib = GetClassPtr( (CGib *)NULL ); - - if ( g_Language == LANGUAGE_GERMAN ) - { - pGib->Spawn( "models/germangibs.mdl" ); - pGib->pev->body = RANDOM_LONG(0,GERMAN_GIB_COUNT-1); - } - else - { - if ( human ) - { - // human pieces - pGib->Spawn( "models/hgibs.mdl" ); - pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib) - } - else - { - // aliens - pGib->Spawn( "models/agibs.mdl" ); - pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1); - } - } - - if ( pevVictim ) - { - // spawn the gib somewhere in the monster's bounding volume - pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) ); - pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) ); - pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ) + 1; // absmin.z is in the floor because the engine subtracts 1 to enlarge the box - - // make the gib fly away from the attack vector - pGib->pev->velocity = g_vecAttackDir * -1; - - // mix in some noise - pGib->pev->velocity.x += RANDOM_FLOAT ( -0.25, 0.25 ); - pGib->pev->velocity.y += RANDOM_FLOAT ( -0.25, 0.25 ); - pGib->pev->velocity.z += RANDOM_FLOAT ( -0.25, 0.25 ); - - pGib->pev->velocity = pGib->pev->velocity * RANDOM_FLOAT ( 300, 400 ); - - pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); - pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); - - // copy owner's blood color - pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); - - if ( pevVictim->health > -50) - { - pGib->pev->velocity = pGib->pev->velocity * 0.7; - } - else if ( pevVictim->health > -200) - { - pGib->pev->velocity = pGib->pev->velocity * 2; - } - else - { - pGib->pev->velocity = pGib->pev->velocity * 4; - } - - pGib->pev->solid = SOLID_BBOX; - UTIL_SetSize ( pGib->pev, Vector( 0 , 0 , 0 ), Vector ( 0, 0, 0 ) ); - } - pGib->LimitVelocity(); - } -} - - -BOOL CBaseMonster :: HasHumanGibs( void ) -{ - int myClass = Classify(); - - if ( myClass == CLASS_HUMAN_MILITARY || - myClass == CLASS_PLAYER_ALLY || - myClass == CLASS_HUMAN_PASSIVE || - myClass == CLASS_PLAYER ) - - return TRUE; - - return FALSE; -} - - -BOOL CBaseMonster :: HasAlienGibs( void ) -{ - int myClass = Classify(); - - if ( myClass == CLASS_ALIEN_MILITARY || - myClass == CLASS_ALIEN_MONSTER || - myClass == CLASS_ALIEN_PASSIVE || - myClass == CLASS_INSECT || - myClass == CLASS_ALIEN_PREDATOR || - myClass == CLASS_ALIEN_PREY ) - - return TRUE; - - return FALSE; -} - - -void CBaseMonster::FadeMonster( void ) -{ - StopAnimation(); - pev->velocity = g_vecZero; - pev->movetype = MOVETYPE_NONE; - pev->avelocity = g_vecZero; - pev->animtime = gpGlobals->time; - pev->effects |= EF_NOINTERP; - SUB_StartFadeOut(); -} - -//========================================================= -// GibMonster - create some gore and get rid of a monster's -// model. -//========================================================= -void CBaseMonster :: GibMonster( void ) -{ - TraceResult tr; - BOOL gibbed = FALSE; - - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM); - - // only humans throw skulls !!!UNDONE - eventually monsters will have their own sets of gibs - if ( HasHumanGibs() ) - { - if ( CVAR_GET_FLOAT("violence_hgibs") != 0 ) // Only the player will ever get here - { - CGib::SpawnHeadGib( pev ); - CGib::SpawnRandomGibs( pev, 4, 1 ); // throw some human gibs. - } - gibbed = TRUE; - } - else if ( HasAlienGibs() ) - { - if ( CVAR_GET_FLOAT("violence_agibs") != 0 ) // Should never get here, but someone might call it directly - { - CGib::SpawnRandomGibs( pev, 4, 0 ); // Throw alien gibs - } - gibbed = TRUE; - } - - if ( !IsPlayer() ) - { - if ( gibbed ) - { - // don't remove players! - SetThink ( &CBaseMonster::SUB_Remove ); - pev->nextthink = gpGlobals->time; - } - else - { - FadeMonster(); - } - } -} - -//========================================================= -// GetDeathActivity - determines the best type of death -// anim to play. -//========================================================= -Activity CBaseMonster :: GetDeathActivity ( void ) -{ - Activity deathActivity; - BOOL fTriedDirection; - float flDot; - TraceResult tr; - Vector vecSrc; - - if ( pev->deadflag != DEAD_NO ) - { - // don't run this while dying. - return m_IdealActivity; - } - - vecSrc = Center(); - - fTriedDirection = FALSE; - deathActivity = ACT_DIESIMPLE;// in case we can't find any special deaths to do. - - UTIL_MakeVectors ( pev->angles ); - flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 ); - - switch ( m_LastHitGroup ) - { - // try to pick a region-specific death. - case HITGROUP_HEAD: - deathActivity = ACT_DIE_HEADSHOT; - break; - - case HITGROUP_STOMACH: - deathActivity = ACT_DIE_GUTSHOT; - break; - - case HITGROUP_GENERIC: - // try to pick a death based on attack direction - fTriedDirection = TRUE; - - if ( flDot > 0.3 ) - { - deathActivity = ACT_DIEFORWARD; - } - else if ( flDot <= -0.3 ) - { - deathActivity = ACT_DIEBACKWARD; - } - break; - - default: - // try to pick a death based on attack direction - fTriedDirection = TRUE; - - if ( flDot > 0.3 ) - { - deathActivity = ACT_DIEFORWARD; - } - else if ( flDot <= -0.3 ) - { - deathActivity = ACT_DIEBACKWARD; - } - break; - } - - - // can we perform the prescribed death? - if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) - { - // no! did we fail to perform a directional death? - if ( fTriedDirection ) - { - // if yes, we're out of options. Go simple. - deathActivity = ACT_DIESIMPLE; - } - else - { - // cannot perform the ideal region-specific death, so try a direction. - if ( flDot > 0.3 ) - { - deathActivity = ACT_DIEFORWARD; - } - else if ( flDot <= -0.3 ) - { - deathActivity = ACT_DIEBACKWARD; - } - } - } - - if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) - { - // if we're still invalid, simple is our only option. - deathActivity = ACT_DIESIMPLE; - } - - if ( deathActivity == ACT_DIEFORWARD ) - { - // make sure there's room to fall forward - UTIL_TraceHull ( vecSrc, vecSrc + gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr ); - - if ( tr.flFraction != 1.0 ) - { - deathActivity = ACT_DIESIMPLE; - } - } - - if ( deathActivity == ACT_DIEBACKWARD ) - { - // make sure there's room to fall backward - UTIL_TraceHull ( vecSrc, vecSrc - gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr ); - - if ( tr.flFraction != 1.0 ) - { - deathActivity = ACT_DIESIMPLE; - } - } - - return deathActivity; -} - -//========================================================= -// GetSmallFlinchActivity - determines the best type of flinch -// anim to play. -//========================================================= -Activity CBaseMonster :: GetSmallFlinchActivity ( void ) -{ - Activity flinchActivity; - BOOL fTriedDirection; - float flDot; - - fTriedDirection = FALSE; - UTIL_MakeVectors ( pev->angles ); - flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 ); - - switch ( m_LastHitGroup ) - { - // pick a region-specific flinch - case HITGROUP_HEAD: - flinchActivity = ACT_FLINCH_HEAD; - break; - case HITGROUP_STOMACH: - flinchActivity = ACT_FLINCH_STOMACH; - break; - case HITGROUP_LEFTARM: - flinchActivity = ACT_FLINCH_LEFTARM; - break; - case HITGROUP_RIGHTARM: - flinchActivity = ACT_FLINCH_RIGHTARM; - break; - case HITGROUP_LEFTLEG: - flinchActivity = ACT_FLINCH_LEFTLEG; - break; - case HITGROUP_RIGHTLEG: - flinchActivity = ACT_FLINCH_RIGHTLEG; - break; - case HITGROUP_GENERIC: - default: - // just get a generic flinch. - flinchActivity = ACT_SMALL_FLINCH; - break; - } - - - // do we have a sequence for the ideal activity? - if ( LookupActivity ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE ) - { - flinchActivity = ACT_SMALL_FLINCH; - } - - return flinchActivity; -} - - -void CBaseMonster::BecomeDead( void ) -{ - pev->takedamage = DAMAGE_YES;// don't let autoaim aim at corpses. - - // give the corpse half of the monster's original maximum health. - pev->health = pev->max_health / 2; - pev->max_health = 5; // max_health now becomes a counter for how many blood decals the corpse can place. - - // make the corpse fly away from the attack vector - pev->movetype = MOVETYPE_TOSS; - //pev->flags &= ~FL_ONGROUND; - //pev->origin.z += 2; - //pev->velocity = g_vecAttackDir * -1; - //pev->velocity = pev->velocity * RANDOM_FLOAT( 300, 400 ); -} - - -BOOL CBaseMonster::ShouldGibMonster( int iGib ) -{ - if ( ( iGib == GIB_NORMAL && pev->health < GIB_HEALTH_VALUE ) || ( iGib == GIB_ALWAYS ) ) - return TRUE; - - return FALSE; -} - - -void CBaseMonster::CallGibMonster( void ) -{ - BOOL fade = FALSE; - - if ( HasHumanGibs() ) - { - if ( CVAR_GET_FLOAT("violence_hgibs") == 0 ) - fade = TRUE; - } - else if ( HasAlienGibs() ) - { - if ( CVAR_GET_FLOAT("violence_agibs") == 0 ) - fade = TRUE; - } - - pev->takedamage = DAMAGE_NO; - pev->solid = SOLID_NOT;// do something with the body. while monster blows up - - if ( fade ) - { - FadeMonster(); - } - else - { - pev->effects = EF_NODRAW; // make the model invisible. - GibMonster(); - } - - pev->deadflag = DEAD_DEAD; - FCheckAITrigger(); - - // don't let the status bar glitch for players.with <0 health. - if (pev->health < -99) - { - pev->health = 0; - } - - if ( ShouldFadeOnDeath() && !fade ) - UTIL_Remove(this); -} - - -/* -============ -Killed -============ -*/ -void CBaseMonster :: Killed( entvars_t *pevAttacker, int iGib ) -{ - unsigned int cCount = 0; - BOOL fDone = FALSE; - - CBaseEntity* theBaseEntity = CBaseEntity::Instance(pevAttacker->owner); - if(!theBaseEntity) - { - theBaseEntity = CBaseEntity::Instance(pevAttacker); - } - ASSERT(theBaseEntity); - theBaseEntity->AwardKill(this->pev); - - if ( HasMemory( bits_MEMORY_KILLED ) ) - { - if ( ShouldGibMonster( iGib ) ) - CallGibMonster(); - return; - } - - Remember( bits_MEMORY_KILLED ); - - // clear the deceased's sound channels.(may have been firing or reloading when killed) - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/null.wav", 1, ATTN_NORM); - m_IdealMonsterState = MONSTERSTATE_DEAD; - // Make sure this condition is fired too (TakeDamage breaks out before this happens on death) - SetConditions( bits_COND_LIGHT_DAMAGE ); - - // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality. - CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner); - if ( pOwner ) - { - pOwner->DeathNotice( pev ); - } - - if ( ShouldGibMonster( iGib ) ) - { - CallGibMonster(); - return; - } - else if ( pev->flags & FL_MONSTER ) - { - SetTouch( NULL ); - BecomeDead(); - } - - // don't let the status bar glitch for players.with <0 health. - if (pev->health < -99) - { - pev->health = 0; - } - - //pev->enemy = ENT( pevAttacker );//why? (sjb) - - m_IdealMonsterState = MONSTERSTATE_DEAD; - -} - -// -// fade out - slowly fades a entity out, then removes it. -// -// DON'T USE ME FOR GIBS AND STUFF IN MULTIPLAYER! -// SET A FUTURE THINK AND A RENDERMODE!! -void CBaseEntity :: SUB_StartFadeOut ( void ) -{ - if (pev->rendermode == kRenderNormal) - { - pev->renderamt = 255; - pev->rendermode = kRenderTransTexture; - } - - pev->solid = SOLID_NOT; - pev->avelocity = g_vecZero; - - pev->nextthink = gpGlobals->time + 0.1; - SetThink ( &CBaseEntity::SUB_FadeOut ); -} - -void CBaseEntity :: SUB_FadeOut ( void ) -{ - if ( pev->renderamt > 7 ) - { - pev->renderamt -= 7; - pev->nextthink = gpGlobals->time + 0.1; - } - else - { - pev->renderamt = 0; - pev->nextthink = gpGlobals->time + 0.2; - SetThink ( &CBaseEntity::SUB_Remove ); - } -} - -//========================================================= -// WaitTillLand - in order to emit their meaty scent from -// the proper location, gibs should wait until they stop -// bouncing to emit their scent. That's what this function -// does. -//========================================================= -void CGib :: WaitTillLand ( void ) -{ - if (!IsInWorld()) - { - UTIL_Remove( this ); - return; - } - - if ( pev->velocity == g_vecZero ) - { - SetThink (&CGib::SUB_StartFadeOut); - pev->nextthink = gpGlobals->time + m_lifeTime; - - // If you bleed, you stink! - if ( m_bloodColor != DONT_BLEED ) - { - // ok, start stinkin! - CSoundEnt::InsertSound ( bits_SOUND_MEAT, pev->origin, 384, 25 ); - } - } - else - { - // wait and check again in another half second. - pev->nextthink = gpGlobals->time + 0.5; - } -} - -// -// Gib bounces on the ground or wall, sponges some blood down, too! -// -void CGib :: BounceGibTouch ( CBaseEntity *pOther ) -{ - Vector vecSpot; - TraceResult tr; - - //if ( RANDOM_LONG(0,1) ) - // return;// don't bleed everytime - - if (pev->flags & FL_ONGROUND) - { - pev->velocity = pev->velocity * 0.9; - pev->angles.x = 0; - pev->angles.z = 0; - pev->avelocity.x = 0; - pev->avelocity.z = 0; - } - else - { - if ( g_Language != LANGUAGE_GERMAN && m_cBloodDecals > 0 && m_bloodColor != DONT_BLEED ) - { - vecSpot = pev->origin + Vector ( 0 , 0 , 8 );//move up a bit, and trace down. - UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr); - - UTIL_BloodDecalTrace( &tr, m_bloodColor ); - - m_cBloodDecals--; - } - - if ( m_material != matNone && RANDOM_LONG(0,2) == 0 ) - { - float volume; - float zvel = fabs(pev->velocity.z); - - volume = 0.8 * min(1.0, ((float)zvel) / 450.0); - - CBreakable::MaterialSoundRandom( edict(), (Materials)m_material, volume ); - } - } -} - -// -// Sticky gib puts blood on the wall and stays put. -// -void CGib :: StickyGibTouch ( CBaseEntity *pOther ) -{ - Vector vecSpot; - TraceResult tr; - - SetThink ( &CBaseEntity::SUB_Remove ); - pev->nextthink = gpGlobals->time + 10; - - if ( !FClassnameIs( pOther->pev, "worldspawn" ) ) - { - pev->nextthink = gpGlobals->time; - return; - } - - UTIL_TraceLine ( pev->origin, pev->origin + pev->velocity * 32, ignore_monsters, ENT(pev), & tr); - - UTIL_BloodDecalTrace( &tr, m_bloodColor ); - - pev->velocity = tr.vecPlaneNormal * -1; - pev->angles = UTIL_VecToAngles ( pev->velocity ); - pev->velocity = g_vecZero; - pev->avelocity = g_vecZero; - pev->movetype = MOVETYPE_NONE; -} - -// -// Throw a chunk -// -void CGib :: Spawn( const char *szGibModel ) -{ - pev->movetype = MOVETYPE_BOUNCE; - pev->friction = 0.55; // deading the bounce a bit - - // sometimes an entity inherits the edict from a former piece of glass, - // and will spawn using the same render FX or rendermode! bad! - pev->renderamt = 255; - pev->rendermode = kRenderNormal; - pev->renderfx = kRenderFxNone; - pev->solid = SOLID_SLIDEBOX;/// hopefully this will fix the VELOCITY TOO LOW crap - pev->classname = MAKE_STRING("gib"); - - SET_MODEL(ENT(pev), szGibModel); - UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - - pev->nextthink = gpGlobals->time + 4; - m_lifeTime = 25; - SetThink ( &CGib::WaitTillLand ); - SetTouch ( &CGib::BounceGibTouch ); - - m_material = matNone; - m_cBloodDecals = 5;// how many blood decals this gib can place (1 per bounce until none remain). -} - -// take health -int CBaseMonster :: TakeHealth (float flHealth, int bitsDamageType) -{ - if (!pev->takedamage) - return 0; - - // clear out any damage types we healed. - // UNDONE: generic health should not heal any - // UNDONE: time-based damage - - m_bitsDamageType &= ~(bitsDamageType & ~DMG_TIMEBASED); - - return CBaseEntity::TakeHealth(flHealth, bitsDamageType); -} - -/* -============ -TakeDamage - -The damage is coming from inflictor, but get mad at attacker -This should be the only function that ever reduces health. -bitsDamageType indicates the type of damage sustained, ie: DMG_SHOCK - -Time-based damage: only occurs while the monster is within the trigger_hurt. -When a monster is poisoned via an arrow etc it takes all the poison damage at once. - - - -GLOBALS ASSUMED SET: g_iSkillLevel -============ -*/ -int CBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - float flTake; - Vector vecDir; - - if (!pev->takedamage) - return 0; - - if ( !IsAlive() ) - { - return DeadTakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); - } - - if ( pev->deadflag == DEAD_NO ) - { - // no pain sound during death animation. - PainSound();// "Ouch!" - } - - //!!!LATER - make armor consideration here! - flTake = flDamage; - - // set damage type sustained - m_bitsDamageType |= bitsDamageType; - - // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). - vecDir = Vector( 0, 0, 0 ); - if (!FNullEnt( pevInflictor )) - { - CBaseEntity *pInflictor = CBaseEntity :: Instance( pevInflictor ); - if (pInflictor) - { - vecDir = ( pInflictor->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize(); - vecDir = g_vecAttackDir = vecDir.Normalize(); - } - } - - // add to the damage total for clients, which will be sent as a single - // message at the end of the frame - // todo: remove after combining shotgun blasts? - if ( IsPlayer() ) - { - if ( pevInflictor ) - pev->dmg_inflictor = ENT(pevInflictor); - - pev->dmg_take += flTake; - - // check for godmode or invincibility - if ( pev->flags & FL_GODMODE ) - { - return 0; - } - } - - // HL: if this is a player, move him around! - // NS: Don't move players - if ( ( !FNullEnt( pevInflictor ) ) && (pev->movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER) && !IsPlayer()) - { - pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); - } - - // do the damage - pev->health -= flTake; - - - // HACKHACK Don't kill monsters in a script. Let them break their scripts first - if ( m_MonsterState == MONSTERSTATE_SCRIPT ) - { - SetConditions( bits_COND_LIGHT_DAMAGE ); - return 0; - } - - if ( (int)(pev->health) <= 0 ) - { - g_pevLastInflictor = pevInflictor; - -// Removed gibbing, as death animations weren't playing with gibs off -// if ( bitsDamageType & DMG_ALWAYSGIB ) -// { -// Killed( pevAttacker, GIB_ALWAYS ); -// } -// else if ( bitsDamageType & DMG_NEVERGIB ) -// { - Killed( pevAttacker, GIB_NEVER ); -// } -// else -// { -// Killed( pevAttacker, GIB_NORMAL ); -// } - - // Trigger log message if needed -// AvHPlayer* theDeadPlayer = dynamic_cast(this); -// AvHPlayer* theAttackingPlayer = dynamic_cast(CBaseEntity::Instance(ENT(pevAttacker))); -// const char* inWeaponName = STRING(pevInflictor->classname); -// if(theDeadPlayer && theAttackingPlayer && inWeaponName) -// { -// theDeadPlayer->LogPlayerKilledPlayer(theAttackingPlayer, inWeaponName); -// } - - g_pevLastInflictor = NULL; - - return 0; - } - - // react to the damage (get mad) - if ( (pev->flags & FL_MONSTER) && !FNullEnt(pevAttacker) ) - { - if ( pevAttacker->flags & (FL_MONSTER | FL_CLIENT) ) - {// only if the attack was a monster or client! - - // enemy's last known position is somewhere down the vector that the attack came from. - if (pevInflictor) - { - if (m_hEnemy == NULL || pevInflictor == m_hEnemy->pev || !HasConditions(bits_COND_SEE_ENEMY)) - { - m_vecEnemyLKP = pevInflictor->origin; - } - } - else - { - m_vecEnemyLKP = pev->origin + ( g_vecAttackDir * 64 ); - } - - MakeIdealYaw( m_vecEnemyLKP ); - - // add pain to the conditions - // !!!HACKHACK - fudged for now. Do we want to have a virtual function to determine what is light and - // heavy damage per monster class? - if ( flDamage > 0 ) - { - SetConditions(bits_COND_LIGHT_DAMAGE); - } - - if ( flDamage >= 20 ) - { - SetConditions(bits_COND_HEAVY_DAMAGE); - } - } - } - - return 1; -} - -//========================================================= -// DeadTakeDamage - takedamage function called when a monster's -// corpse is damaged. -//========================================================= -int CBaseMonster :: DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - Vector vecDir; - - // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). - vecDir = Vector( 0, 0, 0 ); - if (!FNullEnt( pevInflictor )) - { - CBaseEntity *pInflictor = CBaseEntity :: Instance( pevInflictor ); - if (pInflictor) - { - vecDir = ( pInflictor->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize(); - vecDir = g_vecAttackDir = vecDir.Normalize(); - } - } - -#if 0// turn this back on when the bounding box issues are resolved. - - pev->flags &= ~FL_ONGROUND; - pev->origin.z += 1; - - // let the damage scoot the corpse around a bit. - if ( !FNullEnt(pevInflictor) && (pevAttacker->solid != SOLID_TRIGGER) ) - { - pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); - } - -#endif - - // kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse. - if ( bitsDamageType & DMG_GIB_CORPSE ) - { - if ( pev->health <= flDamage ) - { - pev->health = -50; - Killed( pevAttacker, GIB_ALWAYS ); - return 0; - } - // Accumulate corpse gibbing damage, so you can gib with multiple hits - pev->health -= flDamage * 0.1; - } - - return 1; -} - - -float CBaseMonster :: DamageForce( float damage ) -{ - float force = damage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5; - - if ( force > 1000.0) - { - force = 1000.0; - } - - return force; -} - -// -// RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range. -// -// only damage ents that can clearly be seen by the explosion! - - -void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float inDamage, float flRadius, int iClassIgnore, int bitsDamageType ) -{ - CBaseEntity *pEntity = NULL; - TraceResult tr; - float flAdjustedDamage, falloff; - Vector vecSpot; - - if ( flRadius ) - falloff = inDamage / flRadius; - else - falloff = 1.0; - - int bInWater = (UTIL_PointContents ( vecSrc ) == CONTENTS_WATER); - - vecSrc.z += 1;// in case grenade is lying on the ground - - if ( !pevAttacker ) - pevAttacker = pevInflictor; - - // iterate on all entities in the vicinity. - while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL) - { - // NOTE: Should this be inflictor or attacker? - CBaseEntity* theInflictingEntity = CBaseEntity::Instance(pevInflictor); - CBaseEntity* theAttackingEntity = CBaseEntity::Instance(pevAttacker); - float theScalar = 1.0f; - bool aCanDamage=GetGameRules()->CanEntityDoDamageTo(theAttackingEntity, pEntity, &theScalar) || theInflictingEntity->pev->classname == MAKE_STRING(kwsDeployedMine);; - bool iCanDamage=GetGameRules()->CanEntityDoDamageTo(theInflictingEntity, pEntity, &theScalar); - - if(pEntity && ( aCanDamage && iCanDamage )) - { - // Multiply damage by scalar for tourny mode, etc. - float theDamage = inDamage*theScalar; - - if ( pEntity->pev->takedamage != DAMAGE_NO ) - { - - // UNDONE: this should check a damage mask, not an ignore - if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore ) - {// houndeyes don't hurt other houndeyes with their attack - continue; - } - - // blast's don't tavel into or out of water - if (bInWater && pEntity->pev->waterlevel == 0) - continue; - if (!bInWater && pEntity->pev->waterlevel == 3) - continue; - - vecSpot = pEntity->BodyTarget( vecSrc ); - - // Clear pevInflictor's owner temporarily so it can apply damage to it - edict_t* theInflictorOwner = pevInflictor->owner; - pevInflictor->owner = NULL; - - UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr ); - - // Restore owner - pevInflictor->owner = theInflictorOwner; - - if (tr.flFraction == 1.0 || tr.pHit == pEntity->edict() ) - {// the explosion can 'see' this entity, so hurt them! - if (tr.fStartSolid) - { - // if we're stuck inside them, fixup the position and distance - tr.vecEndPos = vecSrc; - tr.flFraction = 0.0; - } - - // decrease damage for an ent that's farther from the bomb. - flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff; - flAdjustedDamage = theDamage - flAdjustedDamage; - - if ( flAdjustedDamage > 0 ) - { - pEntity->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); - } - } - } - } - } -} - - -void CBaseMonster :: RadiusDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) -{ - ::RadiusDamage( pev->origin, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType ); -} - - -void CBaseMonster :: RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) -{ - ::RadiusDamage( vecSrc, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType ); -} - -int CBaseMonster::GetHull() const -{ - return head_hull; -} - -//========================================================= -// CheckTraceHullAttack - expects a length to trace, amount -// of damage to do, and damage type. Returns a pointer to -// the damaged entity in case the monster wishes to do -// other stuff to the victim (punchangle, etc) -// -// Used for many contact-range melee attacks. Bites, claws, etc. -//========================================================= -CBaseEntity* CBaseMonster :: CheckTraceHullAttack( float flDist, float& ioDamage, int iDmgType ) -{ - Vector vecStart = pev->origin; - - if(this->IsPlayer()) - { - // Melee attacks always originate where the view is - vecStart.x += this->pev->view_ofs.x; - vecStart.y += this->pev->view_ofs.y; - vecStart.z += this->pev->view_ofs.z; - } - - if (IsPlayer()) - UTIL_MakeVectors( pev->v_angle ); - else - UTIL_MakeAimVectors( pev->angles ); - - //AvHSUPlayParticleEvent("JetpackEffect", this->edict(), vecStart); - - // First do a tracehull. If that misses, try three tracelines (dead-on center, then randomly left and randomly right). - bool theHitTarget = false; - for(int i = 0; (i < 4) && !theHitTarget; i++) - { - const float kAmount = 0.4f; - float theXAmount = 0.0f; - - if(i == 2) - { - theXAmount = kAmount; - } - else if(i == 3) - { - theXAmount = -kAmount; - } - - Vector vecDir = gpGlobals->v_forward + theXAmount*gpGlobals->v_right; - vecDir.Normalize(); - - Vector vecEnd = vecStart + (vecDir * flDist ); - TraceResult tr; - - if(i == 0) - { - int theOurHull = this->GetHull(); - int theHull = AvHSUGetValveHull(theOurHull); - UTIL_TraceHull(vecStart, vecEnd, dont_ignore_monsters, theHull, this->edict(), &tr); - } - else - { - //AvHSUPlayParticleEvent("JetpackEffect", this->edict(), vecEnd); - - UTIL_TraceLine(vecStart, vecEnd, dont_ignore_monsters, dont_ignore_glass, ENT(this->pev), &tr); - } - - if ( tr.pHit ) - { - CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); - - if ( ioDamage > 0 ) - { - float theScalar = 1.0f; - if(GetGameRules()->CanEntityDoDamageTo(this, pEntity, &theScalar)) - { - theHitTarget = true; - - // Multiply damage by scalar for tourny mode, etc. - ioDamage *= theScalar; - - entvars_t* theInflictor = this->pev; - AvHPlayer* thePlayer = dynamic_cast(this); - if(thePlayer) - { - AvHBasePlayerWeapon* theBasePlayerWeapon = dynamic_cast(thePlayer->m_pActiveItem); - if(theBasePlayerWeapon) - { - theInflictor = theBasePlayerWeapon->pev; - } - } - - pEntity->TakeDamage(theInflictor, pev, ioDamage, iDmgType ); - - // Spawn blood - if(ioDamage > 0.0f) - { - // a little surface blood. - SpawnBlood(tr.vecEndPos, pEntity->BloodColor(), ioDamage); - - // on the wall/floor (don't play because blood decal is green) - //TraceBleed(ioDamage, vecDir, &tr, iDmgType); - } - - return pEntity; - } - } - } - } - - return NULL; -} - - -//========================================================= -// FInViewCone - returns true is the passed ent is in -// the caller's forward view cone. The dot product is performed -// in 2d, making the view cone infinitely tall. -//========================================================= -BOOL CBaseMonster :: FInViewCone ( CBaseEntity *pEntity ) -{ - Vector2D vec2LOS; - float flDot; - - UTIL_MakeVectors ( pev->angles ); - - vec2LOS = ( pEntity->pev->origin - pev->origin ).Make2D(); - vec2LOS = vec2LOS.Normalize(); - - flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); - - if ( flDot > m_flFieldOfView ) - { - return TRUE; - } - else - { - return FALSE; - } -} - -//========================================================= -// FInViewCone - returns true is the passed vector is in -// the caller's forward view cone. The dot product is performed -// in 2d, making the view cone infinitely tall. -//========================================================= -BOOL CBaseMonster :: FInViewCone ( Vector *pOrigin ) -{ - Vector2D vec2LOS; - float flDot; - - UTIL_MakeVectors ( pev->angles ); - - vec2LOS = ( *pOrigin - pev->origin ).Make2D(); - vec2LOS = vec2LOS.Normalize(); - - flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); - - if ( flDot > m_flFieldOfView ) - { - return TRUE; - } - else - { - return FALSE; - } -} - -//========================================================= -// FVisible - returns true if a line can be traced from -// the caller's eyes to the target -//========================================================= -BOOL CBaseEntity :: FVisible ( CBaseEntity *pEntity ) -{ - TraceResult tr; - Vector vecLookerOrigin; - Vector vecTargetOrigin; - - if (FBitSet( pEntity->pev->flags, FL_NOTARGET )) - return FALSE; - - // don't look through water //edit by Elven Thief to prevent bug #728 - // if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) - // || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0)) - // return FALSE; - - vecLookerOrigin = pev->origin + pev->view_ofs;//look through the caller's 'eyes' - vecTargetOrigin = pEntity->EyePosition(); - - return AvHCheckLineOfSight(vecLookerOrigin, vecTargetOrigin, ENT(pev)); - -} - -//========================================================= -// FVisible - returns true if a line can be traced from -// the caller's eyes to the target vector -//========================================================= -BOOL CBaseEntity :: FVisible ( const Vector &vecOrigin ) -{ - TraceResult tr; - Vector vecLookerOrigin; - - vecLookerOrigin = EyePosition();//look through the caller's 'eyes' - - return AvHCheckLineOfSight(vecLookerOrigin, vecOrigin, ENT(pev)); - -} - -/* -================ -TraceAttack -================ -*/ -void CBaseEntity::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - Vector vecOrigin = ptr->vecEndPos - vecDir * 4; - - if ( pev->takedamage ) - { - AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); - - int blood = BloodColor(); - - if ( blood != DONT_BLEED ) - { - SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood. - TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); - } - } -} - - -/* -//========================================================= -// TraceAttack -//========================================================= -void CBaseMonster::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - Vector vecOrigin = ptr->vecEndPos - vecDir * 4; - - ALERT ( at_console, "%d\n", ptr->iHitgroup ); - - - if ( pev->takedamage ) - { - AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); - - int blood = BloodColor(); - - if ( blood != DONT_BLEED ) - { - SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood. - } - } -} -*/ - -//========================================================= -// TraceAttack -//========================================================= -void CBaseMonster :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if ( pev->takedamage ) - { - m_LastHitGroup = ptr->iHitgroup; - - switch ( ptr->iHitgroup ) - { - case HITGROUP_GENERIC: - break; - case HITGROUP_HEAD: - flDamage *= gSkillData.monHead; - break; - case HITGROUP_CHEST: - flDamage *= gSkillData.monChest; - break; - case HITGROUP_STOMACH: - flDamage *= gSkillData.monStomach; - break; - case HITGROUP_LEFTARM: - case HITGROUP_RIGHTARM: - flDamage *= gSkillData.monArm; - break; - case HITGROUP_LEFTLEG: - case HITGROUP_RIGHTLEG: - flDamage *= gSkillData.monLeg; - break; - default: - break; - } - - SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. - TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); - AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); - } -} - -/* -================ -FireBullets - -Go to the trouble of combining multiple pellets into a single damage call. - -This version is used by Monsters. -================ -*/ -void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int inDamageType) -{ - static int tracerCount; - int tracer; - TraceResult tr; - Vector vecRight = gpGlobals->v_right; - Vector vecUp = gpGlobals->v_up; - - if ( pevAttacker == NULL ) - pevAttacker = pev; // the default attacker is ourselves - - ClearMultiDamage(); - gMultiDamage.type = inDamageType; - - for (ULONG iShot = 1; iShot <= cShots; iShot++) - { - // get circular gaussian spread - float x, y, z; - do { - x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); - y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); - z = x*x+y*y; - } while (z > 1); - - Vector vecDir = vecDirShooting + - x * vecSpread.x * vecRight + - y * vecSpread.y * vecUp; - Vector vecEnd; - - vecEnd = vecSrc + vecDir * flDistance; - //UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); - bool theProtected = false; - AvHSUServerTraceBullets(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev), tr, theProtected); - CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); - - if(theProtected && theEntityHit) - { - // joev: experiment - EMIT_SOUND(theEntityHit->edict(), CHAN_AUTO, kUmbraBlockedSound, 1.0f, ATTN_NORM); - // :joev - } - else - { - tracer = 0; - if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0) - { - Vector vecTracerSrc; - - if ( IsPlayer() ) - {// adjust tracer position for player - vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16; - } - else - { - vecTracerSrc = vecSrc; - } - - if ( iTracerFreq != 1 ) // guns that always trace also always decal - tracer = 1; - switch( iBulletType ) - { - case BULLET_MONSTER_MP5: - case BULLET_MONSTER_9MM: - case BULLET_MONSTER_12MM: - default: - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); - WRITE_BYTE( TE_TRACER ); - WRITE_COORD( vecTracerSrc.x ); - WRITE_COORD( vecTracerSrc.y ); - WRITE_COORD( vecTracerSrc.z ); - WRITE_COORD( tr.vecEndPos.x ); - WRITE_COORD( tr.vecEndPos.y ); - WRITE_COORD( tr.vecEndPos.z ); - MESSAGE_END(); - break; - } - } - // do damage, paint decals - if (tr.flFraction != 1.0) - { - float theScalar = 1.0f; - if(theEntityHit && GetGameRules()->CanEntityDoDamageTo(this, theEntityHit, &theScalar)) - { - // Multiply damage by scalar for tourny mode, etc. - iDamage *= theScalar; - - if ( iDamage ) - { - theEntityHit->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); - - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - //DecalGunshot( &tr, 0 ); - } - else switch(iBulletType) - { - default: - case BULLET_MONSTER_9MM: - theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); - - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_MONSTER_MP5: - theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); - - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_MONSTER_12MM: - theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET); - if ( !tracer ) - { - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - } - break; - - case BULLET_NONE: // FIX - theEntityHit->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - // only decal glass - if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) - { - UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); - } - - break; - } - } - } - // make bullet trails - UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); - } - } - ApplyMultiDamage(pev, pevAttacker); -} - - -/* -================ -FireBullets - -Go to the trouble of combining multiple pellets into a single damage call. - -This version is used by Players, uses the random seed generator to sync client and server side shots. -================ -*/ -Vector CBaseEntity::FireBulletsPlayer ( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int shared_rand ) -{ - static int tracerCount; - TraceResult tr; - Vector vecRight = gpGlobals->v_right; - Vector vecUp = gpGlobals->v_up; - entvars_t* theInflictor = this->pev; - //float x, y, z; - - if ( pevAttacker == NULL ) - pevAttacker = pev; // the default attacker is ourselves - - int theDamageType = DMG_BULLET; - bool isShotgun=false; - AvHPlayer* thePlayer = dynamic_cast(this); - if(thePlayer && thePlayer->m_pActiveItem) - { - AvHBasePlayerWeapon* theBasePlayerWeapon = dynamic_cast(thePlayer->m_pActiveItem); - if(theBasePlayerWeapon) - { - theDamageType = theBasePlayerWeapon->GetDamageType(); - theInflictor = theBasePlayerWeapon->pev; - } - AvHSonicGun* theSonicGun = dynamic_cast(thePlayer->m_pActiveItem); - if ( theSonicGun ) - { - isShotgun=true; - } - } - else - { - AvHTurret* theTurret = dynamic_cast(this); - if(theTurret) - { - theDamageType = theTurret->GetDamageType(); - } - } - - ClearMultiDamage(); - gMultiDamage.type = theDamageType; - - for ( ULONG iShot = 1; iShot <= cShots; iShot++ ) - { - //Use player's random seed. - // get circular gaussian spread - //x = UTIL_SharedRandomFloat( shared_rand + iShot, -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 1 + iShot ) , -0.5, 0.5 ); - //y = UTIL_SharedRandomFloat( shared_rand + ( 2 + iShot ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 3 + iShot ), -0.5, 0.5 ); - //z = x * x + y * y; - // - //Vector vecDir = vecDirShooting + - // x * vecSpread.x * vecRight + - // y * vecSpread.y * vecUp; - - // tankefugl: 0000973 - // added inner cone for half of the shots - if (isShotgun && (iShot > (cShots/2))) - { - vecSpread = kSGInnerSpread; - } - // :tankefugl - - Vector vecDir = UTIL_GetRandomSpreadDir(shared_rand, iShot, vecDirShooting, vecRight, vecUp, vecSpread); - Vector vecEnd; - - vecEnd = vecSrc + vecDir * flDistance; - //UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); - bool theProtected = false; - AvHSUServerTraceBullets(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev), tr, theProtected); - CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); - - if(theProtected) - { - // joev: experiment - EMIT_SOUND(theEntityHit->edict(), CHAN_AUTO, kUmbraBlockedSound, 1.0f, ATTN_NORM); - // :joev - } - else - { - - // do damage, paint decals - if (tr.flFraction != 1.0) - { - float theScalar = 1.0f; - if(GetGameRules()->CanEntityDoDamageTo(thePlayer, theEntityHit, &theScalar)) - { - int theAdjustedDamage = iDamage*theScalar; - - if(theAdjustedDamage) - { - // tankefugl: 0000973 - // removed shotgun fallof - //if ( isShotgun && !( theEntityHit->pev->iuser3 & AVH_USER3_BREAKABLE) ) - //{ - // float distance=fabs((vecSrc - theEntityHit->pev->origin).Length()); - // if ( distance > BALANCE_VAR(kShotgunDamageRange) ) - // { - // float fallOffDistance=distance-BALANCE_VAR(kShotgunDamageRange); - // float fallOff=max(0.0, 1.0f-(fallOffDistance/(kSGRange/2))); - // theAdjustedDamage*=fallOff; - // } - //} - // :tankefugl - if ( theAdjustedDamage ) { - theEntityHit->TraceAttack(pevAttacker, theAdjustedDamage, vecDir, &tr, theDamageType | ((theAdjustedDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); - } - -// TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); -// DecalGunshot( &tr, iBulletType ); - } - } - } - // make bullet trails - UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); - } - } - ApplyMultiDamage(theInflictor, pevAttacker); - - //return Vector( (float)(x * vecSpread.x), (float)(y * vecSpread.y), 0.0f ); - return vecSpread; -} - -void CBaseEntity :: TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) -{ - if (BloodColor() == DONT_BLEED) - return; - - if (flDamage == 0) - return; - - if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB /*| DMG_MORTAR*/ ))) - return; - - // make blood decal on the wall! - TraceResult Bloodtr; - Vector vecTraceDir; - float flNoise; - int cCount; - int i; - -/* - if ( !IsAlive() ) - { - // dealing with a dead monster. - if ( pev->max_health <= 0 ) - { - // no blood decal for a monster that has already decalled its limit. - return; - } - else - { - pev->max_health--; - } - } -*/ - - if (flDamage < 10) - { - flNoise = 0.1; - cCount = 1; - } - else if (flDamage < 25) - { - flNoise = 0.2; - cCount = 2; - } - else - { - flNoise = 0.3; - cCount = 4; - } - - for ( i = 0 ; i < cCount ; i++ ) - { - vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going) - - vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); - - UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, ENT(pev), &Bloodtr); - - if ( Bloodtr.flFraction != 1.0 ) - { - UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); - } - } -} - -//========================================================= -//========================================================= -void CBaseMonster :: MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir ) -{ - // make blood decal on the wall! - TraceResult Bloodtr; - Vector vecTraceDir; - int i; - - if ( !IsAlive() ) - { - // dealing with a dead monster. - if ( pev->max_health <= 0 ) - { - // no blood decal for a monster that has already decalled its limit. - return; - } - else - { - pev->max_health--; - } - } - - for ( i = 0 ; i < cCount ; i++ ) - { - vecTraceDir = vecDir; - - vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); - - UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * 172, ignore_monsters, ENT(pev), &Bloodtr); - -/* - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE); - WRITE_COORD( ptr->vecEndPos.x ); - WRITE_COORD( ptr->vecEndPos.y ); - WRITE_COORD( ptr->vecEndPos.z ); - - WRITE_COORD( Bloodtr.vecEndPos.x ); - WRITE_COORD( Bloodtr.vecEndPos.y ); - WRITE_COORD( Bloodtr.vecEndPos.z ); - MESSAGE_END(); -*/ - - if ( Bloodtr.flFraction != 1.0 ) - { - UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); - } - } -} +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ +/* + +===== combat.cpp ======================================================== + + functions dealing with damage infliction & death + +*/ +// +// $Workfile: combat.cpp $ +// $Date: 2002/11/22 23:26:35 $ +// +//------------------------------------------------------------------------------- +// $Log: combat.cpp,v $ +// Revision 1.19 2002/11/22 23:26:35 Flayra +// - Skulks had to aim too high to hit structures, so trying this approach to fixing the melee in vents bug +// +// Revision 1.18 2002/11/22 21:09:50 Flayra +// - Explosion fixes (oops!) +// - Fix melee when in vents (I think) +// +// Revision 1.17 2002/11/13 01:49:33 Flayra +// - Proper death message logging for Psychostats +// +// Revision 1.16 2002/11/12 02:18:08 Flayra +// - Pass on inflictor properly for standard logging +// +// Revision 1.15 2002/11/06 01:36:16 Flayra +// - Allow scalar to apply to damage from gamerules (used for friendly fire) +// +// Revision 1.14 2002/10/03 18:26:36 Flayra +// - Removed all pushback and forces when doing damage to players +// +// Revision 1.13 2002/08/16 02:25:21 Flayra +// - New damage types +// +// Revision 1.12 2002/07/10 14:36:13 Flayra +// - Removed major cause of lag: redundant server-side decals. Also removed duplicate texture sound. +// +// Revision 1.11 2002/07/08 16:40:05 Flayra +// - Fix bug where melee attacks couldn't aim up or down, reworked bullet firing to add random spread (bug #236) +// +//=============================================================================== + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "soundent.h" +#include "decals.h" +#include "animation.h" +#include "weapons.h" +#include "func_break.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHTurret.h" +#include "mod/AvHServerVariables.h" +//#include "mod/AvHMovementUtil.h" + +extern DLL_GLOBAL Vector g_vecAttackDir; +extern DLL_GLOBAL int g_iSkillLevel; + +extern Vector VecBModelOrigin( entvars_t* pevBModel ); +extern entvars_t *g_pevLastInflictor; + +#define GERMAN_GIB_COUNT 4 +#define HUMAN_GIB_COUNT 6 +#define ALIEN_GIB_COUNT 4 + + +// HACKHACK -- The gib velocity equations don't work +void CGib :: LimitVelocity( void ) +{ + float length = pev->velocity.Length(); + + // ceiling at 1500. The gib velocity equation is not bounded properly. Rather than tune it + // in 3 separate places again, I'll just limit it here. + if ( length > 1500.0 ) + pev->velocity = pev->velocity.Normalize() * 1500; // This should really be sv_maxvelocity * 0.75 or something +} + + +void CGib :: SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs ) +{ + int i; + + if ( g_Language == LANGUAGE_GERMAN ) + { + // no sticky gibs in germany right now! + return; + } + + for ( i = 0 ; i < cGibs ; i++ ) + { + CGib *pGib = GetClassPtr( (CGib *)NULL ); + + pGib->Spawn( "models/stickygib.mdl" ); + pGib->pev->body = RANDOM_LONG(0,2); + + if ( pevVictim ) + { + pGib->pev->origin.x = vecOrigin.x + RANDOM_FLOAT( -3, 3 ); + pGib->pev->origin.y = vecOrigin.y + RANDOM_FLOAT( -3, 3 ); + pGib->pev->origin.z = vecOrigin.z + RANDOM_FLOAT( -3, 3 ); + + /* + pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) ); + pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) ); + pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ); + */ + + // make the gib fly away from the attack vector + pGib->pev->velocity = g_vecAttackDir * -1; + + // mix in some noise + pGib->pev->velocity.x += RANDOM_FLOAT ( -0.15, 0.15 ); + pGib->pev->velocity.y += RANDOM_FLOAT ( -0.15, 0.15 ); + pGib->pev->velocity.z += RANDOM_FLOAT ( -0.15, 0.15 ); + + pGib->pev->velocity = pGib->pev->velocity * 900; + + pGib->pev->avelocity.x = RANDOM_FLOAT ( 250, 400 ); + pGib->pev->avelocity.y = RANDOM_FLOAT ( 250, 400 ); + + // copy owner's blood color + pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); + + if ( pevVictim->health > -50) + { + pGib->pev->velocity = pGib->pev->velocity * 0.7; + } + else if ( pevVictim->health > -200) + { + pGib->pev->velocity = pGib->pev->velocity * 2; + } + else + { + pGib->pev->velocity = pGib->pev->velocity * 4; + } + + + pGib->pev->movetype = MOVETYPE_TOSS; + pGib->pev->solid = SOLID_BBOX; + UTIL_SetSize ( pGib->pev, Vector ( 0, 0 ,0 ), Vector ( 0, 0, 0 ) ); + pGib->SetTouch ( &CGib::StickyGibTouch ); + pGib->SetThink (NULL); + } + pGib->LimitVelocity(); + } +} + +void CGib :: SpawnHeadGib( entvars_t *pevVictim ) +{ + CGib *pGib = GetClassPtr( (CGib *)NULL ); + + if ( g_Language == LANGUAGE_GERMAN ) + { + pGib->Spawn( "models/germangibs.mdl" );// throw one head + pGib->pev->body = 0; + } + else + { + pGib->Spawn( "models/hgibs.mdl" );// throw one head + pGib->pev->body = 0; + } + + if ( pevVictim ) + { + pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs; + + edict_t *pentPlayer = FIND_CLIENT_IN_PVS( pGib->edict() ); + + if ( RANDOM_LONG ( 0, 100 ) <= 5 && pentPlayer ) + { + // 5% chance head will be thrown at player's face. + entvars_t *pevPlayer; + + pevPlayer = VARS( pentPlayer ); + pGib->pev->velocity = ( ( pevPlayer->origin + pevPlayer->view_ofs ) - pGib->pev->origin ).Normalize() * 300; + pGib->pev->velocity.z += 100; + } + else + { + pGib->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); + } + + + pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); + pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); + + // copy owner's blood color + pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); + + if ( pevVictim->health > -50) + { + pGib->pev->velocity = pGib->pev->velocity * 0.7; + } + else if ( pevVictim->health > -200) + { + pGib->pev->velocity = pGib->pev->velocity * 2; + } + else + { + pGib->pev->velocity = pGib->pev->velocity * 4; + } + } + pGib->LimitVelocity(); +} + +void CGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ) +{ + int cSplat; + + for ( cSplat = 0 ; cSplat < cGibs ; cSplat++ ) + { + CGib *pGib = GetClassPtr( (CGib *)NULL ); + + if ( g_Language == LANGUAGE_GERMAN ) + { + pGib->Spawn( "models/germangibs.mdl" ); + pGib->pev->body = RANDOM_LONG(0,GERMAN_GIB_COUNT-1); + } + else + { + if ( human ) + { + // human pieces + pGib->Spawn( "models/hgibs.mdl" ); + pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib) + } + else + { + // aliens + pGib->Spawn( "models/agibs.mdl" ); + pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1); + } + } + + if ( pevVictim ) + { + // spawn the gib somewhere in the monster's bounding volume + pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) ); + pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) ); + pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ) + 1; // absmin.z is in the floor because the engine subtracts 1 to enlarge the box + + // make the gib fly away from the attack vector + pGib->pev->velocity = g_vecAttackDir * -1; + + // mix in some noise + pGib->pev->velocity.x += RANDOM_FLOAT ( -0.25, 0.25 ); + pGib->pev->velocity.y += RANDOM_FLOAT ( -0.25, 0.25 ); + pGib->pev->velocity.z += RANDOM_FLOAT ( -0.25, 0.25 ); + + pGib->pev->velocity = pGib->pev->velocity * RANDOM_FLOAT ( 300, 400 ); + + pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); + pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); + + // copy owner's blood color + pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor(); + + if ( pevVictim->health > -50) + { + pGib->pev->velocity = pGib->pev->velocity * 0.7; + } + else if ( pevVictim->health > -200) + { + pGib->pev->velocity = pGib->pev->velocity * 2; + } + else + { + pGib->pev->velocity = pGib->pev->velocity * 4; + } + + pGib->pev->solid = SOLID_BBOX; + UTIL_SetSize ( pGib->pev, Vector( 0 , 0 , 0 ), Vector ( 0, 0, 0 ) ); + } + pGib->LimitVelocity(); + } +} + + +BOOL CBaseMonster :: HasHumanGibs( void ) +{ + int myClass = Classify(); + + if ( myClass == CLASS_HUMAN_MILITARY || + myClass == CLASS_PLAYER_ALLY || + myClass == CLASS_HUMAN_PASSIVE || + myClass == CLASS_PLAYER ) + + return TRUE; + + return FALSE; +} + + +BOOL CBaseMonster :: HasAlienGibs( void ) +{ + int myClass = Classify(); + + if ( myClass == CLASS_ALIEN_MILITARY || + myClass == CLASS_ALIEN_MONSTER || + myClass == CLASS_ALIEN_PASSIVE || + myClass == CLASS_INSECT || + myClass == CLASS_ALIEN_PREDATOR || + myClass == CLASS_ALIEN_PREY ) + + return TRUE; + + return FALSE; +} + + +void CBaseMonster::FadeMonster( void ) +{ + StopAnimation(); + pev->velocity = g_vecZero; + pev->movetype = MOVETYPE_NONE; + pev->avelocity = g_vecZero; + pev->animtime = gpGlobals->time; + pev->effects |= EF_NOINTERP; + SUB_StartFadeOut(); +} + +//========================================================= +// GibMonster - create some gore and get rid of a monster's +// model. +//========================================================= +void CBaseMonster :: GibMonster( void ) +{ + TraceResult tr; + BOOL gibbed = FALSE; + + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM); + + // only humans throw skulls !!!UNDONE - eventually monsters will have their own sets of gibs + if ( HasHumanGibs() ) + { + if ( ns_cvar_float(violence_hgibs) != 0 ) // Only the player will ever get here + { + CGib::SpawnHeadGib( pev ); + CGib::SpawnRandomGibs( pev, 4, 1 ); // throw some human gibs. + } + gibbed = TRUE; + } + else if ( HasAlienGibs() ) + { + if ( ns_cvar_float(violence_hgibs) != 0 ) // Should never get here, but someone might call it directly + { + CGib::SpawnRandomGibs( pev, 4, 0 ); // Throw alien gibs + } + gibbed = TRUE; + } + + if ( !IsPlayer() ) + { + if ( gibbed ) + { + // don't remove players! + SetThink ( &CBaseMonster::SUB_Remove ); + pev->nextthink = gpGlobals->time; + } + else + { + FadeMonster(); + } + } +} + +//========================================================= +// GetDeathActivity - determines the best type of death +// anim to play. +//========================================================= +Activity CBaseMonster :: GetDeathActivity ( void ) +{ + Activity deathActivity; + BOOL fTriedDirection; + float flDot; + TraceResult tr; + Vector vecSrc; + + if ( pev->deadflag != DEAD_NO ) + { + // don't run this while dying. + return m_IdealActivity; + } + + vecSrc = Center(); + + fTriedDirection = FALSE; + deathActivity = ACT_DIESIMPLE;// in case we can't find any special deaths to do. + + UTIL_MakeVectors ( pev->angles ); + flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 ); + + switch ( m_LastHitGroup ) + { + // try to pick a region-specific death. + case HITGROUP_HEAD: + deathActivity = ACT_DIE_HEADSHOT; + break; + + case HITGROUP_STOMACH: + deathActivity = ACT_DIE_GUTSHOT; + break; + + case HITGROUP_GENERIC: + // try to pick a death based on attack direction + fTriedDirection = TRUE; + + if ( flDot > 0.3 ) + { + deathActivity = ACT_DIEFORWARD; + } + else if ( flDot <= -0.3 ) + { + deathActivity = ACT_DIEBACKWARD; + } + break; + + default: + // try to pick a death based on attack direction + fTriedDirection = TRUE; + + if ( flDot > 0.3 ) + { + deathActivity = ACT_DIEFORWARD; + } + else if ( flDot <= -0.3 ) + { + deathActivity = ACT_DIEBACKWARD; + } + break; + } + + + // can we perform the prescribed death? + if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) + { + // no! did we fail to perform a directional death? + if ( fTriedDirection ) + { + // if yes, we're out of options. Go simple. + deathActivity = ACT_DIESIMPLE; + } + else + { + // cannot perform the ideal region-specific death, so try a direction. + if ( flDot > 0.3 ) + { + deathActivity = ACT_DIEFORWARD; + } + else if ( flDot <= -0.3 ) + { + deathActivity = ACT_DIEBACKWARD; + } + } + } + + if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) + { + // if we're still invalid, simple is our only option. + deathActivity = ACT_DIESIMPLE; + } + + if ( deathActivity == ACT_DIEFORWARD ) + { + // make sure there's room to fall forward + UTIL_TraceHull ( vecSrc, vecSrc + gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr ); + + if ( tr.flFraction != 1.0 ) + { + deathActivity = ACT_DIESIMPLE; + } + } + + if ( deathActivity == ACT_DIEBACKWARD ) + { + // make sure there's room to fall backward + UTIL_TraceHull ( vecSrc, vecSrc - gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr ); + + if ( tr.flFraction != 1.0 ) + { + deathActivity = ACT_DIESIMPLE; + } + } + + return deathActivity; +} + +//========================================================= +// GetSmallFlinchActivity - determines the best type of flinch +// anim to play. +//========================================================= +Activity CBaseMonster :: GetSmallFlinchActivity ( void ) +{ + Activity flinchActivity; + BOOL fTriedDirection; + float flDot; + + fTriedDirection = FALSE; + UTIL_MakeVectors ( pev->angles ); + flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 ); + + switch ( m_LastHitGroup ) + { + // pick a region-specific flinch + case HITGROUP_HEAD: + flinchActivity = ACT_FLINCH_HEAD; + break; + case HITGROUP_STOMACH: + flinchActivity = ACT_FLINCH_STOMACH; + break; + case HITGROUP_LEFTARM: + flinchActivity = ACT_FLINCH_LEFTARM; + break; + case HITGROUP_RIGHTARM: + flinchActivity = ACT_FLINCH_RIGHTARM; + break; + case HITGROUP_LEFTLEG: + flinchActivity = ACT_FLINCH_LEFTLEG; + break; + case HITGROUP_RIGHTLEG: + flinchActivity = ACT_FLINCH_RIGHTLEG; + break; + case HITGROUP_GENERIC: + default: + // just get a generic flinch. + flinchActivity = ACT_SMALL_FLINCH; + break; + } + + + // do we have a sequence for the ideal activity? + if ( LookupActivity ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE ) + { + flinchActivity = ACT_SMALL_FLINCH; + } + + return flinchActivity; +} + + +void CBaseMonster::BecomeDead( void ) +{ + pev->takedamage = DAMAGE_YES;// don't let autoaim aim at corpses. + + // give the corpse half of the monster's original maximum health. + pev->health = pev->max_health / 2; + pev->max_health = 5; // max_health now becomes a counter for how many blood decals the corpse can place. + + // make the corpse fly away from the attack vector + pev->movetype = MOVETYPE_TOSS; + //pev->flags &= ~FL_ONGROUND; + //pev->origin.z += 2; + //pev->velocity = g_vecAttackDir * -1; + //pev->velocity = pev->velocity * RANDOM_FLOAT( 300, 400 ); +} + + +BOOL CBaseMonster::ShouldGibMonster( int iGib ) +{ + if ( ( iGib == GIB_NORMAL && pev->health < GIB_HEALTH_VALUE ) || ( iGib == GIB_ALWAYS ) ) + return TRUE; + + return FALSE; +} + + +void CBaseMonster::CallGibMonster( void ) +{ + BOOL fade = FALSE; + + if ( HasHumanGibs() ) + { + if ( ns_cvar_float(violence_hgibs) == 0 ) + fade = TRUE; + } + else if ( HasAlienGibs() ) + { + if ( ns_cvar_float(violence_agibs) == 0 ) + fade = TRUE; + } + + pev->takedamage = DAMAGE_NO; + pev->solid = SOLID_NOT;// do something with the body. while monster blows up + + if ( fade ) + { + FadeMonster(); + } + else + { + pev->effects = EF_NODRAW; // make the model invisible. + GibMonster(); + } + + pev->deadflag = DEAD_DEAD; + FCheckAITrigger(); + + // don't let the status bar glitch for players.with <0 health. + if (pev->health < -99) + { + pev->health = 0; + } + + if ( ShouldFadeOnDeath() && !fade ) + UTIL_Remove(this); +} + + +/* +============ +Killed +============ +*/ +void CBaseMonster :: Killed( entvars_t *pevAttacker, int iGib ) +{ + unsigned int cCount = 0; + BOOL fDone = FALSE; + + CBaseEntity* theBaseEntity = CBaseEntity::Instance(pevAttacker->owner); + if(!theBaseEntity) + { + theBaseEntity = CBaseEntity::Instance(pevAttacker); + } + ASSERT(theBaseEntity); + theBaseEntity->AwardKill(this->pev); + + if ( HasMemory( bits_MEMORY_KILLED ) ) + { + if ( ShouldGibMonster( iGib ) ) + CallGibMonster(); + return; + } + + Remember( bits_MEMORY_KILLED ); + + // clear the deceased's sound channels.(may have been firing or reloading when killed) + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/null.wav", 1, ATTN_NORM); + m_IdealMonsterState = MONSTERSTATE_DEAD; + // Make sure this condition is fired too (TakeDamage breaks out before this happens on death) + SetConditions( bits_COND_LIGHT_DAMAGE ); + + // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality. + CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner); + if ( pOwner ) + { + pOwner->DeathNotice( pev ); + } + + if ( ShouldGibMonster( iGib ) ) + { + CallGibMonster(); + return; + } + else if ( pev->flags & FL_MONSTER ) + { + SetTouch( NULL ); + BecomeDead(); + } + + // don't let the status bar glitch for players.with <0 health. + if (pev->health < -99) + { + pev->health = 0; + } + + //pev->enemy = ENT( pevAttacker );//why? (sjb) + + m_IdealMonsterState = MONSTERSTATE_DEAD; + +} + +// +// fade out - slowly fades a entity out, then removes it. +// +// DON'T USE ME FOR GIBS AND STUFF IN MULTIPLAYER! +// SET A FUTURE THINK AND A RENDERMODE!! +void CBaseEntity :: SUB_StartFadeOut ( void ) +{ + if (pev->rendermode == kRenderNormal) + { + pev->renderamt = 255; + pev->rendermode = kRenderTransTexture; + } + + pev->solid = SOLID_NOT; + pev->avelocity = g_vecZero; + + pev->nextthink = gpGlobals->time + 0.1; + SetThink ( &CBaseEntity::SUB_FadeOut ); +} + +void CBaseEntity :: SUB_FadeOut ( void ) +{ + if ( pev->renderamt > 7 ) + { + pev->renderamt -= 7; + pev->nextthink = gpGlobals->time + 0.1; + } + else + { + pev->renderamt = 0; + pev->nextthink = gpGlobals->time + 0.2; + SetThink ( &CBaseEntity::SUB_Remove ); + } +} + +//========================================================= +// WaitTillLand - in order to emit their meaty scent from +// the proper location, gibs should wait until they stop +// bouncing to emit their scent. That's what this function +// does. +//========================================================= +void CGib :: WaitTillLand ( void ) +{ + if (!IsInWorld()) + { + UTIL_Remove( this ); + return; + } + + if ( pev->velocity == g_vecZero ) + { + SetThink (&CGib::SUB_StartFadeOut); + pev->nextthink = gpGlobals->time + m_lifeTime; + + // If you bleed, you stink! + if ( m_bloodColor != DONT_BLEED ) + { + // ok, start stinkin! + CSoundEnt::InsertSound ( bits_SOUND_MEAT, pev->origin, 384, 25 ); + } + } + else + { + // wait and check again in another half second. + pev->nextthink = gpGlobals->time + 0.5; + } +} + +// +// Gib bounces on the ground or wall, sponges some blood down, too! +// +void CGib :: BounceGibTouch ( CBaseEntity *pOther ) +{ + Vector vecSpot; + TraceResult tr; + + //if ( RANDOM_LONG(0,1) ) + // return;// don't bleed everytime + + if (pev->flags & FL_ONGROUND) + { + pev->velocity = pev->velocity * 0.9; + pev->angles.x = 0; + pev->angles.z = 0; + pev->avelocity.x = 0; + pev->avelocity.z = 0; + } + else + { + if ( g_Language != LANGUAGE_GERMAN && m_cBloodDecals > 0 && m_bloodColor != DONT_BLEED ) + { + vecSpot = pev->origin + Vector ( 0 , 0 , 8 );//move up a bit, and trace down. + UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr); + + UTIL_BloodDecalTrace( &tr, m_bloodColor ); + + m_cBloodDecals--; + } + + if ( m_material != matNone && RANDOM_LONG(0,2) == 0 ) + { + float volume; + float zvel = fabs(pev->velocity.z); + + volume = 0.8 * min(1.0, ((float)zvel) / 450.0); + + CBreakable::MaterialSoundRandom( edict(), (Materials)m_material, volume ); + } + } +} + +// +// Sticky gib puts blood on the wall and stays put. +// +void CGib :: StickyGibTouch ( CBaseEntity *pOther ) +{ + Vector vecSpot; + TraceResult tr; + + SetThink ( &CBaseEntity::SUB_Remove ); + pev->nextthink = gpGlobals->time + 10; + + if ( !FClassnameIs( pOther->pev, "worldspawn" ) ) + { + pev->nextthink = gpGlobals->time; + return; + } + + UTIL_TraceLine ( pev->origin, pev->origin + pev->velocity * 32, ignore_monsters, ENT(pev), & tr); + + UTIL_BloodDecalTrace( &tr, m_bloodColor ); + + pev->velocity = tr.vecPlaneNormal * -1; + pev->angles = UTIL_VecToAngles ( pev->velocity ); + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + pev->movetype = MOVETYPE_NONE; +} + +// +// Throw a chunk +// +void CGib :: Spawn( const char *szGibModel ) +{ + pev->movetype = MOVETYPE_BOUNCE; + pev->friction = 0.55; // deading the bounce a bit + + // sometimes an entity inherits the edict from a former piece of glass, + // and will spawn using the same render FX or rendermode! bad! + pev->renderamt = 255; + pev->rendermode = kRenderNormal; + pev->renderfx = kRenderFxNone; + pev->solid = SOLID_SLIDEBOX;/// hopefully this will fix the VELOCITY TOO LOW crap + pev->classname = MAKE_STRING("gib"); + + SET_MODEL(ENT(pev), szGibModel); + UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + + pev->nextthink = gpGlobals->time + 4; + m_lifeTime = 25; + SetThink ( &CGib::WaitTillLand ); + SetTouch ( &CGib::BounceGibTouch ); + + m_material = matNone; + m_cBloodDecals = 5;// how many blood decals this gib can place (1 per bounce until none remain). +} + +// take health +int CBaseMonster :: TakeHealth (float flHealth, int bitsDamageType) +{ + if (!pev->takedamage) + return 0; + + // clear out any damage types we healed. + // UNDONE: generic health should not heal any + // UNDONE: time-based damage + + m_bitsDamageType &= ~(bitsDamageType & ~DMG_TIMEBASED); + + return CBaseEntity::TakeHealth(flHealth, bitsDamageType); +} + +/* +============ +TakeDamage + +The damage is coming from inflictor, but get mad at attacker +This should be the only function that ever reduces health. +bitsDamageType indicates the type of damage sustained, ie: DMG_SHOCK + +Time-based damage: only occurs while the monster is within the trigger_hurt. +When a monster is poisoned via an arrow etc it takes all the poison damage at once. + + + +GLOBALS ASSUMED SET: g_iSkillLevel +============ +*/ +int CBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + float flTake; + Vector vecDir; + + if (!pev->takedamage) + return 0; + + if ( !IsAlive() ) + { + return DeadTakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); + } + + if ( pev->deadflag == DEAD_NO ) + { + // no pain sound during death animation. + PainSound();// "Ouch!" + } + + //!!!LATER - make armor consideration here! + flTake = flDamage; + + // set damage type sustained + m_bitsDamageType |= bitsDamageType; + + // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). + vecDir = Vector( 0, 0, 0 ); + if (!FNullEnt( pevInflictor )) + { + CBaseEntity *pInflictor = CBaseEntity :: Instance( pevInflictor ); + if (pInflictor) + { + vecDir = ( pInflictor->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize(); + vecDir = g_vecAttackDir = vecDir.Normalize(); + } + } + + // add to the damage total for clients, which will be sent as a single + // message at the end of the frame + // todo: remove after combining shotgun blasts? + if ( IsPlayer() ) + { + if ( pevInflictor ) + pev->dmg_inflictor = ENT(pevInflictor); + + pev->dmg_take += flTake; + + // check for godmode or invincibility + if ( pev->flags & FL_GODMODE ) + { + return 0; + } + } + + // HL: if this is a player, move him around! + // NS: Don't move players + if ( ( !FNullEnt( pevInflictor ) ) && (pev->movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER) && !IsPlayer()) + { + pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); + } + + // do the damage + pev->health -= flTake; + + + // HACKHACK Don't kill monsters in a script. Let them break their scripts first + if ( m_MonsterState == MONSTERSTATE_SCRIPT ) + { + SetConditions( bits_COND_LIGHT_DAMAGE ); + return 0; + } + + if ( (int)(pev->health) <= 0 ) + { + g_pevLastInflictor = pevInflictor; + +// Removed gibbing, as death animations weren't playing with gibs off +// if ( bitsDamageType & DMG_ALWAYSGIB ) +// { +// Killed( pevAttacker, GIB_ALWAYS ); +// } +// else if ( bitsDamageType & DMG_NEVERGIB ) +// { + Killed( pevAttacker, GIB_NEVER ); +// } +// else +// { +// Killed( pevAttacker, GIB_NORMAL ); +// } + + // Trigger log message if needed +// AvHPlayer* theDeadPlayer = dynamic_cast(this); +// AvHPlayer* theAttackingPlayer = dynamic_cast(CBaseEntity::Instance(ENT(pevAttacker))); +// const char* inWeaponName = STRING(pevInflictor->classname); +// if(theDeadPlayer && theAttackingPlayer && inWeaponName) +// { +// theDeadPlayer->LogPlayerKilledPlayer(theAttackingPlayer, inWeaponName); +// } + + g_pevLastInflictor = NULL; + + return 0; + } + + // react to the damage (get mad) + if ( (pev->flags & FL_MONSTER) && !FNullEnt(pevAttacker) ) + { + if ( pevAttacker->flags & (FL_MONSTER | FL_CLIENT) ) + {// only if the attack was a monster or client! + + // enemy's last known position is somewhere down the vector that the attack came from. + if (pevInflictor) + { + if (m_hEnemy == NULL || pevInflictor == m_hEnemy->pev || !HasConditions(bits_COND_SEE_ENEMY)) + { + m_vecEnemyLKP = pevInflictor->origin; + } + } + else + { + m_vecEnemyLKP = pev->origin + ( g_vecAttackDir * 64 ); + } + + MakeIdealYaw( m_vecEnemyLKP ); + + // add pain to the conditions + // !!!HACKHACK - fudged for now. Do we want to have a virtual function to determine what is light and + // heavy damage per monster class? + if ( flDamage > 0 ) + { + SetConditions(bits_COND_LIGHT_DAMAGE); + } + + if ( flDamage >= 20 ) + { + SetConditions(bits_COND_HEAVY_DAMAGE); + } + } + } + + return 1; +} + +//========================================================= +// DeadTakeDamage - takedamage function called when a monster's +// corpse is damaged. +//========================================================= +int CBaseMonster :: DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + Vector vecDir; + + // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). + vecDir = Vector( 0, 0, 0 ); + if (!FNullEnt( pevInflictor )) + { + CBaseEntity *pInflictor = CBaseEntity :: Instance( pevInflictor ); + if (pInflictor) + { + vecDir = ( pInflictor->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize(); + vecDir = g_vecAttackDir = vecDir.Normalize(); + } + } + +#if 0// turn this back on when the bounding box issues are resolved. + + pev->flags &= ~FL_ONGROUND; + pev->origin.z += 1; + + // let the damage scoot the corpse around a bit. + if ( !FNullEnt(pevInflictor) && (pevAttacker->solid != SOLID_TRIGGER) ) + { + pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); + } + +#endif + + // kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse. + if ( bitsDamageType & DMG_GIB_CORPSE ) + { + if ( pev->health <= flDamage ) + { + pev->health = -50; + Killed( pevAttacker, GIB_ALWAYS ); + return 0; + } + // Accumulate corpse gibbing damage, so you can gib with multiple hits + pev->health -= flDamage * 0.1; + } + + return 1; +} + + +float CBaseMonster :: DamageForce( float damage ) +{ + float force = damage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5; + + if ( force > 1000.0) + { + force = 1000.0; + } + + return force; +} + +// +// RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range. +// +// only damage ents that can clearly be seen by the explosion! + + +void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float inDamage, float flRadius, int iClassIgnore, int bitsDamageType ) +{ + CBaseEntity *pEntity = NULL; + TraceResult tr; + float flAdjustedDamage, falloff; + Vector vecSpot; + + if ( flRadius ) + falloff = inDamage / flRadius; + else + falloff = 1.0; + + int bInWater = (UTIL_PointContents ( vecSrc ) == CONTENTS_WATER); + + vecSrc.z += 1;// in case grenade is lying on the ground + + if ( !pevAttacker ) + pevAttacker = pevInflictor; + + // iterate on all entities in the vicinity. + while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL) + { + // NOTE: Should this be inflictor or attacker? + CBaseEntity* theInflictingEntity = CBaseEntity::Instance(pevInflictor); + CBaseEntity* theAttackingEntity = CBaseEntity::Instance(pevAttacker); + float theScalar = 1.0f; + bool aCanDamage=GetGameRules()->CanEntityDoDamageTo(theAttackingEntity, pEntity, &theScalar) || theInflictingEntity->pev->classname == MAKE_STRING(kwsDeployedMine);; + bool iCanDamage=GetGameRules()->CanEntityDoDamageTo(theInflictingEntity, pEntity, &theScalar); + + if(pEntity && ( aCanDamage && iCanDamage )) + { + // Multiply damage by scalar for tourny mode, etc. + float theDamage = inDamage*theScalar; + + if ( pEntity->pev->takedamage != DAMAGE_NO ) + { + + // UNDONE: this should check a damage mask, not an ignore + if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore ) + {// houndeyes don't hurt other houndeyes with their attack + continue; + } + + // blast's don't tavel into or out of water + if (bInWater && pEntity->pev->waterlevel == 0) + continue; + if (!bInWater && pEntity->pev->waterlevel == 3) + continue; + + vecSpot = pEntity->BodyTarget( vecSrc ); + + // Clear pevInflictor's owner temporarily so it can apply damage to it + edict_t* theInflictorOwner = pevInflictor->owner; + pevInflictor->owner = NULL; + + UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr ); + + // Restore owner + pevInflictor->owner = theInflictorOwner; + + if (tr.flFraction == 1.0 || tr.pHit == pEntity->edict() ) + {// the explosion can 'see' this entity, so hurt them! + if (tr.fStartSolid) + { + // if we're stuck inside them, fixup the position and distance + tr.vecEndPos = vecSrc; + tr.flFraction = 0.0; + } + + // decrease damage for an ent that's farther from the bomb. + flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff; + flAdjustedDamage = theDamage - flAdjustedDamage; + + if ( flAdjustedDamage > 0 ) + { + pEntity->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); + } + } + } + } + } +} + + +void CBaseMonster :: RadiusDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) +{ + ::RadiusDamage( pev->origin, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType ); +} + + +void CBaseMonster :: RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) +{ + ::RadiusDamage( vecSrc, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType ); +} + +int CBaseMonster::GetHull() const +{ + return head_hull; +} + +//========================================================= +// CheckTraceHullAttack - expects a length to trace, amount +// of damage to do, and damage type. Returns a pointer to +// the damaged entity in case the monster wishes to do +// other stuff to the victim (punchangle, etc) +// +// Used for many contact-range melee attacks. Bites, claws, etc. +//========================================================= +CBaseEntity* CBaseMonster :: CheckTraceHullAttack( float flDist, float& ioDamage, int iDmgType ) +{ + Vector vecStart = pev->origin; + + if(this->IsPlayer()) + { + // Melee attacks always originate where the view is + vecStart.x += this->pev->view_ofs.x; + vecStart.y += this->pev->view_ofs.y; + vecStart.z += this->pev->view_ofs.z; + } + + if (IsPlayer()) + UTIL_MakeVectors( pev->v_angle ); + else + UTIL_MakeAimVectors( pev->angles ); + + //AvHSUPlayParticleEvent("JetpackEffect", this->edict(), vecStart); + + // First do a tracehull. If that misses, try three tracelines (dead-on center, then randomly left and randomly right). + bool theHitTarget = false; + for(int i = 0; (i < 4) && !theHitTarget; i++) + { + const float kAmount = 0.4f; + float theXAmount = 0.0f; + + if(i == 2) + { + theXAmount = kAmount; + } + else if(i == 3) + { + theXAmount = -kAmount; + } + + Vector vecDir = gpGlobals->v_forward + theXAmount*gpGlobals->v_right; + vecDir.Normalize(); + + Vector vecEnd = vecStart + (vecDir * flDist ); + TraceResult tr; + + if(i == 0) + { + int theOurHull = this->GetHull(); + int theHull = AvHSUGetValveHull(theOurHull); + UTIL_TraceHull(vecStart, vecEnd, dont_ignore_monsters, theHull, this->edict(), &tr); + } + else + { + //AvHSUPlayParticleEvent("JetpackEffect", this->edict(), vecEnd); + + UTIL_TraceLine(vecStart, vecEnd, dont_ignore_monsters, dont_ignore_glass, ENT(this->pev), &tr); + } + + if ( tr.pHit ) + { + CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); + + if ( ioDamage > 0 ) + { + float theScalar = 1.0f; + if(GetGameRules()->CanEntityDoDamageTo(this, pEntity, &theScalar)) + { + theHitTarget = true; + + // Multiply damage by scalar for tourny mode, etc. + ioDamage *= theScalar; + + entvars_t* theInflictor = this->pev; + AvHPlayer* thePlayer = dynamic_cast(this); + if(thePlayer) + { + AvHBasePlayerWeapon* theBasePlayerWeapon = dynamic_cast(thePlayer->m_pActiveItem); + if(theBasePlayerWeapon) + { + theInflictor = theBasePlayerWeapon->pev; + } + } + + pEntity->TakeDamage(theInflictor, pev, ioDamage, iDmgType ); + + // Spawn blood + if(ioDamage > 0.0f) + { + // a little surface blood. + SpawnBlood(tr.vecEndPos, pEntity->BloodColor(), ioDamage); + + // on the wall/floor (don't play because blood decal is green) + //TraceBleed(ioDamage, vecDir, &tr, iDmgType); + } + + return pEntity; + } + } + } + } + + return NULL; +} + + +//========================================================= +// FInViewCone - returns true is the passed ent is in +// the caller's forward view cone. The dot product is performed +// in 2d, making the view cone infinitely tall. +//========================================================= +BOOL CBaseMonster :: FInViewCone ( CBaseEntity *pEntity ) +{ + Vector2D vec2LOS; + float flDot; + + UTIL_MakeVectors ( pev->angles ); + + vec2LOS = ( pEntity->pev->origin - pev->origin ).Make2D(); + vec2LOS = vec2LOS.Normalize(); + + flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); + + if ( flDot > m_flFieldOfView ) + { + return TRUE; + } + else + { + return FALSE; + } +} + +//========================================================= +// FInViewCone - returns true is the passed vector is in +// the caller's forward view cone. The dot product is performed +// in 2d, making the view cone infinitely tall. +//========================================================= +BOOL CBaseMonster :: FInViewCone ( Vector *pOrigin ) +{ + Vector2D vec2LOS; + float flDot; + + UTIL_MakeVectors ( pev->angles ); + + vec2LOS = ( *pOrigin - pev->origin ).Make2D(); + vec2LOS = vec2LOS.Normalize(); + + flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); + + if ( flDot > m_flFieldOfView ) + { + return TRUE; + } + else + { + return FALSE; + } +} + +//========================================================= +// FVisible - returns true if a line can be traced from +// the caller's eyes to the target +//========================================================= +BOOL CBaseEntity :: FVisible ( CBaseEntity *pEntity ) +{ + TraceResult tr; + Vector vecLookerOrigin; + Vector vecTargetOrigin; + + if (FBitSet( pEntity->pev->flags, FL_NOTARGET )) + return FALSE; + + // don't look through water //edit by Elven Thief to prevent bug #728 + // if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) + // || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0)) + // return FALSE; + + vecLookerOrigin = this->EyePosition();//look through the caller's 'eyes' + vecTargetOrigin = pEntity->EyePosition(); + + return AvHCheckLineOfSight(vecLookerOrigin, vecTargetOrigin, ENT(pev)); + +} + +//========================================================= +// FVisible - returns true if a line can be traced from +// the caller's eyes to the target vector +//========================================================= +BOOL CBaseEntity :: FVisible ( const Vector &vecOrigin ) +{ + TraceResult tr; + Vector vecLookerOrigin; + + vecLookerOrigin = EyePosition();//look through the caller's 'eyes' + + return AvHCheckLineOfSight(vecLookerOrigin, vecOrigin, ENT(pev)); + +} + +/* +================ +TraceAttack +================ +*/ +void CBaseEntity::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + Vector vecOrigin = ptr->vecEndPos - vecDir * 4; + + if ( pev->takedamage ) + { + AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); + + int blood = BloodColor(); + + if ( blood != DONT_BLEED ) + { + SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood. + TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); + } + } +} + + +/* +//========================================================= +// TraceAttack +//========================================================= +void CBaseMonster::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + Vector vecOrigin = ptr->vecEndPos - vecDir * 4; + + ALERT ( at_console, "%d\n", ptr->iHitgroup ); + + + if ( pev->takedamage ) + { + AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); + + int blood = BloodColor(); + + if ( blood != DONT_BLEED ) + { + SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood. + } + } +} +*/ + +//========================================================= +// TraceAttack +//========================================================= +void CBaseMonster :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if ( pev->takedamage ) + { + m_LastHitGroup = ptr->iHitgroup; + + switch ( ptr->iHitgroup ) + { + case HITGROUP_GENERIC: + break; + case HITGROUP_HEAD: + flDamage *= gSkillData.monHead; + break; + case HITGROUP_CHEST: + flDamage *= gSkillData.monChest; + break; + case HITGROUP_STOMACH: + flDamage *= gSkillData.monStomach; + break; + case HITGROUP_LEFTARM: + case HITGROUP_RIGHTARM: + flDamage *= gSkillData.monArm; + break; + case HITGROUP_LEFTLEG: + case HITGROUP_RIGHTLEG: + flDamage *= gSkillData.monLeg; + break; + default: + break; + } + + SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. + TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); + AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); + } +} + +/* +================ +FireBullets + +Go to the trouble of combining multiple pellets into a single damage call. + +This version is used by Monsters. +================ +*/ +void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int inDamageType) +{ + static int tracerCount; + int tracer; + TraceResult tr; + Vector vecRight = gpGlobals->v_right; + Vector vecUp = gpGlobals->v_up; + + if ( pevAttacker == NULL ) + pevAttacker = pev; // the default attacker is ourselves + + ClearMultiDamage(); + gMultiDamage.type = inDamageType; + + for (ULONG iShot = 1; iShot <= cShots; iShot++) + { + // get circular gaussian spread + float x, y, z; + do { + x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); + y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); + z = x*x+y*y; + } while (z > 1); + + Vector vecDir = vecDirShooting + + x * vecSpread.x * vecRight + + y * vecSpread.y * vecUp; + Vector vecEnd; + + vecEnd = vecSrc + vecDir * flDistance; + //UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); + bool theProtected = false; + AvHSUServerTraceBullets(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev), tr, theProtected); + CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); + + if(theProtected && theEntityHit) + { + // : experiment + EMIT_SOUND(theEntityHit->edict(), CHAN_AUTO, kUmbraBlockedSound, 1.0f, ATTN_NORM); + // : + } + else + { + tracer = 0; + if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0) + { + Vector vecTracerSrc; + + if ( IsPlayer() ) + {// adjust tracer position for player + vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16; + } + else + { + vecTracerSrc = vecSrc; + } + + if ( iTracerFreq != 1 ) // guns that always trace also always decal + tracer = 1; + switch( iBulletType ) + { + case BULLET_MONSTER_MP5: + case BULLET_MONSTER_9MM: + case BULLET_MONSTER_12MM: + default: + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); + WRITE_BYTE( TE_TRACER ); + WRITE_COORD( vecTracerSrc.x ); + WRITE_COORD( vecTracerSrc.y ); + WRITE_COORD( vecTracerSrc.z ); + WRITE_COORD( tr.vecEndPos.x ); + WRITE_COORD( tr.vecEndPos.y ); + WRITE_COORD( tr.vecEndPos.z ); + MESSAGE_END(); + break; + } + } + // do damage, paint decals + if (tr.flFraction != 1.0) + { + float theScalar = 1.0f; + if(theEntityHit && GetGameRules()->CanEntityDoDamageTo(this, theEntityHit, &theScalar)) + { + // Multiply damage by scalar for tourny mode, etc. + iDamage *= theScalar; + + if ( iDamage ) + { + theEntityHit->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); + + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + //DecalGunshot( &tr, 0 ); + } + else switch(iBulletType) + { + default: + case BULLET_MONSTER_9MM: + theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); + + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_MP5: + theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); + + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_12MM: + theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET); + if ( !tracer ) + { + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + } + break; + + case BULLET_NONE: // FIX + theEntityHit->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + // only decal glass + if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) + { + UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); + } + + break; + } + } + } + // make bullet trails + UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); + } + } + ApplyMultiDamage(pev, pevAttacker); +} + + +/* +================ +FireBullets + +Go to the trouble of combining multiple pellets into a single damage call. + +This version is used by Players, uses the random seed generator to sync client and server side shots. +================ +*/ +Vector CBaseEntity::FireBulletsPlayer ( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int shared_rand ) +{ + static int tracerCount; + TraceResult tr; + Vector vecRight = gpGlobals->v_right; + Vector vecUp = gpGlobals->v_up; + entvars_t* theInflictor = this->pev; + //float x, y, z; + + if ( pevAttacker == NULL ) + pevAttacker = pev; // the default attacker is ourselves + + int theDamageType = DMG_BULLET; + bool isShotgun=false; + AvHPlayer* thePlayer = dynamic_cast(this); + if(thePlayer && thePlayer->m_pActiveItem) + { + AvHBasePlayerWeapon* theBasePlayerWeapon = dynamic_cast(thePlayer->m_pActiveItem); + if(theBasePlayerWeapon) + { + theDamageType = theBasePlayerWeapon->GetDamageType(); + theInflictor = theBasePlayerWeapon->pev; + } + AvHSonicGun* theSonicGun = dynamic_cast(thePlayer->m_pActiveItem); + if ( theSonicGun ) + { + isShotgun=true; + } + } + else + { + AvHTurret* theTurret = dynamic_cast(this); + if(theTurret) + { + theDamageType = theTurret->GetDamageType(); + } + } + + ClearMultiDamage(); + gMultiDamage.type = theDamageType; + + for ( ULONG iShot = 1; iShot <= cShots; iShot++ ) + { + //Use player's random seed. + // get circular gaussian spread + //x = UTIL_SharedRandomFloat( shared_rand + iShot, -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 1 + iShot ) , -0.5, 0.5 ); + //y = UTIL_SharedRandomFloat( shared_rand + ( 2 + iShot ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 3 + iShot ), -0.5, 0.5 ); + //z = x * x + y * y; + // + //Vector vecDir = vecDirShooting + + // x * vecSpread.x * vecRight + + // y * vecSpread.y * vecUp; + + Vector vecDir; + // : 0000973 + // added inner cone for half of the shots + if (isShotgun) + { + vecSpread = kSGInnerSpread; + Vector vecMinSpread; + + if ((iShot > (cShots/3)) && (iShot < (cShots*2/3))) + { + vecSpread = kSGMidSpread; + vecMinSpread = kSGInnerSpread; + vecDir = UTIL_GetRandomSpreadDirFrom(shared_rand, iShot, vecDirShooting, vecRight, vecUp, vecSpread, vecMinSpread); + } + else + if ((iShot > (cShots*2/3))) + { + vecMinSpread = kSGMidSpread; + vecDir = UTIL_GetRandomSpreadDirFrom(shared_rand, iShot, vecDirShooting, vecRight, vecUp, vecSpread, vecMinSpread); + } + else + { + vecSpread = kSGInnerSpread; + vecDir = UTIL_GetRandomSpreadDir(shared_rand, iShot, vecDirShooting, vecRight, vecUp, vecSpread); + } + } + // : + else + { + vecDir = UTIL_GetRandomSpreadDir(shared_rand, iShot, vecDirShooting, vecRight, vecUp, vecSpread); + } + Vector vecEnd; + + vecEnd = vecSrc + vecDir * flDistance; + //UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); + bool theProtected = false; + AvHSUServerTraceBullets(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev), tr, theProtected); + CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); + + if(theProtected) + { + // : experiment + EMIT_SOUND(theEntityHit->edict(), CHAN_AUTO, kUmbraBlockedSound, 1.0f, ATTN_NORM); + // : + } + else + { + + // do damage, paint decals + if (tr.flFraction != 1.0) + { + float theScalar = 1.0f; + if(GetGameRules()->CanEntityDoDamageTo(thePlayer, theEntityHit, &theScalar)) + { + int theAdjustedDamage = iDamage*theScalar; + + if(theAdjustedDamage) + { + // : 0000973 + // removed shotgun fallof + //if ( isShotgun && !( theEntityHit->pev->iuser3 & AVH_USER3_BREAKABLE) ) + //{ + // float distance=fabs((vecSrc - theEntityHit->pev->origin).Length()); + // if ( distance > BALANCE_VAR(kShotgunDamageRange) ) + // { + // float fallOffDistance=distance-BALANCE_VAR(kShotgunDamageRange); + // float fallOff=max(0.0, 1.0f-(fallOffDistance/(kSGRange/2))); + // theAdjustedDamage*=fallOff; + // } + //} + // : + if ( theAdjustedDamage ) { + theEntityHit->TraceAttack(pevAttacker, theAdjustedDamage, vecDir, &tr, theDamageType | ((theAdjustedDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); + } + +// TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); +// DecalGunshot( &tr, iBulletType ); + } + } + } + // make bullet trails + UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); + } + } + ApplyMultiDamage(theInflictor, pevAttacker); + + //return Vector( (float)(x * vecSpread.x), (float)(y * vecSpread.y), 0.0f ); + return vecSpread; +} + +void CBaseEntity :: TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) +{ + if (BloodColor() == DONT_BLEED) + return; + + if (flDamage == 0) + return; + + if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB /*| DMG_MORTAR*/ ))) + return; + + // make blood decal on the wall! + TraceResult Bloodtr; + Vector vecTraceDir; + float flNoise; + int cCount; + int i; + +/* + if ( !IsAlive() ) + { + // dealing with a dead monster. + if ( pev->max_health <= 0 ) + { + // no blood decal for a monster that has already decalled its limit. + return; + } + else + { + pev->max_health--; + } + } +*/ + + if (flDamage < 10) + { + flNoise = 0.1; + cCount = 1; + } + else if (flDamage < 25) + { + flNoise = 0.2; + cCount = 2; + } + else + { + flNoise = 0.3; + cCount = 4; + } + + for ( i = 0 ; i < cCount ; i++ ) + { + vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going) + + vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); + + UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, ENT(pev), &Bloodtr); + + if ( Bloodtr.flFraction != 1.0 ) + { + UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); + } + } +} + +//========================================================= +//========================================================= +void CBaseMonster :: MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir ) +{ + // make blood decal on the wall! + TraceResult Bloodtr; + Vector vecTraceDir; + int i; + + if ( !IsAlive() ) + { + // dealing with a dead monster. + if ( pev->max_health <= 0 ) + { + // no blood decal for a monster that has already decalled its limit. + return; + } + else + { + pev->max_health--; + } + } + + for ( i = 0 ; i < cCount ; i++ ) + { + vecTraceDir = vecDir; + + vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); + + UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * 172, ignore_monsters, ENT(pev), &Bloodtr); + +/* + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE); + WRITE_COORD( ptr->vecEndPos.x ); + WRITE_COORD( ptr->vecEndPos.y ); + WRITE_COORD( ptr->vecEndPos.z ); + + WRITE_COORD( Bloodtr.vecEndPos.x ); + WRITE_COORD( Bloodtr.vecEndPos.y ); + WRITE_COORD( Bloodtr.vecEndPos.z ); + MESSAGE_END(); +*/ + + if ( Bloodtr.flFraction != 1.0 ) + { + UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); + } + } +} diff --git a/main/source/dlls/effects.cpp b/main/source/dlls/effects.cpp index 8352b9a..3063f5c 100644 --- a/main/source/dlls/effects.cpp +++ b/main/source/dlls/effects.cpp @@ -22,6 +22,8 @@ #include "decals.h" #include "func_break.h" #include "engine/shake.h" +#include "mod/AvHServerVariables.h" +extern cvar_t avh_killdelay; #define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired @@ -1454,7 +1456,7 @@ void CGibShooter::Spawn( void ) CGib *CGibShooter :: CreateGib ( void ) { - if ( CVAR_GET_FLOAT("violence_hgibs") == 0 ) + if ( ns_cvar_float(violence_hgibs) == 0 ) return NULL; CGib *pGib = GetClassPtr( (CGib *)NULL ); diff --git a/main/source/dlls/enginecallback.h b/main/source/dlls/enginecallback.h index c16ff29..220b3c1 100644 --- a/main/source/dlls/enginecallback.h +++ b/main/source/dlls/enginecallback.h @@ -82,6 +82,15 @@ ENGINE_FORCE_UNMODIFIED(force_exactfile, NULL, NULL, s); (*g_engfuncs.pfnPrecacheModel)(s); #endif +#ifdef AVH_SERVER +#define PRECACHE_UNMODIFIED_GENERIC(s) \ +(*g_engfuncs.pfnPrecacheGeneric)(s); \ +ENGINE_FORCE_UNMODIFIED(force_exactfile, NULL, NULL, s); +#else +#define PRECACHE_UNMODIFIED_GENERIC(s) \ +(*g_engfuncs.pfnPrecacheGeneric)(s); +#endif + #ifdef AVH_SERVER #define PRECACHE_UNMODIFIED_SOUND(s) \ (*g_engfuncs.pfnPrecacheSound)(s); \ diff --git a/main/source/dlls/game.cpp b/main/source/dlls/game.cpp index 03dda7a..1d1c007 100644 --- a/main/source/dlls/game.cpp +++ b/main/source/dlls/game.cpp @@ -110,6 +110,7 @@ cvar_t avh_team1damagepercent = {kvTeam1DamagePercent, "100", FCVAR_SERVER} cvar_t avh_team2damagepercent = {kvTeam2DamagePercent, "100", FCVAR_SERVER}; cvar_t avh_team3damagepercent = {kvTeam3DamagePercent, "100", FCVAR_SERVER}; cvar_t avh_team4damagepercent = {kvTeam4DamagePercent, "100", FCVAR_SERVER}; +cvar_t avh_structurelimit = {kvStructureLimit, "300", FCVAR_SERVER}; cvar_t avh_votecasttime = {kvVoteCastTime, "2", FCVAR_SERVER}; cvar_t avh_votedowntime = {kvVoteDownTime, "180", FCVAR_SERVER}; cvar_t avh_minvotesneeded = {kvMinVotesNeeded, "2", FCVAR_SERVER}; @@ -120,6 +121,15 @@ cvar_t avh_autoconcede = {kvAutoConcede, "4", FCVAR_SERVER}; cvar_t avh_combattime = {kvCombatTime, "10", FCVAR_SERVER}; cvar_t avh_mapvoteratio = {kvMapVoteRatio, ".6", FCVAR_SERVER}; cvar_t avh_blockscripts = {kvBlockScripts, "1", FCVAR_SERVER}; +#ifdef DEBUG + cvar_t avh_testing = {kvTesting, "0", FCVAR_SERVER}; +#endif +cvar_t *avh_cheats=0; +cvar_t *showtriggers=0; +cvar_t *violence_ablood=0; +cvar_t *violence_agibs=0; +cvar_t *violence_hblood=0; +cvar_t *violence_hgibs=0; // TODO: Remove cvar_t avh_ironman = {kvIronMan, "0", FCVAR_SERVER}; @@ -130,7 +140,6 @@ cvar_t avh_spawninvulnerabletime = {kvSpawnInvulnerableTime, "0", FCVAR_SERVER}; cvar_t avh_trainingmode = {kvTrainingMode,"0", FCVAR_SERVER }; cvar_t avh_assert = {kvAssert, "1", FCVAR_SERVER }; cvar_t avh_bulletcam = {kvBulletCam, "0", FCVAR_SERVER }; -cvar_t avh_testing = {kvTesting, "0", FCVAR_SERVER }; cvar_t avh_drawinvisible = {kvDrawInvisible, "0", FCVAR_SERVER }; cvar_t avh_serverscripts = {kvServerScripts, "0", FCVAR_SERVER}; #endif @@ -154,406 +163,6 @@ cvar_t *g_psv_gravity = NULL; cvar_t *g_psv_aim = NULL; cvar_t *g_footsteps = NULL; -//CVARS FOR SKILL LEVEL SETTINGS -// Agrunt -cvar_t sk_agrunt_health1 = {"sk_agrunt_health1","0"}; -cvar_t sk_agrunt_health2 = {"sk_agrunt_health2","0"}; -cvar_t sk_agrunt_health3 = {"sk_agrunt_health3","0"}; - -cvar_t sk_agrunt_dmg_punch1 = {"sk_agrunt_dmg_punch1","0"}; -cvar_t sk_agrunt_dmg_punch2 = {"sk_agrunt_dmg_punch2","0"}; -cvar_t sk_agrunt_dmg_punch3 = {"sk_agrunt_dmg_punch3","0"}; - -// Apache -cvar_t sk_apache_health1 = {"sk_apache_health1","0"}; -cvar_t sk_apache_health2 = {"sk_apache_health2","0"}; -cvar_t sk_apache_health3 = {"sk_apache_health3","0"}; - -// Barney -cvar_t sk_barney_health1 = {"sk_barney_health1","0"}; -cvar_t sk_barney_health2 = {"sk_barney_health2","0"}; -cvar_t sk_barney_health3 = {"sk_barney_health3","0"}; - -// Bullsquid -cvar_t sk_bullsquid_health1 = {"sk_bullsquid_health1","0"}; -cvar_t sk_bullsquid_health2 = {"sk_bullsquid_health2","0"}; -cvar_t sk_bullsquid_health3 = {"sk_bullsquid_health3","0"}; - -cvar_t sk_bullsquid_dmg_bite1 = {"sk_bullsquid_dmg_bite1","0"}; -cvar_t sk_bullsquid_dmg_bite2 = {"sk_bullsquid_dmg_bite2","0"}; -cvar_t sk_bullsquid_dmg_bite3 = {"sk_bullsquid_dmg_bite3","0"}; - -cvar_t sk_bullsquid_dmg_whip1 = {"sk_bullsquid_dmg_whip1","0"}; -cvar_t sk_bullsquid_dmg_whip2 = {"sk_bullsquid_dmg_whip2","0"}; -cvar_t sk_bullsquid_dmg_whip3 = {"sk_bullsquid_dmg_whip3","0"}; - -cvar_t sk_bullsquid_dmg_spit1 = {"sk_bullsquid_dmg_spit1","0"}; -cvar_t sk_bullsquid_dmg_spit2 = {"sk_bullsquid_dmg_spit2","0"}; -cvar_t sk_bullsquid_dmg_spit3 = {"sk_bullsquid_dmg_spit3","0"}; - - -// Big Momma -cvar_t sk_bigmomma_health_factor1 = {"sk_bigmomma_health_factor1","1.0"}; -cvar_t sk_bigmomma_health_factor2 = {"sk_bigmomma_health_factor2","1.0"}; -cvar_t sk_bigmomma_health_factor3 = {"sk_bigmomma_health_factor3","1.0"}; - -cvar_t sk_bigmomma_dmg_slash1 = {"sk_bigmomma_dmg_slash1","50"}; -cvar_t sk_bigmomma_dmg_slash2 = {"sk_bigmomma_dmg_slash2","50"}; -cvar_t sk_bigmomma_dmg_slash3 = {"sk_bigmomma_dmg_slash3","50"}; - -cvar_t sk_bigmomma_dmg_blast1 = {"sk_bigmomma_dmg_blast1","100"}; -cvar_t sk_bigmomma_dmg_blast2 = {"sk_bigmomma_dmg_blast2","100"}; -cvar_t sk_bigmomma_dmg_blast3 = {"sk_bigmomma_dmg_blast3","100"}; - -cvar_t sk_bigmomma_radius_blast1 = {"sk_bigmomma_radius_blast1","250"}; -cvar_t sk_bigmomma_radius_blast2 = {"sk_bigmomma_radius_blast2","250"}; -cvar_t sk_bigmomma_radius_blast3 = {"sk_bigmomma_radius_blast3","250"}; - -// Gargantua -cvar_t sk_gargantua_health1 = {"sk_gargantua_health1","0"}; -cvar_t sk_gargantua_health2 = {"sk_gargantua_health2","0"}; -cvar_t sk_gargantua_health3 = {"sk_gargantua_health3","0"}; - -cvar_t sk_gargantua_dmg_slash1 = {"sk_gargantua_dmg_slash1","0"}; -cvar_t sk_gargantua_dmg_slash2 = {"sk_gargantua_dmg_slash2","0"}; -cvar_t sk_gargantua_dmg_slash3 = {"sk_gargantua_dmg_slash3","0"}; - -cvar_t sk_gargantua_dmg_fire1 = {"sk_gargantua_dmg_fire1","0"}; -cvar_t sk_gargantua_dmg_fire2 = {"sk_gargantua_dmg_fire2","0"}; -cvar_t sk_gargantua_dmg_fire3 = {"sk_gargantua_dmg_fire3","0"}; - -cvar_t sk_gargantua_dmg_stomp1 = {"sk_gargantua_dmg_stomp1","0"}; -cvar_t sk_gargantua_dmg_stomp2 = {"sk_gargantua_dmg_stomp2","0"}; -cvar_t sk_gargantua_dmg_stomp3 = {"sk_gargantua_dmg_stomp3","0"}; - - -// Hassassin -cvar_t sk_hassassin_health1 = {"sk_hassassin_health1","0"}; -cvar_t sk_hassassin_health2 = {"sk_hassassin_health2","0"}; -cvar_t sk_hassassin_health3 = {"sk_hassassin_health3","0"}; - - -// Headcrab -cvar_t sk_headcrab_health1 = {"sk_headcrab_health1","0"}; -cvar_t sk_headcrab_health2 = {"sk_headcrab_health2","0"}; -cvar_t sk_headcrab_health3 = {"sk_headcrab_health3","0"}; - -cvar_t sk_headcrab_dmg_bite1 = {"sk_headcrab_dmg_bite1","0"}; -cvar_t sk_headcrab_dmg_bite2 = {"sk_headcrab_dmg_bite2","0"}; -cvar_t sk_headcrab_dmg_bite3 = {"sk_headcrab_dmg_bite3","0"}; - - -// Hgrunt -cvar_t sk_hgrunt_health1 = {"sk_hgrunt_health1","0"}; -cvar_t sk_hgrunt_health2 = {"sk_hgrunt_health2","0"}; -cvar_t sk_hgrunt_health3 = {"sk_hgrunt_health3","0"}; - -cvar_t sk_hgrunt_kick1 = {"sk_hgrunt_kick1","0"}; -cvar_t sk_hgrunt_kick2 = {"sk_hgrunt_kick2","0"}; -cvar_t sk_hgrunt_kick3 = {"sk_hgrunt_kick3","0"}; - -cvar_t sk_hgrunt_pellets1 = {"sk_hgrunt_pellets1","0"}; -cvar_t sk_hgrunt_pellets2 = {"sk_hgrunt_pellets2","0"}; -cvar_t sk_hgrunt_pellets3 = {"sk_hgrunt_pellets3","0"}; - -cvar_t sk_hgrunt_gspeed1 = {"sk_hgrunt_gspeed1","0"}; -cvar_t sk_hgrunt_gspeed2 = {"sk_hgrunt_gspeed2","0"}; -cvar_t sk_hgrunt_gspeed3 = {"sk_hgrunt_gspeed3","0"}; - -// Houndeye -cvar_t sk_houndeye_health1 = {"sk_houndeye_health1","0"}; -cvar_t sk_houndeye_health2 = {"sk_houndeye_health2","0"}; -cvar_t sk_houndeye_health3 = {"sk_houndeye_health3","0"}; - -cvar_t sk_houndeye_dmg_blast1 = {"sk_houndeye_dmg_blast1","0"}; -cvar_t sk_houndeye_dmg_blast2 = {"sk_houndeye_dmg_blast2","0"}; -cvar_t sk_houndeye_dmg_blast3 = {"sk_houndeye_dmg_blast3","0"}; - - -// ISlave -cvar_t sk_islave_health1 = {"sk_islave_health1","0"}; -cvar_t sk_islave_health2 = {"sk_islave_health2","0"}; -cvar_t sk_islave_health3 = {"sk_islave_health3","0"}; - -cvar_t sk_islave_dmg_claw1 = {"sk_islave_dmg_claw1","0"}; -cvar_t sk_islave_dmg_claw2 = {"sk_islave_dmg_claw2","0"}; -cvar_t sk_islave_dmg_claw3 = {"sk_islave_dmg_claw3","0"}; - -cvar_t sk_islave_dmg_clawrake1 = {"sk_islave_dmg_clawrake1","0"}; -cvar_t sk_islave_dmg_clawrake2 = {"sk_islave_dmg_clawrake2","0"}; -cvar_t sk_islave_dmg_clawrake3 = {"sk_islave_dmg_clawrake3","0"}; - -cvar_t sk_islave_dmg_zap1 = {"sk_islave_dmg_zap1","0"}; -cvar_t sk_islave_dmg_zap2 = {"sk_islave_dmg_zap2","0"}; -cvar_t sk_islave_dmg_zap3 = {"sk_islave_dmg_zap3","0"}; - - -// Icthyosaur -cvar_t sk_ichthyosaur_health1 = {"sk_ichthyosaur_health1","0"}; -cvar_t sk_ichthyosaur_health2 = {"sk_ichthyosaur_health2","0"}; -cvar_t sk_ichthyosaur_health3 = {"sk_ichthyosaur_health3","0"}; - -cvar_t sk_ichthyosaur_shake1 = {"sk_ichthyosaur_shake1","0"}; -cvar_t sk_ichthyosaur_shake2 = {"sk_ichthyosaur_shake2","0"}; -cvar_t sk_ichthyosaur_shake3 = {"sk_ichthyosaur_shake3","0"}; - - -// Leech -cvar_t sk_leech_health1 = {"sk_leech_health1","0"}; -cvar_t sk_leech_health2 = {"sk_leech_health2","0"}; -cvar_t sk_leech_health3 = {"sk_leech_health3","0"}; - -cvar_t sk_leech_dmg_bite1 = {"sk_leech_dmg_bite1","0"}; -cvar_t sk_leech_dmg_bite2 = {"sk_leech_dmg_bite2","0"}; -cvar_t sk_leech_dmg_bite3 = {"sk_leech_dmg_bite3","0"}; - -// Controller -cvar_t sk_controller_health1 = {"sk_controller_health1","0"}; -cvar_t sk_controller_health2 = {"sk_controller_health2","0"}; -cvar_t sk_controller_health3 = {"sk_controller_health3","0"}; - -cvar_t sk_controller_dmgzap1 = {"sk_controller_dmgzap1","0"}; -cvar_t sk_controller_dmgzap2 = {"sk_controller_dmgzap2","0"}; -cvar_t sk_controller_dmgzap3 = {"sk_controller_dmgzap3","0"}; - -cvar_t sk_controller_speedball1 = {"sk_controller_speedball1","0"}; -cvar_t sk_controller_speedball2 = {"sk_controller_speedball2","0"}; -cvar_t sk_controller_speedball3 = {"sk_controller_speedball3","0"}; - -cvar_t sk_controller_dmgball1 = {"sk_controller_dmgball1","0"}; -cvar_t sk_controller_dmgball2 = {"sk_controller_dmgball2","0"}; -cvar_t sk_controller_dmgball3 = {"sk_controller_dmgball3","0"}; - -// Nihilanth -cvar_t sk_nihilanth_health1 = {"sk_nihilanth_health1","0"}; -cvar_t sk_nihilanth_health2 = {"sk_nihilanth_health2","0"}; -cvar_t sk_nihilanth_health3 = {"sk_nihilanth_health3","0"}; - -cvar_t sk_nihilanth_zap1 = {"sk_nihilanth_zap1","0"}; -cvar_t sk_nihilanth_zap2 = {"sk_nihilanth_zap2","0"}; -cvar_t sk_nihilanth_zap3 = {"sk_nihilanth_zap3","0"}; - -// Scientist -cvar_t sk_scientist_health1 = {"sk_scientist_health1","0"}; -cvar_t sk_scientist_health2 = {"sk_scientist_health2","0"}; -cvar_t sk_scientist_health3 = {"sk_scientist_health3","0"}; - - -// Snark -cvar_t sk_snark_health1 = {"sk_snark_health1","0"}; -cvar_t sk_snark_health2 = {"sk_snark_health2","0"}; -cvar_t sk_snark_health3 = {"sk_snark_health3","0"}; - -cvar_t sk_snark_dmg_bite1 = {"sk_snark_dmg_bite1","0"}; -cvar_t sk_snark_dmg_bite2 = {"sk_snark_dmg_bite2","0"}; -cvar_t sk_snark_dmg_bite3 = {"sk_snark_dmg_bite3","0"}; - -cvar_t sk_snark_dmg_pop1 = {"sk_snark_dmg_pop1","0"}; -cvar_t sk_snark_dmg_pop2 = {"sk_snark_dmg_pop2","0"}; -cvar_t sk_snark_dmg_pop3 = {"sk_snark_dmg_pop3","0"}; - - - -// Zombie -cvar_t sk_zombie_health1 = {"sk_zombie_health1","0"}; -cvar_t sk_zombie_health2 = {"sk_zombie_health2","0"}; -cvar_t sk_zombie_health3 = {"sk_zombie_health3","0"}; - -cvar_t sk_zombie_dmg_one_slash1 = {"sk_zombie_dmg_one_slash1","0"}; -cvar_t sk_zombie_dmg_one_slash2 = {"sk_zombie_dmg_one_slash2","0"}; -cvar_t sk_zombie_dmg_one_slash3 = {"sk_zombie_dmg_one_slash3","0"}; - -cvar_t sk_zombie_dmg_both_slash1 = {"sk_zombie_dmg_both_slash1","0"}; -cvar_t sk_zombie_dmg_both_slash2 = {"sk_zombie_dmg_both_slash2","0"}; -cvar_t sk_zombie_dmg_both_slash3 = {"sk_zombie_dmg_both_slash3","0"}; - - -//Turret -cvar_t sk_turret_health1 = {"sk_turret_health1","0"}; -cvar_t sk_turret_health2 = {"sk_turret_health2","0"}; -cvar_t sk_turret_health3 = {"sk_turret_health3","0"}; - - -// MiniTurret -cvar_t sk_miniturret_health1 = {"sk_miniturret_health1","0"}; -cvar_t sk_miniturret_health2 = {"sk_miniturret_health2","0"}; -cvar_t sk_miniturret_health3 = {"sk_miniturret_health3","0"}; - - -// Sentry Turret -cvar_t sk_sentry_health1 = {"sk_sentry_health1","0"}; -cvar_t sk_sentry_health2 = {"sk_sentry_health2","0"}; -cvar_t sk_sentry_health3 = {"sk_sentry_health3","0"}; - - -// PLAYER WEAPONS - -// Crowbar whack -cvar_t sk_plr_crowbar1 = {"sk_plr_crowbar1","0"}; -cvar_t sk_plr_crowbar2 = {"sk_plr_crowbar2","0"}; -cvar_t sk_plr_crowbar3 = {"sk_plr_crowbar3","0"}; - -// Glock Round -cvar_t sk_plr_9mm_bullet1 = {"sk_plr_9mm_bullet1","0"}; -cvar_t sk_plr_9mm_bullet2 = {"sk_plr_9mm_bullet2","0"}; -cvar_t sk_plr_9mm_bullet3 = {"sk_plr_9mm_bullet3","0"}; - -// 357 Round -cvar_t sk_plr_357_bullet1 = {"sk_plr_357_bullet1","0"}; -cvar_t sk_plr_357_bullet2 = {"sk_plr_357_bullet2","0"}; -cvar_t sk_plr_357_bullet3 = {"sk_plr_357_bullet3","0"}; - -// MP5 Round -cvar_t sk_plr_9mmAR_bullet1 = {"sk_plr_9mmAR_bullet1","0"}; -cvar_t sk_plr_9mmAR_bullet2 = {"sk_plr_9mmAR_bullet2","0"}; -cvar_t sk_plr_9mmAR_bullet3 = {"sk_plr_9mmAR_bullet3","0"}; - - -// M203 grenade -cvar_t sk_plr_9mmAR_grenade1 = {"sk_plr_9mmAR_grenade1","0"}; -cvar_t sk_plr_9mmAR_grenade2 = {"sk_plr_9mmAR_grenade2","0"}; -cvar_t sk_plr_9mmAR_grenade3 = {"sk_plr_9mmAR_grenade3","0"}; - - -// Shotgun buckshot -cvar_t sk_plr_buckshot1 = {"sk_plr_buckshot1","0"}; -cvar_t sk_plr_buckshot2 = {"sk_plr_buckshot2","0"}; -cvar_t sk_plr_buckshot3 = {"sk_plr_buckshot3","0"}; - - -// Crossbow -cvar_t sk_plr_xbow_bolt_client1 = {"sk_plr_xbow_bolt_client1","0"}; -cvar_t sk_plr_xbow_bolt_client2 = {"sk_plr_xbow_bolt_client2","0"}; -cvar_t sk_plr_xbow_bolt_client3 = {"sk_plr_xbow_bolt_client3","0"}; - -cvar_t sk_plr_xbow_bolt_monster1 = {"sk_plr_xbow_bolt_monster1","0"}; -cvar_t sk_plr_xbow_bolt_monster2 = {"sk_plr_xbow_bolt_monster2","0"}; -cvar_t sk_plr_xbow_bolt_monster3 = {"sk_plr_xbow_bolt_monster3","0"}; - - -// RPG -cvar_t sk_plr_rpg1 = {"sk_plr_rpg1","0"}; -cvar_t sk_plr_rpg2 = {"sk_plr_rpg2","0"}; -cvar_t sk_plr_rpg3 = {"sk_plr_rpg3","0"}; - - -// Zero Point Generator -cvar_t sk_plr_gauss1 = {"sk_plr_gauss1","0"}; -cvar_t sk_plr_gauss2 = {"sk_plr_gauss2","0"}; -cvar_t sk_plr_gauss3 = {"sk_plr_gauss3","0"}; - - -// Tau Cannon -cvar_t sk_plr_egon_narrow1 = {"sk_plr_egon_narrow1","0"}; -cvar_t sk_plr_egon_narrow2 = {"sk_plr_egon_narrow2","0"}; -cvar_t sk_plr_egon_narrow3 = {"sk_plr_egon_narrow3","0"}; - -cvar_t sk_plr_egon_wide1 = {"sk_plr_egon_wide1","0"}; -cvar_t sk_plr_egon_wide2 = {"sk_plr_egon_wide2","0"}; -cvar_t sk_plr_egon_wide3 = {"sk_plr_egon_wide3","0"}; - - -// Hand Grendade -cvar_t sk_plr_hand_grenade1 = {"sk_plr_hand_grenade1","0"}; -cvar_t sk_plr_hand_grenade2 = {"sk_plr_hand_grenade2","0"}; -cvar_t sk_plr_hand_grenade3 = {"sk_plr_hand_grenade3","0"}; - - -// Satchel Charge -cvar_t sk_plr_satchel1 = {"sk_plr_satchel1","0"}; -cvar_t sk_plr_satchel2 = {"sk_plr_satchel2","0"}; -cvar_t sk_plr_satchel3 = {"sk_plr_satchel3","0"}; - - -// Tripmine -cvar_t sk_plr_tripmine1 = {"sk_plr_tripmine1","0"}; -cvar_t sk_plr_tripmine2 = {"sk_plr_tripmine2","0"}; -cvar_t sk_plr_tripmine3 = {"sk_plr_tripmine3","0"}; - - -// WORLD WEAPONS -cvar_t sk_12mm_bullet1 = {"sk_12mm_bullet1","0"}; -cvar_t sk_12mm_bullet2 = {"sk_12mm_bullet2","0"}; -cvar_t sk_12mm_bullet3 = {"sk_12mm_bullet3","0"}; - -cvar_t sk_9mmAR_bullet1 = {"sk_9mmAR_bullet1","0"}; -cvar_t sk_9mmAR_bullet2 = {"sk_9mmAR_bullet2","0"}; -cvar_t sk_9mmAR_bullet3 = {"sk_9mmAR_bullet3","0"}; - -cvar_t sk_9mm_bullet1 = {"sk_9mm_bullet1","0"}; -cvar_t sk_9mm_bullet2 = {"sk_9mm_bullet2","0"}; -cvar_t sk_9mm_bullet3 = {"sk_9mm_bullet3","0"}; - - -// HORNET -cvar_t sk_hornet_dmg1 = {"sk_hornet_dmg1","0"}; -cvar_t sk_hornet_dmg2 = {"sk_hornet_dmg2","0"}; -cvar_t sk_hornet_dmg3 = {"sk_hornet_dmg3","0"}; - -// HEALTH/CHARGE -cvar_t sk_suitcharger1 = { "sk_suitcharger1","0" }; -cvar_t sk_suitcharger2 = { "sk_suitcharger2","0" }; -cvar_t sk_suitcharger3 = { "sk_suitcharger3","0" }; - -cvar_t sk_battery1 = { "sk_battery1","0" }; -cvar_t sk_battery2 = { "sk_battery2","0" }; -cvar_t sk_battery3 = { "sk_battery3","0" }; - -cvar_t sk_healthcharger1 = { "sk_healthcharger1","0" }; -cvar_t sk_healthcharger2 = { "sk_healthcharger2","0" }; -cvar_t sk_healthcharger3 = { "sk_healthcharger3","0" }; - -cvar_t sk_healthkit1 = { "sk_healthkit1","0" }; -cvar_t sk_healthkit2 = { "sk_healthkit2","0" }; -cvar_t sk_healthkit3 = { "sk_healthkit3","0" }; - -cvar_t sk_scientist_heal1 = { "sk_scientist_heal1","0" }; -cvar_t sk_scientist_heal2 = { "sk_scientist_heal2","0" }; -cvar_t sk_scientist_heal3 = { "sk_scientist_heal3","0" }; - - -// monster damage adjusters -cvar_t sk_monster_head1 = { "sk_monster_head1","2" }; -cvar_t sk_monster_head2 = { "sk_monster_head2","2" }; -cvar_t sk_monster_head3 = { "sk_monster_head3","2" }; - -cvar_t sk_monster_chest1 = { "sk_monster_chest1","1" }; -cvar_t sk_monster_chest2 = { "sk_monster_chest2","1" }; -cvar_t sk_monster_chest3 = { "sk_monster_chest3","1" }; - -cvar_t sk_monster_stomach1 = { "sk_monster_stomach1","1" }; -cvar_t sk_monster_stomach2 = { "sk_monster_stomach2","1" }; -cvar_t sk_monster_stomach3 = { "sk_monster_stomach3","1" }; - -cvar_t sk_monster_arm1 = { "sk_monster_arm1","1" }; -cvar_t sk_monster_arm2 = { "sk_monster_arm2","1" }; -cvar_t sk_monster_arm3 = { "sk_monster_arm3","1" }; - -cvar_t sk_monster_leg1 = { "sk_monster_leg1","1" }; -cvar_t sk_monster_leg2 = { "sk_monster_leg2","1" }; -cvar_t sk_monster_leg3 = { "sk_monster_leg3","1" }; - -// player damage adjusters -cvar_t sk_player_head1 = { "sk_player_head1","2" }; -cvar_t sk_player_head2 = { "sk_player_head2","2" }; -cvar_t sk_player_head3 = { "sk_player_head3","2" }; - -cvar_t sk_player_chest1 = { "sk_player_chest1","1" }; -cvar_t sk_player_chest2 = { "sk_player_chest2","1" }; -cvar_t sk_player_chest3 = { "sk_player_chest3","1" }; - -cvar_t sk_player_stomach1 = { "sk_player_stomach1","1" }; -cvar_t sk_player_stomach2 = { "sk_player_stomach2","1" }; -cvar_t sk_player_stomach3 = { "sk_player_stomach3","1" }; - -cvar_t sk_player_arm1 = { "sk_player_arm1","1" }; -cvar_t sk_player_arm2 = { "sk_player_arm2","1" }; -cvar_t sk_player_arm3 = { "sk_player_arm3","1" }; - -cvar_t sk_player_leg1 = { "sk_player_leg1","1" }; -cvar_t sk_player_leg2 = { "sk_player_leg2","1" }; -cvar_t sk_player_leg3 = { "sk_player_leg3","1" }; - // END Cvars for Skill Level settings // Register your console variables here @@ -595,6 +204,14 @@ void GameDLLInit( void ) CVAR_REGISTER (&avh_deathmatchmode); CVAR_REGISTER (&avh_countdowntime); + avh_cheats=CVAR_GET_POINTER("sv_cheats"); + showtriggers=CVAR_GET_POINTER("showtriggers"); + violence_ablood=CVAR_GET_POINTER("violence_ablood"); + violence_agibs=CVAR_GET_POINTER("violence_agibs"); + violence_hblood=CVAR_GET_POINTER("violence_hblood"); + violence_hgibs=CVAR_GET_POINTER("violence_hgibs"); + + CVAR_REGISTER (&avh_latejointime); CVAR_REGISTER (&avh_logdetail); //CVAR_REGISTER (&avh_teamsizehandicapping); @@ -602,6 +219,7 @@ void GameDLLInit( void ) CVAR_REGISTER (&avh_team2damagepercent); CVAR_REGISTER (&avh_team3damagepercent); CVAR_REGISTER (&avh_team4damagepercent); + CVAR_REGISTER (&avh_structurelimit); CVAR_REGISTER (&avh_votecasttime); CVAR_REGISTER (&avh_votedowntime); CVAR_REGISTER (&avh_minvotesneeded); @@ -639,407 +257,5 @@ void GameDLLInit( void ) CVAR_REGISTER (&avh_uplink); CVAR_REGISTER (&avh_killdelay); -// REGISTER CVARS FOR SKILL LEVEL STUFF - // Agrunt - CVAR_REGISTER ( &sk_agrunt_health1 );// {"sk_agrunt_health1","0"}; - CVAR_REGISTER ( &sk_agrunt_health2 );// {"sk_agrunt_health2","0"}; - CVAR_REGISTER ( &sk_agrunt_health3 );// {"sk_agrunt_health3","0"}; - - CVAR_REGISTER ( &sk_agrunt_dmg_punch1 );// {"sk_agrunt_dmg_punch1","0"}; - CVAR_REGISTER ( &sk_agrunt_dmg_punch2 );// {"sk_agrunt_dmg_punch2","0"}; - CVAR_REGISTER ( &sk_agrunt_dmg_punch3 );// {"sk_agrunt_dmg_punch3","0"}; - - // Apache - CVAR_REGISTER ( &sk_apache_health1 );// {"sk_apache_health1","0"}; - CVAR_REGISTER ( &sk_apache_health2 );// {"sk_apache_health2","0"}; - CVAR_REGISTER ( &sk_apache_health3 );// {"sk_apache_health3","0"}; - - // Barney - CVAR_REGISTER ( &sk_barney_health1 );// {"sk_barney_health1","0"}; - CVAR_REGISTER ( &sk_barney_health2 );// {"sk_barney_health2","0"}; - CVAR_REGISTER ( &sk_barney_health3 );// {"sk_barney_health3","0"}; - - // Bullsquid - CVAR_REGISTER ( &sk_bullsquid_health1 );// {"sk_bullsquid_health1","0"}; - CVAR_REGISTER ( &sk_bullsquid_health2 );// {"sk_bullsquid_health2","0"}; - CVAR_REGISTER ( &sk_bullsquid_health3 );// {"sk_bullsquid_health3","0"}; - - CVAR_REGISTER ( &sk_bullsquid_dmg_bite1 );// {"sk_bullsquid_dmg_bite1","0"}; - CVAR_REGISTER ( &sk_bullsquid_dmg_bite2 );// {"sk_bullsquid_dmg_bite2","0"}; - CVAR_REGISTER ( &sk_bullsquid_dmg_bite3 );// {"sk_bullsquid_dmg_bite3","0"}; - - CVAR_REGISTER ( &sk_bullsquid_dmg_whip1 );// {"sk_bullsquid_dmg_whip1","0"}; - CVAR_REGISTER ( &sk_bullsquid_dmg_whip2 );// {"sk_bullsquid_dmg_whip2","0"}; - CVAR_REGISTER ( &sk_bullsquid_dmg_whip3 );// {"sk_bullsquid_dmg_whip3","0"}; - - CVAR_REGISTER ( &sk_bullsquid_dmg_spit1 );// {"sk_bullsquid_dmg_spit1","0"}; - CVAR_REGISTER ( &sk_bullsquid_dmg_spit2 );// {"sk_bullsquid_dmg_spit2","0"}; - CVAR_REGISTER ( &sk_bullsquid_dmg_spit3 );// {"sk_bullsquid_dmg_spit3","0"}; - - - CVAR_REGISTER ( &sk_bigmomma_health_factor1 );// {"sk_bigmomma_health_factor1","1.0"}; - CVAR_REGISTER ( &sk_bigmomma_health_factor2 );// {"sk_bigmomma_health_factor2","1.0"}; - CVAR_REGISTER ( &sk_bigmomma_health_factor3 );// {"sk_bigmomma_health_factor3","1.0"}; - - CVAR_REGISTER ( &sk_bigmomma_dmg_slash1 );// {"sk_bigmomma_dmg_slash1","50"}; - CVAR_REGISTER ( &sk_bigmomma_dmg_slash2 );// {"sk_bigmomma_dmg_slash2","50"}; - CVAR_REGISTER ( &sk_bigmomma_dmg_slash3 );// {"sk_bigmomma_dmg_slash3","50"}; - - CVAR_REGISTER ( &sk_bigmomma_dmg_blast1 );// {"sk_bigmomma_dmg_blast1","100"}; - CVAR_REGISTER ( &sk_bigmomma_dmg_blast2 );// {"sk_bigmomma_dmg_blast2","100"}; - CVAR_REGISTER ( &sk_bigmomma_dmg_blast3 );// {"sk_bigmomma_dmg_blast3","100"}; - - CVAR_REGISTER ( &sk_bigmomma_radius_blast1 );// {"sk_bigmomma_radius_blast1","250"}; - CVAR_REGISTER ( &sk_bigmomma_radius_blast2 );// {"sk_bigmomma_radius_blast2","250"}; - CVAR_REGISTER ( &sk_bigmomma_radius_blast3 );// {"sk_bigmomma_radius_blast3","250"}; - - // Gargantua - CVAR_REGISTER ( &sk_gargantua_health1 );// {"sk_gargantua_health1","0"}; - CVAR_REGISTER ( &sk_gargantua_health2 );// {"sk_gargantua_health2","0"}; - CVAR_REGISTER ( &sk_gargantua_health3 );// {"sk_gargantua_health3","0"}; - - CVAR_REGISTER ( &sk_gargantua_dmg_slash1 );// {"sk_gargantua_dmg_slash1","0"}; - CVAR_REGISTER ( &sk_gargantua_dmg_slash2 );// {"sk_gargantua_dmg_slash2","0"}; - CVAR_REGISTER ( &sk_gargantua_dmg_slash3 );// {"sk_gargantua_dmg_slash3","0"}; - - CVAR_REGISTER ( &sk_gargantua_dmg_fire1 );// {"sk_gargantua_dmg_fire1","0"}; - CVAR_REGISTER ( &sk_gargantua_dmg_fire2 );// {"sk_gargantua_dmg_fire2","0"}; - CVAR_REGISTER ( &sk_gargantua_dmg_fire3 );// {"sk_gargantua_dmg_fire3","0"}; - - CVAR_REGISTER ( &sk_gargantua_dmg_stomp1 );// {"sk_gargantua_dmg_stomp1","0"}; - CVAR_REGISTER ( &sk_gargantua_dmg_stomp2 );// {"sk_gargantua_dmg_stomp2","0"}; - CVAR_REGISTER ( &sk_gargantua_dmg_stomp3 );// {"sk_gargantua_dmg_stomp3","0"}; - - - // Hassassin - CVAR_REGISTER ( &sk_hassassin_health1 );// {"sk_hassassin_health1","0"}; - CVAR_REGISTER ( &sk_hassassin_health2 );// {"sk_hassassin_health2","0"}; - CVAR_REGISTER ( &sk_hassassin_health3 );// {"sk_hassassin_health3","0"}; - - - // Headcrab - CVAR_REGISTER ( &sk_headcrab_health1 );// {"sk_headcrab_health1","0"}; - CVAR_REGISTER ( &sk_headcrab_health2 );// {"sk_headcrab_health2","0"}; - CVAR_REGISTER ( &sk_headcrab_health3 );// {"sk_headcrab_health3","0"}; - - CVAR_REGISTER ( &sk_headcrab_dmg_bite1 );// {"sk_headcrab_dmg_bite1","0"}; - CVAR_REGISTER ( &sk_headcrab_dmg_bite2 );// {"sk_headcrab_dmg_bite2","0"}; - CVAR_REGISTER ( &sk_headcrab_dmg_bite3 );// {"sk_headcrab_dmg_bite3","0"}; - - - // Hgrunt - CVAR_REGISTER ( &sk_hgrunt_health1 );// {"sk_hgrunt_health1","0"}; - CVAR_REGISTER ( &sk_hgrunt_health2 );// {"sk_hgrunt_health2","0"}; - CVAR_REGISTER ( &sk_hgrunt_health3 );// {"sk_hgrunt_health3","0"}; - - CVAR_REGISTER ( &sk_hgrunt_kick1 );// {"sk_hgrunt_kick1","0"}; - CVAR_REGISTER ( &sk_hgrunt_kick2 );// {"sk_hgrunt_kick2","0"}; - CVAR_REGISTER ( &sk_hgrunt_kick3 );// {"sk_hgrunt_kick3","0"}; - - CVAR_REGISTER ( &sk_hgrunt_pellets1 ); - CVAR_REGISTER ( &sk_hgrunt_pellets2 ); - CVAR_REGISTER ( &sk_hgrunt_pellets3 ); - - CVAR_REGISTER ( &sk_hgrunt_gspeed1 ); - CVAR_REGISTER ( &sk_hgrunt_gspeed2 ); - CVAR_REGISTER ( &sk_hgrunt_gspeed3 ); - - // Houndeye - CVAR_REGISTER ( &sk_houndeye_health1 );// {"sk_houndeye_health1","0"}; - CVAR_REGISTER ( &sk_houndeye_health2 );// {"sk_houndeye_health2","0"}; - CVAR_REGISTER ( &sk_houndeye_health3 );// {"sk_houndeye_health3","0"}; - - CVAR_REGISTER ( &sk_houndeye_dmg_blast1 );// {"sk_houndeye_dmg_blast1","0"}; - CVAR_REGISTER ( &sk_houndeye_dmg_blast2 );// {"sk_houndeye_dmg_blast2","0"}; - CVAR_REGISTER ( &sk_houndeye_dmg_blast3 );// {"sk_houndeye_dmg_blast3","0"}; - - - // ISlave - CVAR_REGISTER ( &sk_islave_health1 );// {"sk_islave_health1","0"}; - CVAR_REGISTER ( &sk_islave_health2 );// {"sk_islave_health2","0"}; - CVAR_REGISTER ( &sk_islave_health3 );// {"sk_islave_health3","0"}; - - CVAR_REGISTER ( &sk_islave_dmg_claw1 );// {"sk_islave_dmg_claw1","0"}; - CVAR_REGISTER ( &sk_islave_dmg_claw2 );// {"sk_islave_dmg_claw2","0"}; - CVAR_REGISTER ( &sk_islave_dmg_claw3 );// {"sk_islave_dmg_claw3","0"}; - - CVAR_REGISTER ( &sk_islave_dmg_clawrake1 );// {"sk_islave_dmg_clawrake1","0"}; - CVAR_REGISTER ( &sk_islave_dmg_clawrake2 );// {"sk_islave_dmg_clawrake2","0"}; - CVAR_REGISTER ( &sk_islave_dmg_clawrake3 );// {"sk_islave_dmg_clawrake3","0"}; - - CVAR_REGISTER ( &sk_islave_dmg_zap1 );// {"sk_islave_dmg_zap1","0"}; - CVAR_REGISTER ( &sk_islave_dmg_zap2 );// {"sk_islave_dmg_zap2","0"}; - CVAR_REGISTER ( &sk_islave_dmg_zap3 );// {"sk_islave_dmg_zap3","0"}; - - - // Icthyosaur - CVAR_REGISTER ( &sk_ichthyosaur_health1 );// {"sk_ichthyosaur_health1","0"}; - CVAR_REGISTER ( &sk_ichthyosaur_health2 );// {"sk_ichthyosaur_health2","0"}; - CVAR_REGISTER ( &sk_ichthyosaur_health3 );// {"sk_ichthyosaur_health3","0"}; - - CVAR_REGISTER ( &sk_ichthyosaur_shake1 );// {"sk_ichthyosaur_health3","0"}; - CVAR_REGISTER ( &sk_ichthyosaur_shake2 );// {"sk_ichthyosaur_health3","0"}; - CVAR_REGISTER ( &sk_ichthyosaur_shake3 );// {"sk_ichthyosaur_health3","0"}; - - - - // Leech - CVAR_REGISTER ( &sk_leech_health1 );// {"sk_leech_health1","0"}; - CVAR_REGISTER ( &sk_leech_health2 );// {"sk_leech_health2","0"}; - CVAR_REGISTER ( &sk_leech_health3 );// {"sk_leech_health3","0"}; - - CVAR_REGISTER ( &sk_leech_dmg_bite1 );// {"sk_leech_dmg_bite1","0"}; - CVAR_REGISTER ( &sk_leech_dmg_bite2 );// {"sk_leech_dmg_bite2","0"}; - CVAR_REGISTER ( &sk_leech_dmg_bite3 );// {"sk_leech_dmg_bite3","0"}; - - - // Controller - CVAR_REGISTER ( &sk_controller_health1 ); - CVAR_REGISTER ( &sk_controller_health2 ); - CVAR_REGISTER ( &sk_controller_health3 ); - - CVAR_REGISTER ( &sk_controller_dmgzap1 ); - CVAR_REGISTER ( &sk_controller_dmgzap2 ); - CVAR_REGISTER ( &sk_controller_dmgzap3 ); - - CVAR_REGISTER ( &sk_controller_speedball1 ); - CVAR_REGISTER ( &sk_controller_speedball2 ); - CVAR_REGISTER ( &sk_controller_speedball3 ); - - CVAR_REGISTER ( &sk_controller_dmgball1 ); - CVAR_REGISTER ( &sk_controller_dmgball2 ); - CVAR_REGISTER ( &sk_controller_dmgball3 ); - - // Nihilanth - CVAR_REGISTER ( &sk_nihilanth_health1 );// {"sk_nihilanth_health1","0"}; - CVAR_REGISTER ( &sk_nihilanth_health2 );// {"sk_nihilanth_health2","0"}; - CVAR_REGISTER ( &sk_nihilanth_health3 );// {"sk_nihilanth_health3","0"}; - - CVAR_REGISTER ( &sk_nihilanth_zap1 ); - CVAR_REGISTER ( &sk_nihilanth_zap2 ); - CVAR_REGISTER ( &sk_nihilanth_zap3 ); - - // Scientist - CVAR_REGISTER ( &sk_scientist_health1 );// {"sk_scientist_health1","0"}; - CVAR_REGISTER ( &sk_scientist_health2 );// {"sk_scientist_health2","0"}; - CVAR_REGISTER ( &sk_scientist_health3 );// {"sk_scientist_health3","0"}; - - - // Snark - CVAR_REGISTER ( &sk_snark_health1 );// {"sk_snark_health1","0"}; - CVAR_REGISTER ( &sk_snark_health2 );// {"sk_snark_health2","0"}; - CVAR_REGISTER ( &sk_snark_health3 );// {"sk_snark_health3","0"}; - - CVAR_REGISTER ( &sk_snark_dmg_bite1 );// {"sk_snark_dmg_bite1","0"}; - CVAR_REGISTER ( &sk_snark_dmg_bite2 );// {"sk_snark_dmg_bite2","0"}; - CVAR_REGISTER ( &sk_snark_dmg_bite3 );// {"sk_snark_dmg_bite3","0"}; - - CVAR_REGISTER ( &sk_snark_dmg_pop1 );// {"sk_snark_dmg_pop1","0"}; - CVAR_REGISTER ( &sk_snark_dmg_pop2 );// {"sk_snark_dmg_pop2","0"}; - CVAR_REGISTER ( &sk_snark_dmg_pop3 );// {"sk_snark_dmg_pop3","0"}; - - - - // Zombie - CVAR_REGISTER ( &sk_zombie_health1 );// {"sk_zombie_health1","0"}; - CVAR_REGISTER ( &sk_zombie_health2 );// {"sk_zombie_health3","0"}; - CVAR_REGISTER ( &sk_zombie_health3 );// {"sk_zombie_health3","0"}; - - CVAR_REGISTER ( &sk_zombie_dmg_one_slash1 );// {"sk_zombie_dmg_one_slash1","0"}; - CVAR_REGISTER ( &sk_zombie_dmg_one_slash2 );// {"sk_zombie_dmg_one_slash2","0"}; - CVAR_REGISTER ( &sk_zombie_dmg_one_slash3 );// {"sk_zombie_dmg_one_slash3","0"}; - - CVAR_REGISTER ( &sk_zombie_dmg_both_slash1 );// {"sk_zombie_dmg_both_slash1","0"}; - CVAR_REGISTER ( &sk_zombie_dmg_both_slash2 );// {"sk_zombie_dmg_both_slash2","0"}; - CVAR_REGISTER ( &sk_zombie_dmg_both_slash3 );// {"sk_zombie_dmg_both_slash3","0"}; - - - //Turret - CVAR_REGISTER ( &sk_turret_health1 );// {"sk_turret_health1","0"}; - CVAR_REGISTER ( &sk_turret_health2 );// {"sk_turret_health2","0"}; - CVAR_REGISTER ( &sk_turret_health3 );// {"sk_turret_health3","0"}; - - - // MiniTurret - CVAR_REGISTER ( &sk_miniturret_health1 );// {"sk_miniturret_health1","0"}; - CVAR_REGISTER ( &sk_miniturret_health2 );// {"sk_miniturret_health2","0"}; - CVAR_REGISTER ( &sk_miniturret_health3 );// {"sk_miniturret_health3","0"}; - - - // Sentry Turret - CVAR_REGISTER ( &sk_sentry_health1 );// {"sk_sentry_health1","0"}; - CVAR_REGISTER ( &sk_sentry_health2 );// {"sk_sentry_health2","0"}; - CVAR_REGISTER ( &sk_sentry_health3 );// {"sk_sentry_health3","0"}; - - - // PLAYER WEAPONS - - // Crowbar whack - CVAR_REGISTER ( &sk_plr_crowbar1 );// {"sk_plr_crowbar1","0"}; - CVAR_REGISTER ( &sk_plr_crowbar2 );// {"sk_plr_crowbar2","0"}; - CVAR_REGISTER ( &sk_plr_crowbar3 );// {"sk_plr_crowbar3","0"}; - - // Glock Round - CVAR_REGISTER ( &sk_plr_9mm_bullet1 );// {"sk_plr_9mm_bullet1","0"}; - CVAR_REGISTER ( &sk_plr_9mm_bullet2 );// {"sk_plr_9mm_bullet2","0"}; - CVAR_REGISTER ( &sk_plr_9mm_bullet3 );// {"sk_plr_9mm_bullet3","0"}; - - // 357 Round - CVAR_REGISTER ( &sk_plr_357_bullet1 );// {"sk_plr_357_bullet1","0"}; - CVAR_REGISTER ( &sk_plr_357_bullet2 );// {"sk_plr_357_bullet2","0"}; - CVAR_REGISTER ( &sk_plr_357_bullet3 );// {"sk_plr_357_bullet3","0"}; - - // MP5 Round - CVAR_REGISTER ( &sk_plr_9mmAR_bullet1 );// {"sk_plr_9mmAR_bullet1","0"}; - CVAR_REGISTER ( &sk_plr_9mmAR_bullet2 );// {"sk_plr_9mmAR_bullet2","0"}; - CVAR_REGISTER ( &sk_plr_9mmAR_bullet3 );// {"sk_plr_9mmAR_bullet3","0"}; - - - // M203 grenade - CVAR_REGISTER ( &sk_plr_9mmAR_grenade1 );// {"sk_plr_9mmAR_grenade1","0"}; - CVAR_REGISTER ( &sk_plr_9mmAR_grenade2 );// {"sk_plr_9mmAR_grenade2","0"}; - CVAR_REGISTER ( &sk_plr_9mmAR_grenade3 );// {"sk_plr_9mmAR_grenade3","0"}; - - - // Shotgun buckshot - CVAR_REGISTER ( &sk_plr_buckshot1 );// {"sk_plr_buckshot1","0"}; - CVAR_REGISTER ( &sk_plr_buckshot2 );// {"sk_plr_buckshot2","0"}; - CVAR_REGISTER ( &sk_plr_buckshot3 );// {"sk_plr_buckshot3","0"}; - - - // Crossbow - CVAR_REGISTER ( &sk_plr_xbow_bolt_monster1 );// {"sk_plr_xbow_bolt1","0"}; - CVAR_REGISTER ( &sk_plr_xbow_bolt_monster2 );// {"sk_plr_xbow_bolt2","0"}; - CVAR_REGISTER ( &sk_plr_xbow_bolt_monster3 );// {"sk_plr_xbow_bolt3","0"}; - - CVAR_REGISTER ( &sk_plr_xbow_bolt_client1 );// {"sk_plr_xbow_bolt1","0"}; - CVAR_REGISTER ( &sk_plr_xbow_bolt_client2 );// {"sk_plr_xbow_bolt2","0"}; - CVAR_REGISTER ( &sk_plr_xbow_bolt_client3 );// {"sk_plr_xbow_bolt3","0"}; - - - // RPG - CVAR_REGISTER ( &sk_plr_rpg1 );// {"sk_plr_rpg1","0"}; - CVAR_REGISTER ( &sk_plr_rpg2 );// {"sk_plr_rpg2","0"}; - CVAR_REGISTER ( &sk_plr_rpg3 );// {"sk_plr_rpg3","0"}; - - - // Gauss Gun - CVAR_REGISTER ( &sk_plr_gauss1 );// {"sk_plr_gauss1","0"}; - CVAR_REGISTER ( &sk_plr_gauss2 );// {"sk_plr_gauss2","0"}; - CVAR_REGISTER ( &sk_plr_gauss3 );// {"sk_plr_gauss3","0"}; - - - // Egon Gun - CVAR_REGISTER ( &sk_plr_egon_narrow1 );// {"sk_plr_egon_narrow1","0"}; - CVAR_REGISTER ( &sk_plr_egon_narrow2 );// {"sk_plr_egon_narrow2","0"}; - CVAR_REGISTER ( &sk_plr_egon_narrow3 );// {"sk_plr_egon_narrow3","0"}; - - CVAR_REGISTER ( &sk_plr_egon_wide1 );// {"sk_plr_egon_wide1","0"}; - CVAR_REGISTER ( &sk_plr_egon_wide2 );// {"sk_plr_egon_wide2","0"}; - CVAR_REGISTER ( &sk_plr_egon_wide3 );// {"sk_plr_egon_wide3","0"}; - - - // Hand Grendade - CVAR_REGISTER ( &sk_plr_hand_grenade1 );// {"sk_plr_hand_grenade1","0"}; - CVAR_REGISTER ( &sk_plr_hand_grenade2 );// {"sk_plr_hand_grenade2","0"}; - CVAR_REGISTER ( &sk_plr_hand_grenade3 );// {"sk_plr_hand_grenade3","0"}; - - - // Satchel Charge - CVAR_REGISTER ( &sk_plr_satchel1 );// {"sk_plr_satchel1","0"}; - CVAR_REGISTER ( &sk_plr_satchel2 );// {"sk_plr_satchel2","0"}; - CVAR_REGISTER ( &sk_plr_satchel3 );// {"sk_plr_satchel3","0"}; - - - // Tripmine - CVAR_REGISTER ( &sk_plr_tripmine1 );// {"sk_plr_tripmine1","0"}; - CVAR_REGISTER ( &sk_plr_tripmine2 );// {"sk_plr_tripmine2","0"}; - CVAR_REGISTER ( &sk_plr_tripmine3 );// {"sk_plr_tripmine3","0"}; - - - // WORLD WEAPONS - CVAR_REGISTER ( &sk_12mm_bullet1 );// {"sk_12mm_bullet1","0"}; - CVAR_REGISTER ( &sk_12mm_bullet2 );// {"sk_12mm_bullet2","0"}; - CVAR_REGISTER ( &sk_12mm_bullet3 );// {"sk_12mm_bullet3","0"}; - - CVAR_REGISTER ( &sk_9mmAR_bullet1 );// {"sk_9mm_bullet1","0"}; - CVAR_REGISTER ( &sk_9mmAR_bullet2 );// {"sk_9mm_bullet1","0"}; - CVAR_REGISTER ( &sk_9mmAR_bullet3 );// {"sk_9mm_bullet1","0"}; - - CVAR_REGISTER ( &sk_9mm_bullet1 );// {"sk_9mm_bullet1","0"}; - CVAR_REGISTER ( &sk_9mm_bullet2 );// {"sk_9mm_bullet2","0"}; - CVAR_REGISTER ( &sk_9mm_bullet3 );// {"sk_9mm_bullet3","0"}; - - - // HORNET - CVAR_REGISTER ( &sk_hornet_dmg1 );// {"sk_hornet_dmg1","0"}; - CVAR_REGISTER ( &sk_hornet_dmg2 );// {"sk_hornet_dmg2","0"}; - CVAR_REGISTER ( &sk_hornet_dmg3 );// {"sk_hornet_dmg3","0"}; - - // HEALTH/SUIT CHARGE DISTRIBUTION - CVAR_REGISTER ( &sk_suitcharger1 ); - CVAR_REGISTER ( &sk_suitcharger2 ); - CVAR_REGISTER ( &sk_suitcharger3 ); - - CVAR_REGISTER ( &sk_battery1 ); - CVAR_REGISTER ( &sk_battery2 ); - CVAR_REGISTER ( &sk_battery3 ); - - CVAR_REGISTER ( &sk_healthcharger1 ); - CVAR_REGISTER ( &sk_healthcharger2 ); - CVAR_REGISTER ( &sk_healthcharger3 ); - - CVAR_REGISTER ( &sk_healthkit1 ); - CVAR_REGISTER ( &sk_healthkit2 ); - CVAR_REGISTER ( &sk_healthkit3 ); - - CVAR_REGISTER ( &sk_scientist_heal1 ); - CVAR_REGISTER ( &sk_scientist_heal2 ); - CVAR_REGISTER ( &sk_scientist_heal3 ); - -// monster damage adjusters - CVAR_REGISTER ( &sk_monster_head1 ); - CVAR_REGISTER ( &sk_monster_head2 ); - CVAR_REGISTER ( &sk_monster_head3 ); - - CVAR_REGISTER ( &sk_monster_chest1 ); - CVAR_REGISTER ( &sk_monster_chest2 ); - CVAR_REGISTER ( &sk_monster_chest3 ); - - CVAR_REGISTER ( &sk_monster_stomach1 ); - CVAR_REGISTER ( &sk_monster_stomach2 ); - CVAR_REGISTER ( &sk_monster_stomach3 ); - - CVAR_REGISTER ( &sk_monster_arm1 ); - CVAR_REGISTER ( &sk_monster_arm2 ); - CVAR_REGISTER ( &sk_monster_arm3 ); - - CVAR_REGISTER ( &sk_monster_leg1 ); - CVAR_REGISTER ( &sk_monster_leg2 ); - CVAR_REGISTER ( &sk_monster_leg3 ); - -// player damage adjusters - CVAR_REGISTER ( &sk_player_head1 ); - CVAR_REGISTER ( &sk_player_head2 ); - CVAR_REGISTER ( &sk_player_head3 ); - - CVAR_REGISTER ( &sk_player_chest1 ); - CVAR_REGISTER ( &sk_player_chest2 ); - CVAR_REGISTER ( &sk_player_chest3 ); - - CVAR_REGISTER ( &sk_player_stomach1 ); - CVAR_REGISTER ( &sk_player_stomach2 ); - CVAR_REGISTER ( &sk_player_stomach3 ); - - CVAR_REGISTER ( &sk_player_arm1 ); - CVAR_REGISTER ( &sk_player_arm2 ); - CVAR_REGISTER ( &sk_player_arm3 ); - - CVAR_REGISTER ( &sk_player_leg1 ); - CVAR_REGISTER ( &sk_player_leg2 ); - CVAR_REGISTER ( &sk_player_leg3 ); -// END REGISTER CVARS FOR SKILL LEVEL STUFF - - SERVER_COMMAND( "exec skill.cfg\n" ); } diff --git a/main/source/dlls/gamerules.cpp b/main/source/dlls/gamerules.cpp index fcc8de5..7261079 100644 --- a/main/source/dlls/gamerules.cpp +++ b/main/source/dlls/gamerules.cpp @@ -109,196 +109,6 @@ BOOL CGameRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pWeap //========================================================= void CGameRules::RefreshSkillData ( void ) { - int iSkill; - - iSkill = (int)CVAR_GET_FLOAT("skill"); - g_iSkillLevel = iSkill; - - if ( iSkill < 1 ) - { - iSkill = 1; - } - else if ( iSkill > 3 ) - { - iSkill = 3; - } - - gSkillData.iSkillLevel = iSkill; - - ALERT ( at_console, "\nGAME SKILL LEVEL:%d\n",iSkill ); - - //Agrunt - gSkillData.agruntHealth = GetSkillCvar( "sk_agrunt_health" ); - gSkillData.agruntDmgPunch = GetSkillCvar( "sk_agrunt_dmg_punch"); - - // Apache - gSkillData.apacheHealth = GetSkillCvar( "sk_apache_health"); - - // Barney - gSkillData.barneyHealth = GetSkillCvar( "sk_barney_health"); - - // Big Momma - gSkillData.bigmommaHealthFactor = GetSkillCvar( "sk_bigmomma_health_factor" ); - gSkillData.bigmommaDmgSlash = GetSkillCvar( "sk_bigmomma_dmg_slash" ); - gSkillData.bigmommaDmgBlast = GetSkillCvar( "sk_bigmomma_dmg_blast" ); - gSkillData.bigmommaRadiusBlast = GetSkillCvar( "sk_bigmomma_radius_blast" ); - - // Bullsquid - gSkillData.bullsquidHealth = GetSkillCvar( "sk_bullsquid_health"); - gSkillData.bullsquidDmgBite = GetSkillCvar( "sk_bullsquid_dmg_bite"); - gSkillData.bullsquidDmgWhip = GetSkillCvar( "sk_bullsquid_dmg_whip"); - gSkillData.bullsquidDmgSpit = GetSkillCvar( "sk_bullsquid_dmg_spit"); - - // Gargantua - gSkillData.gargantuaHealth = GetSkillCvar( "sk_gargantua_health"); - gSkillData.gargantuaDmgSlash = GetSkillCvar( "sk_gargantua_dmg_slash"); - gSkillData.gargantuaDmgFire = GetSkillCvar( "sk_gargantua_dmg_fire"); - gSkillData.gargantuaDmgStomp = GetSkillCvar( "sk_gargantua_dmg_stomp"); - - // Hassassin - gSkillData.hassassinHealth = GetSkillCvar( "sk_hassassin_health"); - - // Headcrab - gSkillData.headcrabHealth = GetSkillCvar( "sk_headcrab_health"); - gSkillData.headcrabDmgBite = GetSkillCvar( "sk_headcrab_dmg_bite"); - - // Hgrunt - gSkillData.hgruntHealth = GetSkillCvar( "sk_hgrunt_health"); - gSkillData.hgruntDmgKick = GetSkillCvar( "sk_hgrunt_kick"); - gSkillData.hgruntShotgunPellets = GetSkillCvar( "sk_hgrunt_pellets"); - gSkillData.hgruntGrenadeSpeed = GetSkillCvar( "sk_hgrunt_gspeed"); - - // Houndeye - gSkillData.houndeyeHealth = GetSkillCvar( "sk_houndeye_health"); - gSkillData.houndeyeDmgBlast = GetSkillCvar( "sk_houndeye_dmg_blast"); - - // ISlave - gSkillData.slaveHealth = GetSkillCvar( "sk_islave_health"); - gSkillData.slaveDmgClaw = GetSkillCvar( "sk_islave_dmg_claw"); - gSkillData.slaveDmgClawrake = GetSkillCvar( "sk_islave_dmg_clawrake"); - gSkillData.slaveDmgZap = GetSkillCvar( "sk_islave_dmg_zap"); - - // Icthyosaur - gSkillData.ichthyosaurHealth = GetSkillCvar( "sk_ichthyosaur_health"); - gSkillData.ichthyosaurDmgShake = GetSkillCvar( "sk_ichthyosaur_shake"); - - // Leech - gSkillData.leechHealth = GetSkillCvar( "sk_leech_health"); - - gSkillData.leechDmgBite = GetSkillCvar( "sk_leech_dmg_bite"); - - // Controller - gSkillData.controllerHealth = GetSkillCvar( "sk_controller_health"); - gSkillData.controllerDmgZap = GetSkillCvar( "sk_controller_dmgzap"); - gSkillData.controllerSpeedBall = GetSkillCvar( "sk_controller_speedball"); - gSkillData.controllerDmgBall = GetSkillCvar( "sk_controller_dmgball"); - - // Nihilanth - gSkillData.nihilanthHealth = GetSkillCvar( "sk_nihilanth_health"); - gSkillData.nihilanthZap = GetSkillCvar( "sk_nihilanth_zap"); - - // Scientist - gSkillData.scientistHealth = GetSkillCvar( "sk_scientist_health"); - - // Snark - gSkillData.snarkHealth = GetSkillCvar( "sk_snark_health"); - gSkillData.snarkDmgBite = GetSkillCvar( "sk_snark_dmg_bite"); - gSkillData.snarkDmgPop = GetSkillCvar( "sk_snark_dmg_pop"); - - // Zombie - gSkillData.zombieHealth = GetSkillCvar( "sk_zombie_health"); - gSkillData.zombieDmgOneSlash = GetSkillCvar( "sk_zombie_dmg_one_slash"); - gSkillData.zombieDmgBothSlash = GetSkillCvar( "sk_zombie_dmg_both_slash"); - - //Turret - gSkillData.turretHealth = GetSkillCvar( "sk_turret_health"); - - // MiniTurret - gSkillData.miniturretHealth = GetSkillCvar( "sk_miniturret_health"); - - // Sentry Turret - gSkillData.sentryHealth = GetSkillCvar( "sk_sentry_health"); - -// PLAYER WEAPONS - - // Crowbar whack - gSkillData.plrDmgCrowbar = GetSkillCvar( "sk_plr_crowbar"); - - // Glock Round - gSkillData.plrDmg9MM = GetSkillCvar( "sk_plr_9mm_bullet"); - - // 357 Round - gSkillData.plrDmg357 = GetSkillCvar( "sk_plr_357_bullet"); - - // MP5 Round - gSkillData.plrDmgMP5 = GetSkillCvar( "sk_plr_9mmAR_bullet"); - - // M203 grenade - gSkillData.plrDmgM203Grenade = GetSkillCvar( "sk_plr_9mmAR_grenade"); - - // Shotgun buckshot - gSkillData.plrDmgBuckshot = GetSkillCvar( "sk_plr_buckshot"); - - // Crossbow - gSkillData.plrDmgCrossbowClient = GetSkillCvar( "sk_plr_xbow_bolt_client"); - gSkillData.plrDmgCrossbowMonster = GetSkillCvar( "sk_plr_xbow_bolt_monster"); - - // RPG - gSkillData.plrDmgRPG = GetSkillCvar( "sk_plr_rpg"); - - // Gauss gun - gSkillData.plrDmgGauss = GetSkillCvar( "sk_plr_gauss"); - - // Egon Gun - gSkillData.plrDmgEgonNarrow = GetSkillCvar( "sk_plr_egon_narrow"); - gSkillData.plrDmgEgonWide = GetSkillCvar( "sk_plr_egon_wide"); - - // Hand Grendade - gSkillData.plrDmgHandGrenade = GetSkillCvar( "sk_plr_hand_grenade"); - - // Satchel Charge - gSkillData.plrDmgSatchel = GetSkillCvar( "sk_plr_satchel"); - - // Tripmine - gSkillData.plrDmgTripmine = GetSkillCvar( "sk_plr_tripmine"); - - // MONSTER WEAPONS - gSkillData.monDmg12MM = GetSkillCvar( "sk_12mm_bullet"); - gSkillData.monDmgMP5 = GetSkillCvar ("sk_9mmAR_bullet" ); - gSkillData.monDmg9MM = GetSkillCvar( "sk_9mm_bullet"); - - // MONSTER HORNET - gSkillData.monDmgHornet = GetSkillCvar( "sk_hornet_dmg"); - - // PLAYER HORNET -// Up to this point, player hornet damage and monster hornet damage were both using -// monDmgHornet to determine how much damage to do. In tuning the hivehand, we now need -// to separate player damage and monster hivehand damage. Since it's so late in the project, we've -// added plrDmgHornet to the SKILLDATA struct, but not to the engine CVar list, so it's inaccesible -// via SKILLS.CFG. Any player hivehand tuning must take place in the code. (sjb) - gSkillData.plrDmgHornet = 7; - - - // HEALTH/CHARGE - gSkillData.suitchargerCapacity = GetSkillCvar( "sk_suitcharger" ); - gSkillData.batteryCapacity = GetSkillCvar( "sk_battery" ); - gSkillData.healthchargerCapacity = GetSkillCvar ( "sk_healthcharger" ); - gSkillData.healthkitCapacity = GetSkillCvar ( "sk_healthkit" ); - gSkillData.scientistHeal = GetSkillCvar ( "sk_scientist_heal" ); - - // monster damage adj - gSkillData.monHead = GetSkillCvar( "sk_monster_head" ); - gSkillData.monChest = GetSkillCvar( "sk_monster_chest" ); - gSkillData.monStomach = GetSkillCvar( "sk_monster_stomach" ); - gSkillData.monLeg = GetSkillCvar( "sk_monster_leg" ); - gSkillData.monArm = GetSkillCvar( "sk_monster_arm" ); - - // player damage adj - gSkillData.plrHead = GetSkillCvar( "sk_player_head" ); - gSkillData.plrChest = GetSkillCvar( "sk_player_chest" ); - gSkillData.plrStomach = GetSkillCvar( "sk_player_stomach" ); - gSkillData.plrLeg = GetSkillCvar( "sk_player_leg" ); - gSkillData.plrArm = GetSkillCvar( "sk_player_arm" ); } //========================================================= diff --git a/main/source/dlls/ggrenade.cpp b/main/source/dlls/ggrenade.cpp index 9c8fd2e..b830e01 100644 --- a/main/source/dlls/ggrenade.cpp +++ b/main/source/dlls/ggrenade.cpp @@ -30,6 +30,7 @@ #include "mod/AvHServerVariables.h" #include "mod/AvHMarineWeaponConstants.h" #include "mod/AvHGamerules.h" +#include "mod/AvHServerVariables.h" #include "util/MathUtil.h" #include "common/vec_op.h" @@ -44,12 +45,16 @@ LINK_ENTITY_TO_CLASS( grenade, CGrenade ); // // Grenade Explode // -void CGrenade::Explode( Vector vecSrc, Vector vecAim ) +void CGrenade::SetDamageType(int inDamageType) { + this->m_damageType=inDamageType; +} + +void CGrenade::Explode( Vector vecSrc, Vector vecAim) { TraceResult tr; UTIL_TraceLine ( pev->origin, pev->origin + Vector ( 0, 0, -32 ), ignore_monsters, ENT(pev), & tr); - Explode( &tr, NS_DMG_BLAST); + Explode( &tr, this->m_damageType); } // UNDONE: temporary scorching for PreAlpha - find a less sleazy permenant solution. @@ -207,15 +212,17 @@ void CGrenade::Detonate( void ) TraceResult tr; Vector vecSpot;// trace starts here! - if(CVAR_GET_FLOAT(kvBulletCam)) +#ifdef DEBUG + if(ns_cvar_float(&avh_bulletcam)) { SET_VIEW(this->pev->owner, this->pev->owner); } +#endif vecSpot = pev->origin + Vector ( 0 , 0 , 8 ); UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr); - Explode( &tr, NS_DMG_BLAST); + Explode( &tr, this->m_damageType); } // @@ -252,7 +259,7 @@ void CGrenade::ExplodeTouch( CBaseEntity *pOther ) vecSpot = pev->origin - pev->velocity.Normalize() * 32; UTIL_TraceLine( vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr ); - Explode( &tr, NS_DMG_BLAST); + Explode( &tr, this->m_damageType); } @@ -315,7 +322,7 @@ void CGrenade::BounceTouch( CBaseEntity *pOther ) { TraceResult tr = UTIL_GetGlobalTrace( ); ClearMultiDamage( ); - pOther->TraceAttack(pevOwner, 1, gpGlobals->v_forward, &tr, NS_DMG_BLAST); + pOther->TraceAttack(pevOwner, 1, gpGlobals->v_forward, &tr, this->m_damageType); ApplyMultiDamage( pev, pevOwner); } m_flNextAttack = gpGlobals->time + 1.0; // debounce @@ -452,6 +459,7 @@ void CGrenade:: Spawn( void ) UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); m_fRegisteredSound = FALSE; + m_damageType = NS_DMG_BLAST; } @@ -481,9 +489,10 @@ CGrenade *CGrenade::ShootContact( entvars_t *pevOwner, Vector vecStart, Vector v return pGrenade; } -CGrenade* CGrenade::ShootExplosiveTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time ) +CGrenade* CGrenade::ShootExplosiveTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time, int inDamageType) { CGrenade *pGrenade = CGrenade::ShootTimed(pevOwner, vecStart, vecVelocity, time); + pGrenade->SetDamageType(inDamageType); pGrenade->SetTouch(&CGrenade::ExplosiveBounceTouch); return pGrenade; } @@ -500,10 +509,12 @@ CGrenade * CGrenade:: ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector v pGrenade->SetTouch( &CGrenade::BounceTouch ); // Bounce if touched - if(CVAR_GET_FLOAT(kvBulletCam)) +#ifdef DEBUG + if(ns_cvar_float(&avh_bulletcam)) { SET_VIEW(ENT(pevOwner), ENT(pGrenade->pev)); } +#endif // Take one second off of the desired detonation time and set the think to PreDetonate. PreDetonate // will insert a DANGER sound into the world sound list and delay detonation for one second so that diff --git a/main/source/dlls/hl.vcproj b/main/source/dlls/hl.vcproj index 4681221..bfc1b40 100644 --- a/main/source/dlls/hl.vcproj +++ b/main/source/dlls/hl.vcproj @@ -27,7 +27,7 @@ InlineFunctionExpansion="2" EnableIntrinsicFunctions="TRUE" AdditionalIncludeDirectories=""$(SolutionDir)";U:\include\stlport;U:\include\nexus;U:\include\lua;U:\include\particle;U:\include" - PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;QUIVER;VOXEL;QUAKE2;VALVE_DLL;AVH_SERVER;AVH_PLAYTEST_BUILD;$(NOINHERIT)" + PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;QUIVER;VOXEL;QUAKE2;VALVE_DLL;AVH_SERVER;AVH_SECURE_PRERELEASE_BUILD;USE_OLDAUTH;$(NOINHERIT)" StringPooling="TRUE" MinimalRebuild="TRUE" RuntimeLibrary="0" @@ -99,7 +99,7 @@ Optimization="0" OptimizeForProcessor="1" AdditionalIncludeDirectories=""$(SolutionDir)";U:\include\stlport;U:\include\nexus;U:\include\lua;U:\include\particle;U:\include" - PreprocessorDefinitions="_DEBUG;DEBUG;WIN32;_WINDOWS;QUIVER;VOXEL;QUAKE2;VALVE_DLL;AVH_SERVER;$(NOINHERIT)" + PreprocessorDefinitions="_DEBUG;DEBUG;WIN32;_WINDOWS;QUIVER;VOXEL;QUAKE2;VALVE_DLL;AVH_SERVER;AVH_SECURE_PRERELEASE_BUILD;USE_OLDAUTH;$(NOINHERIT)" RuntimeLibrary="1" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="0" @@ -2783,12 +2783,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/main/source/dlls/multiplay_gamerules.cpp b/main/source/dlls/multiplay_gamerules.cpp index 62e17bf..3a26b51 100644 --- a/main/source/dlls/multiplay_gamerules.cpp +++ b/main/source/dlls/multiplay_gamerules.cpp @@ -29,6 +29,7 @@ #include "mod/AvHServerUtil.h" #include "common/hltv.h" #include "mod/AvHNetworkMessages.h" +#include "mod/AvHServerVariables.h" extern DLL_GLOBAL CGameRules *g_pGameRules; extern DLL_GLOBAL BOOL g_fGameOver; @@ -74,7 +75,7 @@ CHalfLifeMultiplay :: CHalfLifeMultiplay() { //g_VoiceGameMgr.Init(&g_GameMgrHelper, gpGlobals->maxClients); - RefreshSkillData(); + //RefreshSkillData(); m_flIntermissionEndTime = 0; g_flIntermissionStartTime = 0; @@ -130,7 +131,7 @@ BOOL CHalfLifeMultiplay::ClientCommand( CBasePlayer *pPlayer, const char *pcmd ) void CHalfLifeMultiplay::RefreshSkillData( void ) { // load all default values - CGameRules::RefreshSkillData(); + //CGameRules::RefreshSkillData(); // override some values for multiplay. @@ -201,7 +202,7 @@ void CHalfLifeMultiplay :: Think ( void ) if ( g_fGameOver ) // someone else quit the game already { // bounds check - int time = (int)CVAR_GET_FLOAT( "mp_chattime" ); + int time = (int)ns_cvar_float( &mp_chattime ); if ( time < 10 ) CVAR_SET_STRING( "mp_chattime", "10" ); else if ( time > MAX_INTERMISSION_TIME ) @@ -1012,7 +1013,7 @@ void CHalfLifeMultiplay :: GoToIntermission( void ) MESSAGE_END(); // bounds check - int time = (int)CVAR_GET_FLOAT( "mp_chattime" ); + int time = (int)ns_cvar_float( &mp_chattime ); if ( time < 10 ) CVAR_SET_STRING( "mp_chattime", "10" ); else if ( time > MAX_INTERMISSION_TIME ) diff --git a/main/source/dlls/player.cpp b/main/source/dlls/player.cpp index 575ebb1..c08f9e3 100644 --- a/main/source/dlls/player.cpp +++ b/main/source/dlls/player.cpp @@ -1,5012 +1,5047 @@ -// -// Copyright (c) 1999, Valve LLC. All rights reserved. -// -// This product contains software technology licensed from Id -// Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -// All Rights Reserved. -// -// Use, distribution, and modification of this source code and/or resulting -// object code is restricted to non-commercial enhancements to products from -// Valve LLC. All other use, distribution, or modification is prohibited -// without written permission from Valve LLC. -// -// -//===== player.cpp ======================================================== -// -// functions dealing with the player -// -// -// $Workfile: player.cpp $ -// $Date: 2002/11/22 21:12:55 $ -// -//------------------------------------------------------------------------------- -// $Log: player.cpp,v $ -// Revision 1.39 2002/11/22 21:12:55 Flayra -// - Added DMG_IGNOREARMOR flag for players (used for ending game quickly) -// - NULL checks for when owner of turret is removed after owner leaves team -// - Send deploy weapon switch to player, when "lastinv" is executed -// -// Revision 1.38 2002/11/15 19:09:40 Flayra -// - Reworked network metering to be easily toggleable -// -// Revision 1.37 2002/11/12 02:18:41 Flayra -// - Beginning of HLTV changes -// -// Revision 1.36 2002/10/24 21:18:23 Flayra -// - Major networking optimizations, to prevent overlflows after 20+ players on game reset -// -// Revision 1.35 2002/10/18 22:15:13 Flayra -// - Fixed problem where level5 gets stuck with redemption -// -// Revision 1.34 2002/10/16 00:40:39 Flayra -// - Fixed bug where player couldn't switch to next player while observing -// - Propagate health as short for larger aliens -// - Propagate auth mask -// -// Revision 1.33 2002/10/03 18:26:03 Flayra -// - Removed HL "flatline" sound on death -// -// Revision 1.32 2002/09/23 22:03:50 Flayra -// - Fixed problem where anims weren't playing properly in ready room -// - Fixed problem where gestation model anims were playing before model was updated -// -// Revision 1.31 2002/08/31 18:01:18 Flayra -// - Work at VALVe -// -// Revision 1.30 2002/08/09 00:17:03 Flayra -// - Allow moveleft and moveright to change targets, allow players to have only one primary weapon -// -// Revision 1.29 2002/07/23 16:48:21 Flayra -// - Refactored duplicate spawn code -// -// Revision 1.28 2002/07/10 14:36:09 Flayra -// - Fixed bug that caused "Battery" message to be sent every tick (this never showed up in HL because they never had a float armor value?) -// -// Revision 1.27 2002/07/08 16:31:39 Flayra -// - Level 1 doesn't make falling splat sound, dead players shouldn't block spawn points, replaced impulse hard-codes with constants -// -//=============================================================================== -#include -#include "extdll.h" -#include "util.h" - -#include "cbase.h" -#include "player.h" -#include "trains.h" -#include "nodes.h" -#include "weapons.h" -#include "soundent.h" -#include "monsters.h" -#include "engine/shake.h" -#include "decals.h" -#include "gamerules.h" -#include "mod/AvHEntities.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHHulls.h" -#include "mod/AvHMovementUtil.h" -#include "game.h" -#include "common/hltv.h" -#include "mod/AvHNetworkMessages.h" - -// #define DUCKFIX - -// Allow assignment within conditional -#pragma warning (disable: 4706) - -extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; -extern DLL_GLOBAL BOOL g_fGameOver; -extern DLL_GLOBAL BOOL g_fDrawLines; -int gEvilImpulse101; -extern DLL_GLOBAL int g_iSkillLevel, gDisplayTitle; - - -BOOL gInitHUD = TRUE; - -extern void CopyToBodyQue(entvars_t* pev); -extern void respawn(entvars_t *pev, BOOL fCopyCorpse); -extern Vector VecBModelOrigin(entvars_t *pevBModel ); -//extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ); - -// the world node graph -extern CGraph WorldGraph; - -#define TRAIN_ACTIVE 0x80 -#define TRAIN_NEW 0xc0 -#define TRAIN_OFF 0x00 -#define TRAIN_NEUTRAL 0x01 -#define TRAIN_SLOW 0x02 -#define TRAIN_MEDIUM 0x03 -#define TRAIN_FAST 0x04 -#define TRAIN_BACK 0x05 - -#define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes -#define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit) - -// Global Savedata for player -TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = -{ - DEFINE_FIELD( CBasePlayer, m_afButtonLast, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_afButtonPressed, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_afButtonReleased, FIELD_INTEGER ), - - DEFINE_ARRAY( CBasePlayer, m_rgItems, FIELD_INTEGER, MAX_ITEMS ), - DEFINE_FIELD( CBasePlayer, m_afPhysicsFlags, FIELD_INTEGER ), - - DEFINE_FIELD( CBasePlayer, m_flTimeStepSound, FIELD_TIME ), - DEFINE_FIELD( CBasePlayer, m_flTimeWeaponIdle, FIELD_TIME ), - DEFINE_FIELD( CBasePlayer, m_flSwimTime, FIELD_TIME ), - DEFINE_FIELD( CBasePlayer, m_flDuckTime, FIELD_TIME ), - DEFINE_FIELD( CBasePlayer, m_flWallJumpTime, FIELD_TIME ), - - DEFINE_FIELD( CBasePlayer, m_flSuitUpdate, FIELD_TIME ), - DEFINE_ARRAY( CBasePlayer, m_rgSuitPlayList, FIELD_INTEGER, CSUITPLAYLIST ), - DEFINE_FIELD( CBasePlayer, m_iSuitPlayNext, FIELD_INTEGER ), - DEFINE_ARRAY( CBasePlayer, m_rgiSuitNoRepeat, FIELD_INTEGER, CSUITNOREPEAT ), - DEFINE_ARRAY( CBasePlayer, m_rgflSuitNoRepeatTime, FIELD_TIME, CSUITNOREPEAT ), - DEFINE_FIELD( CBasePlayer, m_lastDamageAmount, FIELD_INTEGER ), - - DEFINE_ARRAY( CBasePlayer, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ), - DEFINE_FIELD( CBasePlayer, m_pActiveItem, FIELD_CLASSPTR ), - DEFINE_FIELD( CBasePlayer, m_pLastItem, FIELD_CLASSPTR ), - - DEFINE_ARRAY( CBasePlayer, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ), - DEFINE_FIELD( CBasePlayer, m_idrowndmg, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_idrownrestored, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_tSneaking, FIELD_TIME ), - - DEFINE_FIELD( CBasePlayer, m_iTrain, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_bitsHUDDamage, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_flFallVelocity, FIELD_FLOAT ), - DEFINE_FIELD( CBasePlayer, m_iTargetVolume, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_iWeaponVolume, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_iExtraSoundTypes, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_iWeaponFlash, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_fLongJump, FIELD_BOOLEAN ), - DEFINE_FIELD( CBasePlayer, m_fInitHUD, FIELD_BOOLEAN ), - DEFINE_FIELD( CBasePlayer, m_tbdPrev, FIELD_TIME ), - - DEFINE_FIELD( CBasePlayer, m_pTank, FIELD_EHANDLE ), - DEFINE_FIELD( CBasePlayer, m_iHideHUD, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_iFOV, FIELD_INTEGER ), -}; - -void CBasePlayer :: Pain( void ) -{ - float flRndSound;//sound randomizer - - flRndSound = RANDOM_FLOAT ( 0 , 1 ); - - if ( flRndSound <= 0.33 ) - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM); - else if ( flRndSound <= 0.66 ) - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM); - else - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM); -} - -/* - * - */ -Vector VecVelocityForDamage(float flDamage) -{ - Vector vec(RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); - - if (flDamage > -50) - vec = vec * 0.7; - else if (flDamage > -200) - vec = vec * 2; - else - vec = vec * 10; - - return vec; -} - -#if 0 /* -static void ThrowGib(entvars_t *pev, char *szGibModel, float flDamage) -{ - edict_t *pentNew = CREATE_ENTITY(); - entvars_t *pevNew = VARS(pentNew); - - pevNew->origin = pev->origin; - SET_MODEL(ENT(pevNew), szGibModel); - UTIL_SetSize(pevNew, g_vecZero, g_vecZero); - - pevNew->velocity = VecVelocityForDamage(flDamage); - pevNew->movetype = MOVETYPE_BOUNCE; - pevNew->solid = SOLID_NOT; - pevNew->avelocity.x = RANDOM_FLOAT(0,600); - pevNew->avelocity.y = RANDOM_FLOAT(0,600); - pevNew->avelocity.z = RANDOM_FLOAT(0,600); - CHANGE_METHOD(ENT(pevNew), em_think, SUB_Remove); - pevNew->ltime = gpGlobals->time; - pevNew->nextthink = gpGlobals->time + RANDOM_FLOAT(10,20); - pevNew->frame = 0; - pevNew->flags = 0; -} - - -static void ThrowHead(entvars_t *pev, char *szGibModel, floatflDamage) -{ - SET_MODEL(ENT(pev), szGibModel); - pev->frame = 0; - pev->nextthink = -1; - pev->movetype = MOVETYPE_BOUNCE; - pev->takedamage = DAMAGE_NO; - pev->solid = SOLID_NOT; - pev->view_ofs = Vector(0,0,8); - UTIL_SetSize(pev, Vector(-16,-16,0), Vector(16,16,56)); - pev->velocity = VecVelocityForDamage(flDamage); - pev->avelocity = RANDOM_FLOAT(-1,1) * Vector(0,600,0); - pev->origin.z -= 24; - ClearBits(pev->flags, FL_ONGROUND); -} - - -*/ -#endif - -int TrainSpeed(int iSpeed, int iMax) -{ - float fSpeed, fMax; - int iRet = 0; - - fMax = (float)iMax; - fSpeed = iSpeed; - - fSpeed = fSpeed/fMax; - - if (iSpeed < 0) - iRet = TRAIN_BACK; - else if (iSpeed == 0) - iRet = TRAIN_NEUTRAL; - else if (fSpeed < 0.33) - iRet = TRAIN_SLOW; - else if (fSpeed < 0.66) - iRet = TRAIN_MEDIUM; - else - iRet = TRAIN_FAST; - - return iRet; -} - -void CBasePlayer :: DeathSound( void ) -{ - // water death sounds - /* - if (pev->waterlevel == 3) - { - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/h2odeath.wav", 1, ATTN_NONE); - return; - } - */ - - // temporarily using pain sounds for death sounds - switch (RANDOM_LONG(1,5)) - { - case 1: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM); - break; - case 2: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM); - break; - case 3: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM); - break; - } - - // play one of the suit death alarms - //EMIT_GROUPNAME_SUIT(ENT(pev), "HEV_DEAD"); -} - -// override takehealth -// bitsDamageType indicates type of damage healed. - -int CBasePlayer :: TakeHealth( float flHealth, int bitsDamageType ) -{ - return CBaseMonster :: TakeHealth (flHealth, bitsDamageType); - -} - -Vector CBasePlayer :: GetGunPosition( ) -{ -// UTIL_MakeVectors(pev->v_angle); -// m_HackedGunPos = pev->view_ofs; - Vector origin; - - origin = pev->origin + pev->view_ofs; - - return origin; -} - -//========================================================= -// TraceAttack -//========================================================= -void CBasePlayer :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if ( pev->takedamage ) - { - m_LastHitGroup = ptr->iHitgroup; - - switch ( ptr->iHitgroup ) - { - case HITGROUP_GENERIC: - break; - case HITGROUP_HEAD: - flDamage *= gSkillData.plrHead; - break; - case HITGROUP_CHEST: - flDamage *= gSkillData.plrChest; - break; - case HITGROUP_STOMACH: - flDamage *= gSkillData.plrStomach; - break; - case HITGROUP_LEFTARM: - case HITGROUP_RIGHTARM: - flDamage *= gSkillData.plrArm; - break; - case HITGROUP_LEFTLEG: - case HITGROUP_RIGHTLEG: - flDamage *= gSkillData.plrLeg; - break; - default: - break; - } - - SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. - TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); - AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); - } -} - -void CBasePlayer::SpawnClientSideCorpse() -{ -// char *infobuffer = g_engfuncs.pfnGetInfoKeyBuffer( edict() ); -// char *pModel = g_engfuncs.pfnInfoKeyValue( infobuffer, "model" ); -// -// MESSAGE_BEGIN( MSG_ALL, gmsgSendCorpse ); -// WRITE_STRING( pModel ); -// WRITE_LONG ( pev->origin.x * 128.0); -// WRITE_LONG ( pev->origin.y * 128.0); -// WRITE_LONG ( pev->origin.z * 128.0); -// WRITE_COORD ( pev->angles.x ); -// WRITE_COORD ( pev->angles.y ); -// WRITE_COORD ( pev->angles.z ); -// WRITE_LONG ( (pev->animtime - gpGlobals->time) * 100.0f ); -// WRITE_BYTE ( pev->sequence ); -// WRITE_BYTE ( pev->body ); -// MESSAGE_END(); -} - -/* - Take some damage. - NOTE: each call to TakeDamage with bitsDamageType set to a time-based damage - type will cause the damage time countdown to be reset. Thus the ongoing effects of poison, radiation - etc are implemented with subsequent calls to TakeDamage using DMG_GENERIC. -*/ - -int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // have suit diagnose the problem - ie: report damage type - int bitsDamage = bitsDamageType; - int ffound = TRUE; - int fmajor; - int fcritical; - int fTookDamage; - int ftrivial; - //float flRatio; - //float flBonus; - float flHealthPrev = pev->health; - - //flBonus = AvHPlayerUpgrade::GetArmorBonus(this->pev->iuser4); - //flRatio = AvHPlayerUpgrade::GetArmorRatio(this->pev->iuser4); - -// if ( ( bitsDamageType & DMG_BLAST ) && g_pGameRules->IsMultiplayer() ) -// { -// // blasts damage armor more. -// flBonus *= 2; -// } - - // Already dead - if ( !IsAlive() ) - return 0; - // go take the damage first - - - CBaseEntity *pAttacker = NULL; - - if(pevAttacker) - { - pAttacker = CBaseEntity::Instance(pevAttacker); - } - - if ( !g_pGameRules->FPlayerCanTakeDamage( this, pAttacker ) ) - { - // Refuse the damage - return 0; - } - - // keep track of amount of damage last sustained - m_lastDamageAmount = flDamage; - - if(!(bitsDamageType & DMG_IGNOREARMOR)) - { - flDamage = AvHPlayerUpgrade::CalculateDamageLessArmor((AvHUser3)this->pev->iuser3, this->pev->iuser4, flDamage, this->pev->armorvalue, bitsDamageType, GetGameRules()->GetNumActiveHives((AvHTeamNumber)this->pev->team)); - } - else - { - int a = 0; - } - - // Armor. -// if (pev->armorvalue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage! -// { -// float flNew = flDamage * flRatio; -// -// float flArmor; -// -// flArmor = (flDamage - flNew) * flBonus; -// -// // Does this use more armor than we have? -// if (flArmor > pev->armorvalue) -// { -// flArmor = pev->armorvalue; -// flArmor *= (1/flBonus); -// flNew = flDamage - flArmor; -// pev->armorvalue = 0; -// } -// else -// pev->armorvalue -= flArmor; -// -// flDamage = flNew; -// } - - fTookDamage = CBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); - - // reset damage time countdown for each type of time based damage player just sustained - - { - for (int i = 0; i < CDMG_TIMEBASED; i++) - if (bitsDamageType & (DMG_PARALYZE << i)) - m_rgbTimeBasedDamage[i] = 0; - } - - // tell director about it - MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR ); - WRITE_BYTE(DIRECTOR_MESSAGE_SIZE); - WRITE_BYTE ( DRC_CMD_EVENT ); // take damage event - WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity - WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) ); // index number of secondary entity - WRITE_LONG( 5 ); // eventflags (priority and flags) - MESSAGE_END(); - - - // how bad is it, doc? - - ftrivial = (pev->health > 75 || m_lastDamageAmount < 5); - fmajor = (m_lastDamageAmount > 25); - fcritical = (pev->health < 30); - - // handle all bits set in this damage message, - // let the suit give player the diagnosis - - // UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash ) - - // UNDONE: still need to record damage and heal messages for the following types - - // DMG_BURN - // DMG_FREEZE - // DMG_BLAST - // DMG_SHOCK - - m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client - m_bitsHUDDamage = -1; // make sure the damage bits get resent - - while (fTookDamage && (!ftrivial || (bitsDamage & DMG_TIMEBASED)) && ffound && bitsDamage) - { - ffound = FALSE; - - if (bitsDamage & DMG_CLUB) - { - if (fmajor) - SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture - bitsDamage &= ~DMG_CLUB; - ffound = TRUE; - } - if (bitsDamage & (DMG_FALL | DMG_CRUSH)) - { - if (fmajor) - SetSuitUpdate("!HEV_DMG5", FALSE, SUIT_NEXT_IN_30SEC); // major fracture - else - SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture - - bitsDamage &= ~(DMG_FALL | DMG_CRUSH); - ffound = TRUE; - } - - if (bitsDamage & DMG_BULLET) - { - if (m_lastDamageAmount > 5) - SetSuitUpdate("!HEV_DMG6", FALSE, SUIT_NEXT_IN_30SEC); // blood loss detected - //else - // SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration - - bitsDamage &= ~DMG_BULLET; - ffound = TRUE; - } - - if (bitsDamage & DMG_SLASH) - { - if (fmajor) - SetSuitUpdate("!HEV_DMG1", FALSE, SUIT_NEXT_IN_30SEC); // major laceration - else - SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration - - bitsDamage &= ~DMG_SLASH; - ffound = TRUE; - } - - if (bitsDamage & DMG_SONIC) - { - if (fmajor) - SetSuitUpdate("!HEV_DMG2", FALSE, SUIT_NEXT_IN_1MIN); // internal bleeding - bitsDamage &= ~DMG_SONIC; - ffound = TRUE; - } - - if (bitsDamage & (DMG_POISON | DMG_PARALYZE)) - { - SetSuitUpdate("!HEV_DMG3", FALSE, SUIT_NEXT_IN_1MIN); // blood toxins detected - bitsDamage &= ~(DMG_POISON | DMG_PARALYZE); - ffound = TRUE; - } - - if (bitsDamage & DMG_ACID) - { - SetSuitUpdate("!HEV_DET1", FALSE, SUIT_NEXT_IN_1MIN); // hazardous chemicals detected - bitsDamage &= ~DMG_ACID; - ffound = TRUE; - } - - if (bitsDamage & DMG_NERVEGAS) - { - SetSuitUpdate("!HEV_DET0", FALSE, SUIT_NEXT_IN_1MIN); // biohazard detected - bitsDamage &= ~DMG_NERVEGAS; - ffound = TRUE; - } - - if (bitsDamage & DMG_RADIATION) - { - SetSuitUpdate("!HEV_DET2", FALSE, SUIT_NEXT_IN_1MIN); // radiation detected - bitsDamage &= ~DMG_RADIATION; - ffound = TRUE; - } - if (bitsDamage & DMG_SHOCK) - { - bitsDamage &= ~DMG_SHOCK; - ffound = TRUE; - } - } - - pev->punchangle.x = -2; - - if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75) - { - // first time we take major damage... - // turn automedic on if not on - SetSuitUpdate("!HEV_MED1", FALSE, SUIT_NEXT_IN_30MIN); // automedic on - - // give morphine shot if not given recently - SetSuitUpdate("!HEV_HEAL7", FALSE, SUIT_NEXT_IN_30MIN); // morphine shot - } - - if (fTookDamage && !ftrivial && fcritical && flHealthPrev < 75) - { - - // already took major damage, now it's critical... - if (pev->health < 6) - SetSuitUpdate("!HEV_HLTH3", FALSE, SUIT_NEXT_IN_10MIN); // near death - else if (pev->health < 20) - SetSuitUpdate("!HEV_HLTH2", FALSE, SUIT_NEXT_IN_10MIN); // health critical - - // give critical health warnings - if (!RANDOM_LONG(0,3) && flHealthPrev < 50) - SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention - } - - // if we're taking time based damage, warn about its continuing effects - if (fTookDamage && (bitsDamageType & DMG_TIMEBASED) && flHealthPrev < 75) - { - if (flHealthPrev < 50) - { - if (!RANDOM_LONG(0,3)) - SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention - } - else - SetSuitUpdate("!HEV_HLTH1", FALSE, SUIT_NEXT_IN_10MIN); // health dropping - } - - //return fTookDamage; - int theReturnValue = 0; - if(fTookDamage) - { - theReturnValue = flDamage; - } - return theReturnValue; -} - -//========================================================= -// PackDeadPlayerItems - call this when a player dies to -// pack up the appropriate weapons and ammo items, and to -// destroy anything that shouldn't be packed. -// -// This is pretty brute force :( -//========================================================= -void CBasePlayer::PackDeadPlayerItems( void ) -{ - int iWeaponRules; - int iAmmoRules; - int i; - CBasePlayerWeapon *rgpPackWeapons[ 20 ];// 20 hardcoded for now. How to determine exactly how many weapons we have? - int iPackAmmo[ MAX_AMMO_SLOTS + 1]; - int iPW = 0;// index into packweapons array - int iPA = 0;// index into packammo array - - memset(rgpPackWeapons, NULL, sizeof(rgpPackWeapons) ); - memset(iPackAmmo, -1, sizeof(iPackAmmo) ); - - // get the game rules - iWeaponRules = g_pGameRules->DeadPlayerWeapons( this ); - iAmmoRules = g_pGameRules->DeadPlayerAmmo( this ); - - if ( iWeaponRules == GR_PLR_DROP_GUN_NO && iAmmoRules == GR_PLR_DROP_AMMO_NO ) - { - // nothing to pack. Remove the weapons and return. Don't call create on the box! - RemoveAllItems( TRUE ); - return; - } - -// go through all of the weapons and make a list of the ones to pack - for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - if ( m_rgpPlayerItems[ i ] ) - { - // there's a weapon here. Should I pack it? - CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[ i ]; - - while ( pPlayerItem ) - { - switch( iWeaponRules ) - { - case GR_PLR_DROP_GUN_ACTIVE: - if ( m_pActiveItem && pPlayerItem == m_pActiveItem ) - { - // this is the active item. Pack it. - rgpPackWeapons[ iPW++ ] = (CBasePlayerWeapon *)pPlayerItem; - } - break; - - case GR_PLR_DROP_GUN_ALL: - rgpPackWeapons[ iPW++ ] = (CBasePlayerWeapon *)pPlayerItem; - break; - - default: - break; - } - - pPlayerItem = pPlayerItem->m_pNext; - } - } - } - -// now go through ammo and make a list of which types to pack. - if ( iAmmoRules != GR_PLR_DROP_AMMO_NO ) - { - for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) - { - if ( m_rgAmmo[ i ] > 0 ) - { - // player has some ammo of this type. - switch ( iAmmoRules ) - { - case GR_PLR_DROP_AMMO_ALL: - iPackAmmo[ iPA++ ] = i; - break; - - case GR_PLR_DROP_AMMO_ACTIVE: - if ( m_pActiveItem && i == m_pActiveItem->PrimaryAmmoIndex() ) - { - // this is the primary ammo type for the active weapon - iPackAmmo[ iPA++ ] = i; - } - else if ( m_pActiveItem && i == m_pActiveItem->SecondaryAmmoIndex() ) - { - // this is the secondary ammo type for the active weapon - iPackAmmo[ iPA++ ] = i; - } - break; - - default: - break; - } - } - } - } - -// create a box to pack the stuff into. - CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin, pev->angles, edict() ); - - pWeaponBox->pev->angles.x = 0;// don't let weaponbox tilt. - pWeaponBox->pev->angles.z = 0; - - pWeaponBox->SetThink( &CWeaponBox::Kill ); - pWeaponBox->pev->nextthink = gpGlobals->time + 120; - -// back these two lists up to their first elements - iPA = 0; - iPW = 0; - -// pack the ammo - while ( iPackAmmo[ iPA ] != -1 ) - { - // Only pack items with ammo - const char* theAmmoName = CBasePlayerItem::AmmoInfoArray[ iPackAmmo[ iPA ] ].pszName; - if(theAmmoName && theAmmoName != "") - pWeaponBox->PackAmmo( MAKE_STRING(theAmmoName), m_rgAmmo[ iPackAmmo[ iPA ] ] ); - - iPA++; - } - -// now pack all of the items in the lists - while ( rgpPackWeapons[ iPW ] ) - { - // weapon unhooked from the player. Pack it into der box. - pWeaponBox->PackWeapon( rgpPackWeapons[ iPW ] ); - - iPW++; - } - - pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some. - - RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above. -} - -void CBasePlayer::DestroyAllItems(BOOL removeSuit) -{ - if (m_pActiveItem) - { - ResetAutoaim( ); - m_pActiveItem->Holster( ); - m_pActiveItem = NULL; - } - - m_pLastItem = NULL; - - int i; - CBasePlayerItem *pPendingItem; - for (i = 0; i < MAX_ITEM_TYPES; i++) - { - m_pActiveItem = m_rgpPlayerItems[i]; - while (m_pActiveItem) - { - pPendingItem = m_pActiveItem->m_pNext; - m_pActiveItem->DestroyItem(); - m_pActiveItem = pPendingItem; - } - m_rgpPlayerItems[i] = NULL; - } - m_pActiveItem = NULL; - - pev->viewmodel = 0; - pev->weaponmodel = 0; - - if ( removeSuit ) - pev->weapons = 0; - else - pev->weapons &= ~WEAPON_ALLWEAPONS; - - for ( i = 0; i < MAX_AMMO_SLOTS;i++) - m_rgAmmo[i] = 0; - - // send Selected Weapon Message to our client - NetMsg_CurWeapon( pev, 0, 0, 0 ); -} - -void CBasePlayer::RemoveAllItems( BOOL removeSuit ) -{ - if (m_pActiveItem) - { - ResetAutoaim( ); - m_pActiveItem->Holster( ); - m_pActiveItem = NULL; - } - - m_pLastItem = NULL; - - int i; - CBasePlayerItem *pPendingItem; - for (i = 0; i < MAX_ITEM_TYPES; i++) - { - m_pActiveItem = m_rgpPlayerItems[i]; - while (m_pActiveItem) - { - pPendingItem = m_pActiveItem->m_pNext; - m_pActiveItem->Drop( ); - m_pActiveItem = pPendingItem; - } - m_rgpPlayerItems[i] = NULL; - } - m_pActiveItem = NULL; - - pev->viewmodel = 0; - pev->weaponmodel = 0; - - if ( removeSuit ) - pev->weapons = 0; - else - pev->weapons &= ~WEAPON_ALLWEAPONS; - - for ( i = 0; i < MAX_AMMO_SLOTS;i++) - m_rgAmmo[i] = 0; - -// UpdateClientData(); -// // send Selected Weapon Message to our client -// MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev ); -// WRITE_BYTE(0); -// WRITE_BYTE(0); -// WRITE_BYTE(0); -// MESSAGE_END(); -} - -/* - * GLOBALS ASSUMED SET: g_ulModelIndexPlayer - * - * ENTITY_METHOD(PlayerDie) - */ -entvars_t *g_pevLastInflictor; // Set in combat.cpp. Used to pass the damage inflictor for death messages. - // Better solution: Add as parameter to all Killed() functions. - -void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) -{ - CSound *pSound; - - // Holster weapon immediately, to allow it to cleanup - if ( m_pActiveItem ) - m_pActiveItem->Holster( ); - - GetGameRules()->PlayerKilled( this, pevAttacker, g_pevLastInflictor ); - - if ( m_pTank != NULL ) - { - m_pTank->Use( this, this, USE_OFF, 0 ); - m_pTank = NULL; - } - - // this client isn't going to be thinking for a while, so reset the sound until they respawn - pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) ); - { - if ( pSound ) - { - pSound->Reset(); - } - } - - SetAnimation( PLAYER_DIE ); - - m_iRespawnFrames = 0; - - //pev->modelindex = g_ulModelIndexPlayer; // don't use eyes - - // Clear buttons held down on death, fixes bug where player can't switch observer targets - m_afButtonLast = 0; - - pev->deadflag = DEAD_DYING; - pev->movetype = MOVETYPE_TOSS; - ClearBits( pev->flags, FL_ONGROUND ); - if (pev->velocity.z < 10) - pev->velocity.z += RANDOM_FLOAT(0,300); - - // clear out the suit message cache so we don't keep chattering - SetSuitUpdate(NULL, FALSE, 0); - - // send "health" update message to zero - m_iClientHealth = 0; - NetMsg_Health( pev, m_iClientHealth ); - - // Tell Ammo Hud that the player is dead - NetMsg_CurWeapon( pev, 0, 0xFF, 0xFF ); - - // reset FOV - pev->fov = m_iFOV = m_iClientFOV = 0; - NetMsg_SetFOV( pev, 0 ); - - - // UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12 - // UTIL_ScreenFade( edict(), Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE ); - - if ( ( pev->health < -40 && iGib != GIB_NEVER ) || iGib == GIB_ALWAYS ) - { - pev->solid = SOLID_NOT; - GibMonster(); // This clears pev->model - pev->effects |= EF_NODRAW; - return; - } - - DeathSound(); - - pev->angles.x = 0; - pev->angles.z = 0; - - SetThink(&CBasePlayer::PlayerDeathThink); - pev->nextthink = gpGlobals->time + 0.1; -} - -void CBasePlayer::Suicide(void) -{ - AvHPlayer* thePlayer = dynamic_cast(this); - - ASSERT(thePlayer); - - if(thePlayer && thePlayer->GetUsedKilled())//voogru: prevent exploitation of "kill" command. - return; - - // have the player kill themself - float theKillDelay = CVAR_GET_FLOAT(kvKillDelay); - - #ifdef DEBUG - #ifndef AVH_EXTERNAL_BUILD - theKillDelay = 0; - #endif - #endif - - if(theKillDelay > 0.0f) - { - char theMessage[256]; - sprintf(theMessage, "Suiciding in %.1f seconds...\n", theKillDelay); - UTIL_SayText(theMessage, this, ENTINDEX(this->edict())); - } - - thePlayer->SetUsedKilled(true); - SetThink(&CBasePlayer::SuicideThink); - this->pev->nextthink = gpGlobals->time + theKillDelay; -} - -void CBasePlayer::SuicideThink(void) -{ - AvHPlayer* thePlayer = dynamic_cast(this); - if(thePlayer && thePlayer->GetCanBeAffectedByEnemies()) - { - this->pev->health = 0; - this->Killed(this->pev, GIB_NEVER); - } -} - -// Set the activity based on an event or current state -void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim ) -{ - int animDesired; - int gaitDesired; - float speed; - char szAnim[64]; - bool theFoundAnim = true; - int theDebugAnimations = 0; // BALANCE_VAR(kDebugAnimations); - bool reloadAnim = false; - - // Make sure the model is set, as gestating models aren't set before the animation starts playing - AvHPlayer* theAvHPlayer = dynamic_cast(this); - ASSERT(theAvHPlayer); - theAvHPlayer->SetModelFromState(); - - speed = pev->velocity.Length2D(); - - if (pev->flags & FL_FROZEN) - { - speed = 0; - playerAnim = PLAYER_IDLE; - } - - switch (playerAnim) - { - case PLAYER_JUMP: - m_IdealActivity = ACT_HOP; - break; - - case PLAYER_SUPERJUMP: - m_IdealActivity = ACT_LEAP; - break; - - case PLAYER_DIE: - m_IdealActivity = ACT_DIESIMPLE; - m_IdealActivity = GetDeathActivity( ); - - //this->GetAnimationForActivity(this->m_IdealActivity, szAnim); - //animDesired = LookupSequence( szAnim ); - //if (animDesired == -1) - //{ - // animDesired = 0; - //} - //this->pev->sequence = animDesired; - break; - - case PLAYER_ATTACK1: - switch( m_Activity ) - { - case ACT_HOVER: - case ACT_SWIM: - case ACT_HOP: - case ACT_LEAP: - case ACT_DIESIMPLE: - m_IdealActivity = m_Activity; - break; - default: - m_IdealActivity = ACT_RANGE_ATTACK1; - break; - } - break; - - case PLAYER_PRIME: - switch( m_Activity ) - { - case ACT_HOVER: - case ACT_SWIM: - case ACT_HOP: - case ACT_LEAP: - case ACT_DIESIMPLE: - m_IdealActivity = m_Activity; - break; - default: - m_IdealActivity = ACT_RANGE_PRIME; - break; - } - break; - - case PLAYER_RELOAD: - switch( m_Activity ) - { - case ACT_HOVER: - case ACT_SWIM: - case ACT_HOP: - case ACT_LEAP: - case ACT_DIESIMPLE: - m_IdealActivity = m_Activity; - break; - default: - m_IdealActivity = ACT_RELOAD; - break; - } - break; - case PLAYER_RELOAD_START: - switch( m_Activity ) - { - case ACT_HOVER: - case ACT_SWIM: - case ACT_HOP: - case ACT_LEAP: - case ACT_DIESIMPLE: - m_IdealActivity = m_Activity; - break; - default: - m_IdealActivity = ACT_RELOAD_START; - break; - } - break; - case PLAYER_RELOAD_INSERT: - switch( m_Activity ) - { - case ACT_HOVER: - case ACT_SWIM: - case ACT_HOP: - case ACT_LEAP: - case ACT_DIESIMPLE: - m_IdealActivity = m_Activity; - break; - default: - m_IdealActivity = ACT_RELOAD_INSERT; - break; - } - break; - case PLAYER_RELOAD_END: - switch( m_Activity ) - { - case ACT_HOVER: - case ACT_SWIM: - case ACT_HOP: - case ACT_LEAP: - case ACT_DIESIMPLE: - m_IdealActivity = m_Activity; - break; - default: - m_IdealActivity = ACT_RELOAD_END; - break; - } - break; - case PLAYER_IDLE: - case PLAYER_WALK: - if ( !FBitSet( pev->flags, FL_ONGROUND ) && (m_Activity == ACT_HOP || m_Activity == ACT_LEAP) ) // Still jumping - { - m_IdealActivity = m_Activity; - } - else if ( pev->waterlevel > 1 ) - { - if ( speed == 0 ) - m_IdealActivity = ACT_HOVER; - else - m_IdealActivity = ACT_SWIM; - } - else - { - if((playerAnim == PLAYER_IDLE) && !strcmp(this->m_szAnimExtention, "") && (theAvHPlayer->GetPlayMode() != PLAYMODE_READYROOM)) - { - m_IdealActivity = ACT_IDLE; - } - else - { - m_IdealActivity = ACT_WALK; - } - } - break; - } - - switch (m_IdealActivity) - { - - case ACT_DIESIMPLE: - case ACT_DIEBACKWARD: - case ACT_DIEFORWARD: - case ACT_DIEVIOLENT: - default: - if ( m_Activity == m_IdealActivity) - return; - m_Activity = m_IdealActivity; - - //animDesired = LookupActivity( m_Activity ); - this->GetAnimationForActivity(this->m_Activity, szAnim); - animDesired = LookupSequence( szAnim ); - - // Already using the desired animation? - if (pev->sequence == animDesired) - return; - - #ifdef DEBUG - if(theDebugAnimations) - { - char theMessage[256]; - sprintf(theMessage, "Setting full-body animation (%s): %d (no gaitsequence)\n", szAnim, animDesired); - ALERT(at_console, theMessage); - } - #endif - - pev->gaitsequence = 0; - pev->sequence = animDesired; - pev->frame = 0; - ResetSequenceInfo( ); - return; - - // create seperate case for these so that they don't interfere with a reload - Elven - case ACT_HOVER: - case ACT_LEAP: - case ACT_SWIM: - case ACT_HOP: - reloadAnim = (m_Activity == ACT_RELOAD) || (m_Activity == ACT_RELOAD_START) || (m_Activity == ACT_RELOAD_INSERT) || (m_Activity == ACT_RELOAD_END); - if ( m_Activity == m_IdealActivity || reloadAnim) - return; - m_Activity = m_IdealActivity; - - //animDesired = LookupActivity( m_Activity ); - this->GetAnimationForActivity(this->m_Activity, szAnim); - animDesired = LookupSequence( szAnim ); - - // Already using the desired animation? - if (pev->sequence == animDesired) - return; - pev->gaitsequence = 0; - pev->sequence = animDesired; - pev->frame = 0; - ResetSequenceInfo( ); - return; - - - case ACT_RANGE_ATTACK1: - case ACT_RANGE_PRIME: - this->GetAnimationForActivity(this->m_IdealActivity, szAnim); - animDesired = LookupSequence( szAnim ); - - if (animDesired == -1) - { - animDesired = 0; - theFoundAnim = false; - } - - if ( pev->sequence != animDesired || !m_fSequenceLoops ) - { - pev->frame = 0; - } - - if (!m_fSequenceLoops) - { - pev->effects |= EF_NOINTERP; - } - - m_Activity = m_IdealActivity; - - if(theFoundAnim) - { - #ifdef DEBUG - if(theDebugAnimations) - { - char theMessage[256]; - sprintf(theMessage, "%s%s\n", "Setting attack animation: ", szAnim); - ALERT(at_console, theMessage); - } - #endif - } - - pev->sequence = animDesired; - ResetSequenceInfo( ); - break; - - case ACT_RELOAD: - case ACT_RELOAD_START: - case ACT_RELOAD_INSERT: - case ACT_RELOAD_END: - this->GetAnimationForActivity(this->m_IdealActivity, szAnim); - animDesired = LookupSequence( szAnim ); - - if ( pev->sequence != animDesired || !m_fSequenceLoops ) - { - pev->frame = 0; - } - m_Activity = m_IdealActivity; - break; - - - - case ACT_WALK: - reloadAnim = (m_Activity == ACT_RELOAD) || (m_Activity == ACT_RELOAD_START) || - (m_Activity == ACT_RELOAD_INSERT) || (m_Activity == ACT_RELOAD_END); - if ((m_Activity != ACT_RANGE_ATTACK1 && m_Activity != ACT_RANGE_PRIME && !reloadAnim/*m_Activity != ACT_RELOAD*/ ) || m_fSequenceFinished) - { - this->GetAnimationForActivity(this->m_IdealActivity, szAnim); - animDesired = LookupSequence( szAnim ); - if (animDesired == -1) - { - animDesired = 0; - } - m_Activity = ACT_WALK; - } - else - { - animDesired = pev->sequence; - } - break; - } - - // Now handle gaitsequence - if(FBitSet(this->pev->flags, FL_DUCKING)) - { - if(speed == 0) - { - this->GetAnimationForActivity(ACT_CROUCHIDLE, szAnim, true); - } - else - { - this->GetAnimationForActivity(ACT_CROUCH, szAnim, true); - } - } - else if (speed > this->GetMaxWalkSpeed() ) - { - this->GetAnimationForActivity(ACT_RUN, szAnim, true); - } - else if (speed > 0) - { - this->GetAnimationForActivity(ACT_WALK, szAnim, true); - } - else - { - this->GetAnimationForActivity(ACT_IDLE, szAnim, true); - } - - // Set the gaitsequence - gaitDesired = LookupSequence(szAnim, 1); - if(gaitDesired == -1) - { - gaitDesired = 0; - } - -#ifdef DEBUG - if(theDebugAnimations) - { - if((this->pev->gaitsequence != gaitDesired) || (this->pev->sequence != animDesired)) - { - ALERT(at_console, "Anim desired (%s): %d, gaitsequence: %d gaitdesired: %d \n", szAnim, animDesired, pev->gaitsequence, gaitDesired); - } - } -#endif - - this->pev->gaitsequence = gaitDesired; - - AvHPlayer* thePlayer = dynamic_cast(this); - if(thePlayer && (thePlayer->GetPlayMode() == PLAYMODE_READYROOM)) - { - // Play some animation on top and bottom when not holding any weapons - animDesired = gaitDesired; - } - - // Already using the desired animation? - if (pev->sequence == animDesired) - return; - - // Reset to first frame of desired animation - pev->sequence = animDesired; - pev->frame = 0; - ResetSequenceInfo( ); -} - -/* -=========== -TabulateAmmo -This function is used to find and store -all the ammo we have into the ammo vars. -============ -*/ -void CBasePlayer::TabulateAmmo() -{ - ammo_9mm = AmmoInventory( GetAmmoIndex( "9mm" ) ); - ammo_357 = AmmoInventory( GetAmmoIndex( "357" ) ); - ammo_argrens = AmmoInventory( GetAmmoIndex( "ARgrenades" ) ); - ammo_bolts = AmmoInventory( GetAmmoIndex( "bolts" ) ); - ammo_buckshot = AmmoInventory( GetAmmoIndex( "buckshot" ) ); - ammo_rockets = AmmoInventory( GetAmmoIndex( "rockets" ) ); - ammo_uranium = AmmoInventory( GetAmmoIndex( "uranium" ) ); - ammo_hornets = AmmoInventory( GetAmmoIndex( "Hornets" ) ); -} - - -/* -=========== -WaterMove -============ -*/ -#define AIRTIME 12 // lung full of air lasts this many seconds - -void CBasePlayer::WaterMove() -{ - int air; - - if (pev->movetype == MOVETYPE_NOCLIP) - return; - - if (pev->health < 0) - return; - - // waterlevel 0 - not in water - // waterlevel 1 - feet in water - // waterlevel 2 - waist in water - // waterlevel 3 - head in water - - if (pev->waterlevel != 3) - { - // not underwater - - // play 'up for air' sound - if (pev->air_finished < gpGlobals->time) - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade1.wav", 1, ATTN_NORM); - else if (pev->air_finished < gpGlobals->time + 9) - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade2.wav", 1, ATTN_NORM); - - pev->air_finished = gpGlobals->time + AIRTIME; - pev->dmg = 2; - - // if we took drowning damage, give it back slowly - if (m_idrowndmg > m_idrownrestored) - { - // set drowning damage bit. hack - dmg_drownrecover actually - // makes the time based damage code 'give back' health over time. - // make sure counter is cleared so we start count correctly. - - // NOTE: this actually causes the count to continue restarting - // until all drowning damage is healed. - - m_bitsDamageType |= DMG_DROWNRECOVER; - m_bitsDamageType &= ~DMG_DROWN; - m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; - } - - } - else - { // fully under water - // stop restoring damage while underwater - m_bitsDamageType &= ~DMG_DROWNRECOVER; - m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; - - if (pev->air_finished < gpGlobals->time) // drown! - { - if (pev->pain_finished < gpGlobals->time) - { - // take drowning damage - pev->dmg += 1; - if (pev->dmg > 5) - pev->dmg = 5; - TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), pev->dmg, DMG_DROWN); - pev->pain_finished = gpGlobals->time + 1; - - // track drowning damage, give it back when - // player finally takes a breath - - m_idrowndmg += pev->dmg; - } - } - else - { - m_bitsDamageType &= ~DMG_DROWN; - } - } - - if (!pev->waterlevel) - { - if (FBitSet(pev->flags, FL_INWATER)) - { - ClearBits(pev->flags, FL_INWATER); - } - return; - } - - // make bubbles - - air = (int)(pev->air_finished - gpGlobals->time); - if (!RANDOM_LONG(0,0x1f) && RANDOM_LONG(0,AIRTIME-1) >= air) - { - switch (RANDOM_LONG(0,3)) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim1.wav", 0.8, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim2.wav", 0.8, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim3.wav", 0.8, ATTN_NORM); break; - case 3: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim4.wav", 0.8, ATTN_NORM); break; - } - } - - if (pev->watertype == CONTENT_LAVA) // do damage - { - if (pev->dmgtime < gpGlobals->time) - TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 10 * pev->waterlevel, DMG_BURN); - } - else if (pev->watertype == CONTENT_SLIME) // do damage - { - pev->dmgtime = gpGlobals->time + 1; - TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 4 * pev->waterlevel, DMG_ACID); - } - - if (!FBitSet(pev->flags, FL_INWATER)) - { - SetBits(pev->flags, FL_INWATER); - pev->dmgtime = 0; - } -} - -void CBasePlayer::InitPlayerFromSpawn(edict_t* inSpawn) -{ - if(!FNullEnt(inSpawn)) - { - int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(this->pev->iuser3)); - this->pev->origin = VARS(inSpawn)->origin + Vector(0, 0, theOffset); - - this->pev->v_angle = VARS(inSpawn)->v_angle; - this->pev->velocity = g_vecZero; - this->pev->angles = VARS(inSpawn)->angles; - this->pev->punchangle = g_vecZero; - this->pev->fixangle = TRUE; - } -} - -// TRUE if the player is attached to a ladder -BOOL CBasePlayer::IsOnLadder( void ) -{ - return ( pev->movetype == MOVETYPE_FLY ); -} - -void CBasePlayer::PlayerDeathThink(void) -{ - float flForward; - - if (FBitSet(pev->flags, FL_ONGROUND)) - { - flForward = pev->velocity.Length() - 20; - if (flForward <= 0) - pev->velocity = g_vecZero; - else - pev->velocity = flForward * pev->velocity.Normalize(); - } - - if ( HasWeapons() ) - { - // we drop the guns here because weapons that have an area effect and can kill their user - // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the - // player class sometimes is freed. It's safer to manipulate the weapons once we know - // we aren't calling into any of their code anymore through the player pointer. - PackDeadPlayerItems(); - - // Assumes that all players have at least one weapon when they die - //SpawnClientSideCorpse(); - } - - if (pev->modelindex && (!m_fSequenceFinished) && (pev->deadflag == DEAD_DYING)) - { - StudioFrameAdvance( ); - - m_iRespawnFrames++; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands - if ( m_iRespawnFrames < 120 ) // Animations should be no longer than this - return; - } - - // once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore - // this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn - if ( pev->movetype != MOVETYPE_NONE && FBitSet(pev->flags, FL_ONGROUND) ) - pev->movetype = MOVETYPE_NONE; - - if (pev->deadflag == DEAD_DYING) - pev->deadflag = DEAD_DEAD; - - StopAnimation(); - - pev->effects |= EF_NOINTERP; - pev->framerate = 0.0; - - BOOL fAnyButtonDown = (pev->button & ~IN_SCORE ); - - // wait for all buttons released - if (pev->deadflag == DEAD_DEAD) - { - //if (fAnyButtonDown) - // return; - - //if ( g_pGameRules->FPlayerCanRespawn( this ) ) - //{ - m_fDeadTime = gpGlobals->time; - pev->deadflag = DEAD_RESPAWNABLE; - //} - - return; - } - -// if the player has been dead for one second longer than allowed by forcerespawn, -// forcerespawn isn't on. Send the player off to an intermission camera until they -// choose to respawn. - //if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->time > (m_fDeadTime + 6) ) && !(m_afPhysicsFlags & PFLAG_OBSERVER) ) - //{ - // // go to dead camera. - // StartDeathCam(); - //} - - - // From Counter-strike - // if the player has been dead for one second longer than allowed by forcerespawn, - // forcerespawn isn't on. Send the player off to an intermission camera until they - // choose to respawn. -// if ( g_pGameRules->IsMultiplayer() && -// ( gpGlobals->time > (m_fDeadTime + 3) ) && -// !( m_afPhysicsFlags & PFLAG_OBSERVER) ) -// { -// //Send message to everybody to spawn a corpse. -// SpawnClientSideCorpse(); -// -// // go to dead camera. -// //StartDeathCam(); -// } - - const float kMinDeathTime = .5f; - -// wait for any button down, or mp_forcerespawn is set and the respawn time is up - if ((!fAnyButtonDown || ( gpGlobals->time < (m_fDeadTime + kMinDeathTime) )) - && !( g_pGameRules->IsMultiplayer() && forcerespawn.value > 0 && (gpGlobals->time > (m_fDeadTime + kMinDeathTime))) ) - return; - - pev->button = 0; - m_iRespawnFrames = 0; - - // Player is done with the death cam, continue - AvHPlayer* thePlayer = dynamic_cast(this); - AvHGamerules* theGameRules = dynamic_cast(g_pGameRules); - - theGameRules->PlayerDeathEnd(thePlayer); -} - -//========================================================= -// StartDeathCam - find an intermission spot and send the -// player off into observer mode -//========================================================= -void CBasePlayer::StartDeathCam( void ) -{ - edict_t *pSpot, *pNewSpot; - int iRand; - - if ( pev->view_ofs == g_vecZero ) - { - // don't accept subsequent attempts to StartDeathCam() - return; - } - - pSpot = FIND_ENTITY_BY_CLASSNAME( NULL, "info_intermission"); - - if ( !FNullEnt( pSpot ) ) - { - // at least one intermission spot in the world. - iRand = RANDOM_LONG( 0, 3 ); - - while ( iRand > 0 ) - { - pNewSpot = FIND_ENTITY_BY_CLASSNAME( pSpot, "info_intermission"); - - if ( pNewSpot ) - { - pSpot = pNewSpot; - } - - iRand--; - } - - CopyToBodyQue( pev ); - StartObserver( pSpot->v.origin, pSpot->v.v_angle ); - } - else - { - // no intermission spot. Push them up in the air, looking down at their corpse - TraceResult tr; - CopyToBodyQue( pev ); - UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, 128 ), ignore_monsters, edict(), &tr ); - StartObserver( tr.vecEndPos, UTIL_VecToAngles( tr.vecEndPos - pev->origin ) ); - return; - } -} - -void CBasePlayer::StartObserver( Vector vecPosition, Vector vecViewAngle ) -{ -// m_afPhysicsFlags |= PFLAG_OBSERVER; -// -// pev->view_ofs = g_vecZero; -// pev->angles = pev->v_angle = vecViewAngle; -// pev->fixangle = TRUE; -// pev->solid = SOLID_NOT; -// pev->takedamage = DAMAGE_NO; -// pev->movetype = MOVETYPE_NONE; -// pev->modelindex = 0; -// UTIL_SetOrigin( pev, vecPosition ); - - m_iHideHUD |= (HIDEHUD_HEALTH | HIDEHUD_WEAPONS); - m_afPhysicsFlags |= PFLAG_OBSERVER; - - pev->effects = EF_NODRAW; - pev->view_ofs = g_vecZero; - pev->angles = pev->v_angle = vecViewAngle; - pev->fixangle = TRUE; - pev->solid = SOLID_NOT; - pev->takedamage = DAMAGE_NO; - pev->movetype = MOVETYPE_NONE; - UTIL_SetOrigin( pev, vecPosition ); - - ClearBits( m_afPhysicsFlags, PFLAG_DUCKING ); - ClearBits( pev->flags, FL_DUCKING ); - // pev->flags = FL_CLIENT | FL_SPECTATOR; // Should we set Spectator flag? Or is it reserver for people connecting with spectator 1? - pev->deadflag = DEAD_RESPAWNABLE; - - // Tell the physics code that this player's now in observer mode - Observer_SetMode(this->GetDefaultSpectatingMode()); - Observer_SpectatePlayer(this->GetDefaultSpectatingTarget()); - m_flNextObserverInput = 0; -} - -void CBasePlayer::StopObserver() -{ - this->pev->solid = SOLID_SLIDEBOX; - this->pev->effects = 0; - this->pev->takedamage = DAMAGE_YES; - this->pev->movetype = MOVETYPE_WALK; - ClearBits(this->m_afPhysicsFlags, PFLAG_OBSERVER); - ClearBits(this->m_afPhysicsFlags, PFLAG_DUCKING ); - ClearBits(this->pev->flags, FL_DUCKING ); - this->pev->iuser1 = this->pev->iuser2 = 0; -} - -// -// PlayerUse - handles USE keypress -// -#define PLAYER_SEARCH_RADIUS (float)64 - -void CBasePlayer::ClearKeys() -{ - // clear attack/use commands from player - this->m_afButtonPressed = 0; - this->pev->button = 0; - this->m_afButtonReleased = 0; -} - -void CBasePlayer::PlayerUse ( void ) -{ - AvHPlayer* theAvHPlayer = dynamic_cast(this); - - // Was use pressed or released? - if ( ! ((pev->button | m_afButtonPressed | m_afButtonReleased) & IN_USE) ) - return; - - //voogru: Dont do this on commanders to prevent phantom use sounds. - if(theAvHPlayer->GetIsInTopDownMode()) - return; - - // Hit Use on a train? - if ( m_afButtonPressed & IN_USE ) - { - if ( m_pTank != NULL ) - { - // Stop controlling the tank - // TODO: Send HUD Update - m_pTank->Use( this, this, USE_OFF, 0 ); - m_pTank = NULL; - return; - } - else - { - if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) - { - m_afPhysicsFlags &= ~PFLAG_ONTRAIN; - m_iTrain = TRAIN_NEW|TRAIN_OFF; - return; - } - else - { // Start controlling the train! - CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); - - if ( pTrain && !(pev->button & IN_JUMP) && FBitSet(pev->flags, FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(pev) ) - { - m_afPhysicsFlags |= PFLAG_ONTRAIN; - m_iTrain = TrainSpeed(pTrain->pev->speed, pTrain->pev->impulse); - m_iTrain |= TRAIN_NEW; - EMIT_SOUND( ENT(pev), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM); - return; - } - } - } - } - - CBaseEntity *pObject = NULL; - CBaseEntity *pClosest = NULL; - Vector vecLOS; - float flMaxDot = VIEW_FIELD_NARROW; - float flDot; - - UTIL_MakeVectors ( pev->v_angle );// so we know which way we are facing - - while ((pObject = UTIL_FindEntityInSphere( pObject, pev->origin, PLAYER_SEARCH_RADIUS )) != NULL) - { - - if (pObject->ObjectCaps() & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE)) - { - // !!!PERFORMANCE- should this check be done on a per case basis AFTER we've determined that - // this object is actually usable? This dot is being done for every object within PLAYER_SEARCH_RADIUS - // when player hits the use key. How many objects can be in that area, anyway? (sjb) - vecLOS = (VecBModelOrigin( pObject->pev ) - (pev->origin + pev->view_ofs)); - - // This essentially moves the origin of the target to the corner nearest the player to test to see - // if it's "hull" is in the view cone - vecLOS = UTIL_ClampVectorToBox( vecLOS, pObject->pev->size * 0.5 ); - - flDot = DotProduct (vecLOS , gpGlobals->v_forward); - if (flDot > flMaxDot ) - {// only if the item is in front of the user - pClosest = pObject; - flMaxDot = flDot; -// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); - } -// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); - } - } - pObject = pClosest; - - // Found an object - if (pObject ) - { - //!!!UNDONE: traceline here to prevent USEing buttons through walls - int caps = pObject->ObjectCaps(); - - if ( m_afButtonPressed & IN_USE ) - { - //EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_select.wav", 0.4, ATTN_NORM); - const char* theSound = AvHSHUGetCommonSoundName(theAvHPlayer->GetIsAlien(), WEAPON_SOUND_SELECT); - EMIT_SOUND( ENT(pev), CHAN_ITEM, theSound, 0.4, ATTN_NORM); - } - - if ( ( (pev->button & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) || - ( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) ) - { - if ( caps & FCAP_CONTINUOUS_USE ) - m_afPhysicsFlags |= PFLAG_USING; - - pObject->Use( this, this, USE_SET, 1 ); - } - // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away - else if ( (m_afButtonReleased & IN_USE) && (pObject->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use - { - pObject->Use( this, this, USE_SET, 0 ); - } - } - else - { - if ( m_afButtonPressed & IN_USE ) - { - //EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_denyselect.wav", 0.4, ATTN_NORM); - const char* theSound = AvHSHUGetCommonSoundName(theAvHPlayer->GetIsAlien(), WEAPON_SOUND_DENYSELECT); - EMIT_SOUND( ENT(pev), CHAN_ITEM, theSound, 0.4, ATTN_NORM); - } - } -} - - - -void CBasePlayer::Jump() -{ - Vector vecWallCheckDir;// direction we're tracing a line to find a wall when walljumping - Vector vecAdjustedVelocity; - Vector vecSpot; - TraceResult tr; - - if (FBitSet(pev->flags, FL_WATERJUMP)) - return; - - if (pev->waterlevel >= 2) - { - return; - } - - // jump velocity is sqrt( height * gravity * 2) - - // If this isn't the first frame pressing the jump button, break out. - if ( !FBitSet( m_afButtonPressed, IN_JUMP ) ) - return; // don't pogo stick - - if ( !(pev->flags & FL_ONGROUND) || !pev->groundentity ) - { - return; - } - -// many features in this function use v_forward, so makevectors now. - UTIL_MakeVectors (pev->angles); - - // ClearBits(pev->flags, FL_ONGROUND); // don't stairwalk - - SetAnimation( PLAYER_JUMP ); - - if ( m_fLongJump && - (pev->button & IN_DUCK) && - ( pev->flDuckTime > 0 ) && - pev->velocity.Length() > 50 ) - { - SetAnimation( PLAYER_SUPERJUMP ); - } - - // If you're standing on a conveyor, add it's velocity to yours (for momentum) - entvars_t *pevGround = VARS(pev->groundentity); - if ( pevGround && (pevGround->flags & FL_CONVEYOR) ) - { - pev->velocity = pev->velocity + pev->basevelocity; - } -} - - - -// This is a glorious hack to find free space when you've crouched into some solid space -// Our crouching collisions do not work correctly for some reason and this is easier -// than fixing the problem :( -void FixPlayerCrouchStuck( edict_t *pPlayer ) -{ - TraceResult trace; - - // Move up as many as 18 pixels if the player is stuck. - for ( int i = 0; i < 18; i++ ) - { - UTIL_TraceHull( pPlayer->v.origin, pPlayer->v.origin, dont_ignore_monsters, head_hull, pPlayer, &trace ); - if ( trace.fStartSolid ) - pPlayer->v.origin.z ++; - else - break; - } -} - -void CBasePlayer::Duck( ) -{ - if (pev->button & IN_DUCK) - { - if ( m_IdealActivity != ACT_LEAP ) - { - SetAnimation( PLAYER_WALK ); - } - } -} - -// -// ID's player as such. -// -int CBasePlayer::Classify ( void ) -{ - return CLASS_PLAYER; -} - - -void CBasePlayer::AddPoints( int score, BOOL bAllowNegativeScore ) -{ - // Positive score always adds - if ( score < 0 ) - { - if ( !bAllowNegativeScore ) - { - if ( pev->frags < 0 ) // Can't go more negative - return; - - if ( -score > pev->frags ) // Will this go negative? - { - score = -pev->frags; // Sum will be 0 - } - } - } - - pev->frags += score; -} - -void CBasePlayer::EffectivePlayerClassChanged() -{ -} - -void CBasePlayer::NeedsTeamUpdate() -{ -} - -void CBasePlayer::SendTeamUpdate() -{ -} - -void CBasePlayer::AddPointsToTeam( int score, BOOL bAllowNegativeScore ) -{ - int index = entindex(); - - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); - - if ( pPlayer && i != index ) - { - if ( g_pGameRules->PlayerRelationship( this, pPlayer ) == GR_TEAMMATE ) - { - pPlayer->AddPoints( score, bAllowNegativeScore ); - } - } - } -} - -//Player ID -void CBasePlayer::InitStatusBar() -{ - m_flStatusBarDisappearDelay = 0; - m_SbarString1[0] = m_SbarString0[0] = 0; -} - -void CBasePlayer::UpdateStatusBar() -{ - int newSBarState[ SBAR_END ]; - char sbuf0[ SBAR_STRING_SIZE ]; - char sbuf1[ SBAR_STRING_SIZE ]; - - int i; - - for (i = 0; i < SBAR_END; ++i) - { - newSBarState[i] = -1; - } - - //memset( newSBarState, 0, sizeof(newSBarState) ); - - strcpy( sbuf0, m_SbarString0 ); - strcpy( sbuf1, m_SbarString1 ); - - // Find an ID Target - TraceResult tr; - UTIL_MakeVectors( pev->v_angle + pev->punchangle ); - Vector vecSrc = EyePosition(); - Vector vecEnd = vecSrc + (gpGlobals->v_forward * MAX_ID_RANGE); - UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, edict(), &tr); - - bool theIsCombatMode = GetGameRules()->GetIsCombatMode(); - int theMaxEnumToSend = SBAR_ID_TARGETARMOR; - if(theIsCombatMode) - { - theMaxEnumToSend = SBAR_ID_TARGETLEVEL; - } - - if (tr.flFraction != 1.0) - { - if ( !FNullEnt( tr.pHit ) ) - { - CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); - - if(!pEntity) - return; - - if (pEntity->Classify() == CLASS_PLAYER ) - { - newSBarState[ SBAR_ID_TARGETNAME ] = ENTINDEX( pEntity->edict() ); - - // Name Health: X% Armor: Y% - //strcpy( sbuf1, "1 %p1\n2 Health: %i2%%\n3 Armor: %i3%%" ); - - // (health: X armor: Y) - if(!theIsCombatMode) - { - strcpy( sbuf1, "2 (health: %i2%%\n3 armor: %i3%%)" ); - } - else - { - strcpy( sbuf1, "2 (health: %i2%%\n3 armor: %i3%%\n4 level: %i4)" ); - } - - // allies and medics get to see the targets health - if ( g_pGameRules->PlayerRelationship( this, pEntity ) == GR_TEAMMATE ) - { - int theLevel = 1; - AvHPlayer* thePlayer = dynamic_cast(pEntity); - if(thePlayer) - { - theLevel = thePlayer->GetExperienceLevel(); - } - - //newSBarState[ SBAR_ID_TARGETHEALTH ] = 100 * (pEntity->pev->health / pEntity->pev->max_health); - //newSBarState[ SBAR_ID_TARGETARMOR ] = pEntity->pev->armorvalue; //No need to get it % based since 100 it's the max. - float theHealthPercent = pEntity->pev->health/AvHPlayerUpgrade::GetMaxHealth(pEntity->pev->iuser4, (AvHUser3)pEntity->pev->iuser3, theLevel); - float theArmorPercent = pEntity->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(pEntity->pev->iuser4, (AvHUser3)pEntity->pev->iuser3); - newSBarState[ SBAR_ID_TARGETHEALTH ] = max(theHealthPercent*100+0.5f, 0.0f); - newSBarState[ SBAR_ID_TARGETARMOR ] = max(theArmorPercent*100+0.5f, 0.0f); - newSBarState[ SBAR_ID_TARGETLEVEL ] = theLevel; - } - } - else - { - bool theSuccess = false; - - // Show hive health for friendlies - if(this->pev->team == pEntity->pev->team) - { - AvHUser3 theUser3 = (AvHUser3)pEntity->pev->iuser3; - switch(theUser3) - { - // Place user3s to draw here - case AVH_USER3_HIVE: - theSuccess = true; - break; - } - } - - if(theSuccess) - { - newSBarState[ SBAR_ID_TARGETNAME ] = ENTINDEX( pEntity->edict() ); - - strcpy( sbuf1, "2 (health: %i2%%)\n" ); - - float theHealthPercentage = pEntity->pev->health/pEntity->pev->max_health; - newSBarState[ SBAR_ID_TARGETHEALTH ] = max(theHealthPercentage*100+0.5f, 0.0f); - } - } - - m_flStatusBarDisappearDelay = gpGlobals->time + 1.0; - } - else if ( m_flStatusBarDisappearDelay > gpGlobals->time ) - { - // hold the values for a short amount of time after viewing the object - newSBarState[ SBAR_ID_TARGETNAME ] = m_izSBarState[ SBAR_ID_TARGETNAME ]; - newSBarState[ SBAR_ID_TARGETHEALTH ] = m_izSBarState[ SBAR_ID_TARGETHEALTH ]; - newSBarState[ SBAR_ID_TARGETARMOR ] = m_izSBarState[ SBAR_ID_TARGETARMOR ]; - newSBarState[ SBAR_ID_TARGETLEVEL ] = m_izSBarState[ SBAR_ID_TARGETLEVEL ]; - } - } - - BOOL bForceResend = FALSE; - - if ( strcmp( sbuf0, m_SbarString0 ) ) - { - NetMsg_StatusText( pev, 0, string(sbuf0) ); - strcpy( m_SbarString0, sbuf0 ); - - // make sure everything's resent - bForceResend = TRUE; - } - - if ( strcmp( sbuf1, m_SbarString1 ) ) - { - NetMsg_StatusText( pev, 1, string(sbuf1) ); - strcpy( m_SbarString1, sbuf1 ); - - // make sure everything's resent - bForceResend = TRUE; - } - - // Check values and send if they don't match - for (i = 1; i <= theMaxEnumToSend; i++) - { - if ( newSBarState[i] != m_izSBarState[i] || bForceResend ) - { - NetMsg_StatusValue( pev, i, newSBarState[i] ); - m_izSBarState[i] = newSBarState[i]; - } - } -} - - - - - - - - - -#define CLIMB_SHAKE_FREQUENCY 22 // how many frames in between screen shakes when climbing -#define MAX_CLIMB_SPEED 200 // fastest vertical climbing speed possible -#define CLIMB_SPEED_DEC 15 // climbing deceleration rate -#define CLIMB_PUNCH_X -7 // how far to 'punch' client X axis when climbing -#define CLIMB_PUNCH_Z 7 // how far to 'punch' client Z axis when climbing - -void CBasePlayer::PreThink(void) -{ - int buttonsChanged = (m_afButtonLast ^ pev->button); // These buttons have changed this frame - - // Debounced button codes for pressed/released - // UNDONE: Do we need auto-repeat? - m_afButtonPressed = buttonsChanged & pev->button; // The changed ones still down are "pressed" - m_afButtonReleased = buttonsChanged & (~pev->button); // The ones not down are "released" - - g_pGameRules->PlayerThink( this ); - - if ( g_fGameOver ) - return; // intermission or finale - - UTIL_MakeVectors(pev->v_angle); // is this still used? - - ItemPreFrame( ); - WaterMove(); - - if ( g_pGameRules && g_pGameRules->FAllowFlashlight() ) - m_iHideHUD &= ~HIDEHUD_FLASHLIGHT; - else - m_iHideHUD |= HIDEHUD_FLASHLIGHT; - - - // JOHN: checks if new client data (for HUD and view control) needs to be sent to the client - UpdateClientData(); - - CheckTimeBasedDamage(); - - // Observer Button Handling - if (this->IsObserver() ) - { - Observer_HandleButtons(); - pev->impulse = 0; - return; - } - - CheckSuitUpdate(); - - if (pev->deadflag >= DEAD_DYING) - { - PlayerDeathThink(); - return; - } - - // So the correct flags get sent to client asap. - // - if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) - pev->flags |= FL_ONTRAIN; - else - pev->flags &= ~FL_ONTRAIN; - - // Train speed control - if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) - { - CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); - float vel; - - if ( !pTrain ) - { - TraceResult trainTrace; - // Maybe this is on the other side of a level transition - UTIL_TraceLine( pev->origin, pev->origin + Vector(0,0,-38), ignore_monsters, ENT(pev), &trainTrace ); - - // HACKHACK - Just look for the func_tracktrain classname - if ( trainTrace.flFraction != 1.0 && trainTrace.pHit ) - pTrain = CBaseEntity::Instance( trainTrace.pHit ); - - - if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(pev) ) - { - //ALERT( at_error, "In train mode with no train!\n" ); - m_afPhysicsFlags &= ~PFLAG_ONTRAIN; - m_iTrain = TRAIN_NEW|TRAIN_OFF; - return; - } - } - else if ( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) || (pev->button & (IN_MOVELEFT|IN_MOVERIGHT) ) ) - { - // Turn off the train if you jump, strafe, or the train controls go dead - m_afPhysicsFlags &= ~PFLAG_ONTRAIN; - m_iTrain = TRAIN_NEW|TRAIN_OFF; - return; - } - - pev->velocity = g_vecZero; - vel = 0; - if ( m_afButtonPressed & IN_FORWARD ) - { - vel = 1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } - else if ( m_afButtonPressed & IN_BACK ) - { - vel = -1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } - - if (vel) - { - m_iTrain = TrainSpeed(pTrain->pev->speed, pTrain->pev->impulse); - m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW; - } - - } else if (m_iTrain & TRAIN_ACTIVE) - m_iTrain = TRAIN_NEW; // turn off train - - if (pev->button & IN_JUMP) - { - // If on a ladder, jump off the ladder - // else Jump - Jump(); - } - - - // If trying to duck, already ducked, or in the process of ducking - if ((pev->button & IN_DUCK) || FBitSet(pev->flags,FL_DUCKING) || (m_afPhysicsFlags & PFLAG_DUCKING) ) - Duck(); - - if ( !FBitSet ( pev->flags, FL_ONGROUND ) ) - { - m_flFallVelocity = -pev->velocity.z; - } - - // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating? - - // Clear out ladder pointer - m_hEnemy = NULL; - - if ( m_afPhysicsFlags & PFLAG_ONBARNACLE ) - { - pev->velocity = g_vecZero; - } -} -/* Time based Damage works as follows: - 1) There are several types of timebased damage: - - #define DMG_PARALYZE (1 << 14) // slows affected creature down - #define DMG_NERVEGAS (1 << 15) // nerve toxins, very bad - #define DMG_POISON (1 << 16) // blood poisioning - #define DMG_RADIATION (1 << 17) // radiation exposure - #define DMG_DROWNRECOVER (1 << 18) // drown recovery - #define DMG_ACID (1 << 19) // toxic chemicals or acid burns - #define DMG_SLOWBURN (1 << 20) // in an oven - #define DMG_SLOWFREEZE (1 << 21) // in a subzero freezer - - 2) A new hit inflicting tbd restarts the tbd counter - each monster has an 8bit counter, - per damage type. The counter is decremented every second, so the maximum time - an effect will last is 255/60 = 4.25 minutes. Of course, staying within the radius - of a damaging effect like fire, nervegas, radiation will continually reset the counter to max. - - 3) Every second that a tbd counter is running, the player takes damage. The damage - is determined by the type of tdb. - Paralyze - 1/2 movement rate, 30 second duration. - Nervegas - 5 points per second, 16 second duration = 80 points max dose. - Poison - 2 points per second, 25 second duration = 50 points max dose. - Radiation - 1 point per second, 50 second duration = 50 points max dose. - Drown - 5 points per second, 2 second duration. - Acid/Chemical - 5 points per second, 10 second duration = 50 points max. - Burn - 10 points per second, 2 second duration. - Freeze - 3 points per second, 10 second duration = 30 points max. - - 4) Certain actions or countermeasures counteract the damaging effects of tbds: - - Armor/Heater/Cooler - Chemical(acid),burn, freeze all do damage to armor power, then to body - - recharged by suit recharger - Air In Lungs - drowning damage is done to air in lungs first, then to body - - recharged by poking head out of water - - 10 seconds if swiming fast - Air In SCUBA - drowning damage is done to air in tanks first, then to body - - 2 minutes in tanks. Need new tank once empty. - Radiation Syringe - Each syringe full provides protection vs one radiation dosage - Antitoxin Syringe - Each syringe full provides protection vs one poisoning (nervegas or poison). - Health kit - Immediate stop to acid/chemical, fire or freeze damage. - Radiation Shower - Immediate stop to radiation damage, acid/chemical or fire damage. - - -*/ - -// If player is taking time based damage, continue doing damage to player - -// this simulates the effect of being poisoned, gassed, dosed with radiation etc - -// anything that continues to do damage even after the initial contact stops. -// Update all time based damage counters, and shut off any that are done. - -// The m_bitsDamageType bit MUST be set if any damage is to be taken. -// This routine will detect the initial on value of the m_bitsDamageType -// and init the appropriate counter. Only processes damage every second. - -//#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage -//#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval - -//#define NERVEGAS_DURATION 16 -//#define NERVEGAS_DAMAGE 5.0 - -//#define POISON_DURATION 25 -//#define POISON_DAMAGE 2.0 - -//#define RADIATION_DURATION 50 -//#define RADIATION_DAMAGE 1.0 - -//#define ACID_DURATION 10 -//#define ACID_DAMAGE 5.0 - -//#define SLOWBURN_DURATION 2 -//#define SLOWBURN_DAMAGE 1.0 - -//#define SLOWFREEZE_DURATION 1.0 -//#define SLOWFREEZE_DAMAGE 3.0 - -/* */ - - -void CBasePlayer::CheckTimeBasedDamage() -{ - int i; - BYTE bDuration = 0; - - static float gtbdPrev = 0.0; - - if (!(m_bitsDamageType & DMG_TIMEBASED)) - return; - - // only check for time based damage approx. every 2 seconds - if (abs(gpGlobals->time - m_tbdPrev) < 2.0) - return; - - m_tbdPrev = gpGlobals->time; - - for (i = 0; i < CDMG_TIMEBASED; i++) - { - // make sure bit is set for damage type - if (m_bitsDamageType & (DMG_PARALYZE << i)) - { - switch (i) - { - case itbd_Paralyze: - // UNDONE - flag movement as half-speed - bDuration = PARALYZE_DURATION; - break; - case itbd_NerveGas: -// TakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC); - bDuration = NERVEGAS_DURATION; - break; - case itbd_Poison: - TakeDamage(pev, pev, POISON_DAMAGE, DMG_GENERIC); - bDuration = POISON_DURATION; - break; - case itbd_Radiation: -// TakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC); - bDuration = RADIATION_DURATION; - break; - case itbd_DrownRecover: - // NOTE: this hack is actually used to RESTORE health - // after the player has been drowning and finally takes a breath - if (m_idrowndmg > m_idrownrestored) - { - int idif = min(m_idrowndmg - m_idrownrestored, 10); - - TakeHealth(idif, DMG_GENERIC); - m_idrownrestored += idif; - } - bDuration = 4; // get up to 5*10 = 50 points back - break; - case itbd_Acid: -// TakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC); - bDuration = ACID_DURATION; - break; - case itbd_SlowBurn: -// TakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC); - bDuration = SLOWBURN_DURATION; - break; - case itbd_SlowFreeze: -// TakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC); - bDuration = SLOWFREEZE_DURATION; - break; - default: - bDuration = 0; - } - - if (m_rgbTimeBasedDamage[i]) - { - // use up an antitoxin on poison or nervegas after a few seconds of damage - if (((i == itbd_NerveGas) && (m_rgbTimeBasedDamage[i] < NERVEGAS_DURATION)) || - ((i == itbd_Poison) && (m_rgbTimeBasedDamage[i] < POISON_DURATION))) - { - if (m_rgItems[ITEM_ANTIDOTE]) - { - m_rgbTimeBasedDamage[i] = 0; - m_rgItems[ITEM_ANTIDOTE]--; - SetSuitUpdate("!HEV_HEAL4", FALSE, SUIT_REPEAT_OK); - } - } - - - // decrement damage duration, detect when done. - if (!m_rgbTimeBasedDamage[i] || --m_rgbTimeBasedDamage[i] == 0) - { - m_rgbTimeBasedDamage[i] = 0; - // if we're done, clear damage bits - m_bitsDamageType &= ~(DMG_PARALYZE << i); - } - } - else - // first time taking this damage type - init damage duration - m_rgbTimeBasedDamage[i] = bDuration; - } - } -} - -/* -THE POWER SUIT - -The Suit provides 3 main functions: Protection, Notification and Augmentation. -Some functions are automatic, some require power. -The player gets the suit shortly after getting off the train in C1A0 and it stays -with him for the entire game. - -Protection - - Heat/Cold - When the player enters a hot/cold area, the heating/cooling indicator on the suit - will come on and the battery will drain while the player stays in the area. - After the battery is dead, the player starts to take damage. - This feature is built into the suit and is automatically engaged. - Radiation Syringe - This will cause the player to be immune from the effects of radiation for N seconds. Single use item. - Anti-Toxin Syringe - This will cure the player from being poisoned. Single use item. - Health - Small (1st aid kits, food, etc.) - Large (boxes on walls) - Armor - The armor works using energy to create a protective field that deflects a - percentage of damage projectile and explosive attacks. After the armor has been deployed, - it will attempt to recharge itself to full capacity with the energy reserves from the battery. - It takes the armor N seconds to fully charge. - -Notification (via the HUD) - -x Health -x Ammo -x Automatic Health Care - Notifies the player when automatic healing has been engaged. -x Geiger counter - Classic Geiger counter sound and status bar at top of HUD - alerts player to dangerous levels of radiation. This is not visible when radiation levels are normal. -x Poison - Armor - Displays the current level of armor. - -Augmentation - - Reanimation (w/adrenaline) - Causes the player to come back to life after he has been dead for 3 seconds. - Will not work if player was gibbed. Single use. - Long Jump - Used by hitting the ??? key(s). Caused the player to further than normal. - SCUBA - Used automatically after picked up and after player enters the water. - Works for N seconds. Single use. - -Things powered by the battery - - Armor - Uses N watts for every M units of damage. - Heat/Cool - Uses N watts for every second in hot/cold area. - Long Jump - Uses N watts for every jump. - Alien Cloak - Uses N watts for each use. Each use lasts M seconds. - Alien Shield - Augments armor. Reduces Armor drain by one half - -*/ - -// if in range of radiation source, ping geiger counter - -#define GEIGERDELAY 0.25 - -void CBasePlayer :: UpdateGeigerCounter( void ) -{ - BYTE range; - - // delay per update ie: don't flood net with these msgs - if (gpGlobals->time < m_flgeigerDelay) - return; - - m_flgeigerDelay = gpGlobals->time + GEIGERDELAY; - - // send range to radition source to client - - range = (BYTE) (m_flgeigerRange / 4); - - if (range != m_igeigerRangePrev) - { - m_igeigerRangePrev = range; - NetMsg_GeigerRange( pev, range ); - } - - // reset counter and semaphore - if (!RANDOM_LONG(0,3)) - m_flgeigerRange = 1000; - -} - -/* -================ -CheckSuitUpdate - -Play suit update if it's time -================ -*/ - -#define SUITUPDATETIME 3.5 -#define SUITFIRSTUPDATETIME 0.1 - -void CBasePlayer::CheckSuitUpdate() -{ - int i; - int isentence = 0; - int isearch = m_iSuitPlayNext; - - // Ignore suit updates if no suit - if ( !(pev->weapons & (1<IsMultiplayer() ) - { - // don't bother updating HEV voice in multiplayer. - return; - } - - if ( gpGlobals->time >= m_flSuitUpdate && m_flSuitUpdate > 0) - { - // play a sentence off of the end of the queue - for (i = 0; i < CSUITPLAYLIST; i++) - { - if (isentence = m_rgSuitPlayList[isearch]) - break; - - if (++isearch == CSUITPLAYLIST) - isearch = 0; - } - - if (isentence) - { - m_rgSuitPlayList[isearch] = 0; - if (isentence > 0) - { - // play sentence number - - char sentence[CBSENTENCENAME_MAX+1]; - strcpy(sentence, "!"); - strcat(sentence, gszallsentencenames[isentence]); - EMIT_SOUND_SUIT(ENT(pev), sentence); - } - else - { - // play sentence group - EMIT_GROUPID_SUIT(ENT(pev), -isentence); - } - m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME; - } - else - // queue is empty, don't check - m_flSuitUpdate = 0; - } -} - -// add sentence to suit playlist queue. if fgroup is true, then -// name is a sentence group (HEV_AA), otherwise name is a specific -// sentence name ie: !HEV_AA0. If iNoRepeat is specified in -// seconds, then we won't repeat playback of this word or sentence -// for at least that number of seconds. - -void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) -{ - int i; - int isentence; - int iempty = -1; - - - // Ignore suit updates if no suit - if ( !(pev->weapons & (1<IsMultiplayer() ) - { - // due to static channel design, etc. We don't play HEV sounds in multiplayer right now. - return; - } - - // if name == NULL, then clear out the queue - - if (!name) - { - for (i = 0; i < CSUITPLAYLIST; i++) - m_rgSuitPlayList[i] = 0; - return; - } - // get sentence or group number - if (!fgroup) - { - isentence = SENTENCEG_Lookup(name, NULL); - if (isentence < 0) - return; - } - else - // mark group number as negative - isentence = -SENTENCEG_GetIndex(name); - - // check norepeat list - this list lets us cancel - // the playback of words or sentences that have already - // been played within a certain time. - - for (i = 0; i < CSUITNOREPEAT; i++) - { - if (isentence == m_rgiSuitNoRepeat[i]) - { - // this sentence or group is already in - // the norepeat list - - if (m_rgflSuitNoRepeatTime[i] < gpGlobals->time) - { - // norepeat time has expired, clear it out - m_rgiSuitNoRepeat[i] = 0; - m_rgflSuitNoRepeatTime[i] = 0.0; - iempty = i; - break; - } - else - { - // don't play, still marked as norepeat - return; - } - } - // keep track of empty slot - if (!m_rgiSuitNoRepeat[i]) - iempty = i; - } - - // sentence is not in norepeat list, save if norepeat time was given - - if (iNoRepeatTime) - { - if (iempty < 0) - iempty = RANDOM_LONG(0, CSUITNOREPEAT-1); // pick random slot to take over - m_rgiSuitNoRepeat[iempty] = isentence; - m_rgflSuitNoRepeatTime[iempty] = iNoRepeatTime + gpGlobals->time; - } - - // find empty spot in queue, or overwrite last spot - - m_rgSuitPlayList[m_iSuitPlayNext++] = isentence; - if (m_iSuitPlayNext == CSUITPLAYLIST) - m_iSuitPlayNext = 0; - - if (m_flSuitUpdate <= gpGlobals->time) - { - if (m_flSuitUpdate == 0) - // play queue is empty, don't delay too long before playback - m_flSuitUpdate = gpGlobals->time + SUITFIRSTUPDATETIME; - else - m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME; - } - -} - -/* -================ -CheckPowerups - -Check for turning off powerups - -GLOBALS ASSUMED SET: g_ulModelIndexPlayer -================ -*/ - static void -CheckPowerups(entvars_t *pev) -{ - if (pev->health <= 0) - return; - - //pev->modelindex = g_ulModelIndexPlayer; // don't use eyes -} - - -//========================================================= -// UpdatePlayerSound - updates the position of the player's -// reserved sound slot in the sound list. -//========================================================= -void CBasePlayer :: UpdatePlayerSound ( void ) -{ - int iBodyVolume; - int iVolume; - CSound *pSound; - - pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt :: ClientSoundIndex( edict() ) ); - - if ( !pSound ) - { - ALERT ( at_console, "Client lost reserved sound!\n" ); - return; - } - - pSound->m_iType = bits_SOUND_NONE; - - // now calculate the best target volume for the sound. If the player's weapon - // is louder than his body/movement, use the weapon volume, else, use the body volume. - - if ( FBitSet ( pev->flags, FL_ONGROUND ) ) - { - iBodyVolume = pev->velocity.Length(); - - // clamp the noise that can be made by the body, in case a push trigger, - // weapon recoil, or anything shoves the player abnormally fast. - if ( iBodyVolume > 512 ) - { - iBodyVolume = 512; - } - } - else - { - iBodyVolume = 0; - } - - if ( pev->button & IN_JUMP ) - { - iBodyVolume += 100; - } - -// convert player move speed and actions into sound audible by monsters. - if ( m_iWeaponVolume > iBodyVolume ) - { - m_iTargetVolume = m_iWeaponVolume; - - // OR in the bits for COMBAT sound if the weapon is being louder than the player. - pSound->m_iType |= bits_SOUND_COMBAT; - } - else - { - m_iTargetVolume = iBodyVolume; - } - - // decay weapon volume over time so bits_SOUND_COMBAT stays set for a while - m_iWeaponVolume -= 250 * gpGlobals->frametime; - if ( m_iWeaponVolume < 0 ) - { - iVolume = 0; - } - - - // if target volume is greater than the player sound's current volume, we paste the new volume in - // immediately. If target is less than the current volume, current volume is not set immediately to the - // lower volume, rather works itself towards target volume over time. This gives monsters a much better chance - // to hear a sound, especially if they don't listen every frame. - iVolume = pSound->m_iVolume; - - if ( m_iTargetVolume > iVolume ) - { - iVolume = m_iTargetVolume; - } - else if ( iVolume > m_iTargetVolume ) - { - iVolume -= 250 * gpGlobals->frametime; - - if ( iVolume < m_iTargetVolume ) - { - iVolume = 0; - } - } - - if ( m_fNoPlayerSound ) - { - // debugging flag, lets players move around and shoot without monsters hearing. - iVolume = 0; - } - - if ( gpGlobals->time > m_flStopExtraSoundTime ) - { - // since the extra sound that a weapon emits only lasts for one client frame, we keep that sound around for a server frame or two - // after actual emission to make sure it gets heard. - m_iExtraSoundTypes = 0; - } - - if ( pSound ) - { - pSound->m_vecOrigin = pev->origin; - pSound->m_iType |= ( bits_SOUND_PLAYER | m_iExtraSoundTypes ); - pSound->m_iVolume = iVolume; - } - - // keep track of virtual muzzle flash - m_iWeaponFlash -= 256 * gpGlobals->frametime; - if (m_iWeaponFlash < 0) - m_iWeaponFlash = 0; - - //UTIL_MakeVectors ( pev->angles ); - //gpGlobals->v_forward.z = 0; - - // Below are a couple of useful little bits that make it easier to determine just how much noise the - // player is making. - // UTIL_ParticleEffect ( pev->origin + gpGlobals->v_forward * iVolume, g_vecZero, 255, 25 ); - //ALERT ( at_console, "%d/%d\n", iVolume, m_iTargetVolume ); -} - - -void CBasePlayer::PostThink() -{ - if ( g_fGameOver ) - goto pt_end; // intermission or finale - - if (!IsAlive()) - goto pt_end; - - // Handle Tank controlling - if ( m_pTank != NULL ) - { // if they've moved too far from the gun, or selected a weapon, unuse the gun - if ( m_pTank->OnControls( pev ) && !pev->weaponmodel ) - { - m_pTank->Use( this, this, USE_SET, 2 ); // try fire the gun - } - else - { // they've moved off the platform - m_pTank->Use( this, this, USE_OFF, 0 ); - m_pTank = NULL; - } - } - -// do weapon stuff - ItemPostFrame( ); - -// check to see if player landed hard enough to make a sound -// falling farther than half of the maximum safe distance, but not as far a max safe distance will -// play a bootscrape sound, and no damage will be inflicted. Fallling a distance shorter than half -// of maximum safe distance will make no sound. Falling farther than max safe distance will play a -// fallpain sound, and damage will be inflicted based on how far the player fell - - if ( (FBitSet(pev->flags, FL_ONGROUND)) && (pev->health > 0) && m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD ) - { - // ALERT ( at_console, "%f\n", m_flFallVelocity ); - - if (pev->watertype == CONTENT_WATER) - { - // Did he hit the world or a non-moving entity? - // BUG - this happens all the time in water, especially when - // BUG - water has current force - // if ( !pev->groundentity || VARS(pev->groundentity)->velocity.z == 0 ) - // EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM); - } - // skulks, lerks, fades and jetpackers don't take falling damage - else if ((m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED) && (this->pev->iuser3 != AVH_USER3_ALIEN_PLAYER1) && (this->pev->iuser3 != AVH_USER3_ALIEN_PLAYER3) && (this->pev->iuser3 != AVH_USER3_ALIEN_PLAYER4) && (!GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_7) || !(this->pev->iuser3 == AVH_USER3_MARINE_PLAYER)) ) - {// after this point, we start doing damage - - float flFallDamage = g_pGameRules->FlPlayerFallDamage( this ); - - if ( flFallDamage > pev->health ) - {//splat - // note: play on item channel because we play footstep landing on body channel - EMIT_SOUND(ENT(pev), CHAN_ITEM, "common/bodysplat.wav", 1, ATTN_NORM); - } - - if ( flFallDamage > 0 ) - { - TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), flFallDamage, DMG_FALL ); - pev->punchangle.x = 0; - } - } - - if ( IsAlive() ) - { - SetAnimation( PLAYER_WALK ); - } - } - - if (FBitSet(pev->flags, FL_ONGROUND)) - { - if (m_flFallVelocity > 64 && !g_pGameRules->IsMultiplayer()) - { - CSoundEnt::InsertSound ( bits_SOUND_PLAYER, pev->origin, m_flFallVelocity, 0.2 ); - // ALERT( at_console, "fall %f\n", m_flFallVelocity ); - } - m_flFallVelocity = 0; - } - - // select the proper animation for the player character - if ( IsAlive() ) - { - if (!pev->velocity.x && !pev->velocity.y) - SetAnimation( PLAYER_IDLE ); - else if ((pev->velocity.x || pev->velocity.y) && (FBitSet(pev->flags, FL_ONGROUND))) - SetAnimation( PLAYER_WALK ); - else if (pev->waterlevel > 1) - SetAnimation( PLAYER_WALK ); - } - - StudioFrameAdvance( ); - CheckPowerups(pev); - - UpdatePlayerSound(); - - // Track button info so we can detect 'pressed' and 'released' buttons next frame - m_afButtonLast = pev->button; - -pt_end: - // Decay timers on weapons - // go through all of the weapons and make a list of the ones to pack - for ( int i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - if ( m_rgpPlayerItems[ i ] ) - { - CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[ i ]; - - while ( pPlayerItem ) - { - CBasePlayerWeapon *gun; - - gun = (CBasePlayerWeapon *)pPlayerItem->GetWeaponPtr(); - - if ( gun && gun->UseDecrement() ) - { - gun->m_flNextPrimaryAttack = max( gun->m_flNextPrimaryAttack - gpGlobals->frametime, -1.0f ); - gun->m_flNextSecondaryAttack = max( gun->m_flNextSecondaryAttack - gpGlobals->frametime, -0.001f ); - - if ( gun->m_flTimeWeaponIdle != 1000 ) - { - gun->m_flTimeWeaponIdle = max( gun->m_flTimeWeaponIdle - gpGlobals->frametime, -0.001f ); - } - - if ( gun->pev->fuser1 != 1000 ) - { - gun->pev->fuser1 = max( gun->pev->fuser1 - gpGlobals->frametime, -0.001f ); - } - - // Only decrement if not flagged as NO_DECREMENT -// if ( gun->m_flPumpTime != 1000 ) - // { - // gun->m_flPumpTime = max( gun->m_flPumpTime - gpGlobals->frametime, -0.001f ); - // } - - } - - pPlayerItem = pPlayerItem->m_pNext; - } - } - } - - m_flNextAttack -= gpGlobals->frametime; - if ( m_flNextAttack < -0.001 ) - m_flNextAttack = -0.001; - - if ( m_flNextAmmoBurn != 1000 ) - { - m_flNextAmmoBurn -= gpGlobals->frametime; - - if ( m_flNextAmmoBurn < -0.001 ) - m_flNextAmmoBurn = -0.001; - } - - if ( m_flAmmoStartCharge != 1000 ) - { - m_flAmmoStartCharge -= gpGlobals->frametime; - - if ( m_flAmmoStartCharge < -0.001 ) - m_flAmmoStartCharge = -0.001; - } -} - - -// checks if the spot is clear of players -BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ) -{ - CBaseEntity *ent = NULL; - - if ( !pSpot->IsTriggered( pPlayer ) ) - { - return FALSE; - } - - AvHPlayer* thePlayer = dynamic_cast(pPlayer); - ASSERT(thePlayer); - - Vector theMinSize; - Vector theMaxSize; - thePlayer->GetSize(theMinSize, theMaxSize); - - int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(thePlayer->pev->iuser3)); - Vector theOriginToSpawn = pSpot->pev->origin; - theOriginToSpawn.z += theOffset; - - if(!AvHSUGetIsEnoughRoomForHull(theOriginToSpawn, AvHMUGetHull(false, thePlayer->pev->iuser3), thePlayer->edict())) - return FALSE; - - //while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 )) != NULL ) - - // Assumes UTIL_FindEntityInSphere doesn't take size of other object into account. Players should be 2*HULL0_MAXX from each other, add another for safety - while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 3*HULL0_MAXX)) != NULL ) - { - // if ent is a client, don't spawn on 'em (but dead players don't block) - if ( ent->IsPlayer() && (ent != pPlayer) && ent->IsAlive() ) - return FALSE; - - } - - return TRUE; -} - -BOOL CBasePlayer::IsAlive() const -{ - // Take into account spectators - return (pev->deadflag == DEAD_NO) && pev->health > 0; -} - -BOOL CBasePlayer::IsAlive(bool inIncludeSpectating) const -{ - // Take into account spectators - BOOL theIsAlive = this->IsAlive(); - if(inIncludeSpectating) - { - CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - theIsAlive = theSpectatingEntity->IsAlive(); - } - } - - return theIsAlive; -} - -CBaseEntity* CBasePlayer::GetSpectatingEntity() const -{ - CBaseEntity* theSpectatingEntity = NULL; - - if(this->pev && this->pev->iuser1 && this->pev->iuser2) - { - int theEntityIndex = this->pev->iuser2; - theSpectatingEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theEntityIndex)); - } - - return theSpectatingEntity; -} - -void CBasePlayer::Spawn( void ) -{ - pev->classname = MAKE_STRING("player"); -// pev->health = 100; -// pev->armorvalue = 0; -// pev->max_health = pev->health; - pev->takedamage = DAMAGE_AIM; - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_WALK; - pev->flags &= FL_PROXY; // keep proxy flag sey by engine - pev->flags |= FL_CLIENT; - pev->air_finished = gpGlobals->time + 12; - pev->dmg = 2; // initial water damage - pev->effects = 0; - pev->deadflag = DEAD_NO; - pev->dmg_take = 0; - pev->dmg_save = 0; - pev->friction = 1.0; - pev->gravity = 1.0; - m_bitsHUDDamage = -1; - m_bitsDamageType = 0; - m_afPhysicsFlags = 0; - m_fLongJump = FALSE;// no longjump module. - - g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); - g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); - - pev->fov = m_iFOV = 0;// init field of view. - m_iClientFOV = -1; // make sure fov reset is sent - - m_flNextDecalTime = 0;// let this player decal as soon as he spawns. - - m_flgeigerDelay = gpGlobals->time + 2.0; // wait a few seconds until user-defined message registrations - // are recieved by all clients - - m_flTimeStepSound = 0; - m_iStepLeft = 0; - m_flFieldOfView = 0.5;// some monsters use this to determine whether or not the player is looking at them. - - m_bloodColor = BLOOD_COLOR_RED; - m_flNextAttack = UTIL_WeaponTimeBase(); - StartSneaking(); - -// m_iFlashBattery = 99; -// m_flFlashLightTime = 1; // force first message - -// dont let uninitialized value here hurt the player - m_flFallVelocity = 0; - - GetGameRules()->SetDefaultPlayerTeam( this ); - edict_t* theSpawnPoint = GetGameRules()->SelectSpawnPoint( this ); - ASSERT(theSpawnPoint); - this->InitPlayerFromSpawn(theSpawnPoint); - - //AvHPlayer* thePlayer = dynamic_cast(this); - //char* thePlayerModel = thePlayer->GetPlayerModel(); - //SET_MODEL(ENT(pev), thePlayerModel); - SET_MODEL(ENT(pev), "models/player.mdl"); - - g_ulModelIndexPlayer = pev->modelindex; - //pev->sequence = LookupActivity( ACT_IDLE ); - pev->sequence = LookupSequence("idle1"); - - if ( FBitSet(pev->flags, FL_DUCKING) ) - UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); - else - UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); - - pev->view_ofs = VEC_VIEW; - Precache(); - m_HackedGunPos = Vector( 0, 32, 0 ); - - if ( m_iPlayerSound == SOUNDLIST_EMPTY ) - { - ALERT ( at_console, "Couldn't alloc player sound slot!\n" ); - } - - m_fNoPlayerSound = FALSE;// normal sound behavior. - - m_pLastItem = NULL; - m_pLastItemName = 0; // Added by mmcguire. - - m_fInitHUD = TRUE; - m_iClientHideHUD = -1; // force this to be recalculated - m_fWeapon = FALSE; - m_pClientActiveItem = NULL; - m_iClientBattery = -1; - - // reset all ammo values to 0 - for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) - { - m_rgAmmo[i] = 0; - m_rgAmmoLast[i] = 0; // client ammo values also have to be reset (the death hud clear messages does on the client side) - } - - m_lastx = m_lasty = 0; - - m_flNextChatTime = gpGlobals->time; - - g_pGameRules->PlayerSpawn( this ); -} - - -void CBasePlayer :: Precache( void ) -{ - // in the event that the player JUST spawned, and the level node graph - // was loaded, fix all of the node graph pointers before the game starts. - - // !!!BUGBUG - now that we have multiplayer, this needs to be moved! - if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet ) - { - if ( !WorldGraph.FSetGraphPointers() ) - { - ALERT ( at_console, "**Graph pointers were not set!\n"); - } - else - { - ALERT ( at_console, "**Graph Pointers Set!\n" ); - } - } - - // SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific) - // because they need to precache before any clients have connected - - Net_InitializeMessages(); - - // init geiger counter vars during spawn and each time - // we cross a level transition - - m_flgeigerRange = 1000; - m_igeigerRangePrev = 1000; - - m_bitsDamageType = 0; - m_bitsHUDDamage = -1; - - m_iClientBattery = -1; - - m_iTrain = TRAIN_NEW; - - m_iUpdateTime = 5; // won't update for 1/2 a second - - if ( gInitHUD ) - m_fInitHUD = TRUE; -} - - -int CBasePlayer::Save( CSave &save ) -{ - if ( !CBaseMonster::Save(save) ) - return 0; - - return save.WriteFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) ); -} - - -// -// Marks everything as new so the player will resend this to the hud. -// -void CBasePlayer::RenewItems(void) -{ - -} - -int CBasePlayer::Restore( CRestore &restore ) -{ - if ( !CBaseMonster::Restore(restore) ) - return 0; - - int status = restore.ReadFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) ); - - SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData; - // landmark isn't present. - if ( !pSaveData->fUseLandmark ) - { - ALERT( at_console, "No Landmark:%s\n", pSaveData->szLandmarkName ); - - // default to normal spawn - //edict_t* pentSpawnSpot = EntSelectSpawnPoint( this ); - ASSERT(FALSE); - //pev->origin = VARS(pentSpawnSpot)->origin + Vector(0,0,1); - //pev->angles = VARS(pentSpawnSpot)->angles; - } - pev->v_angle.z = 0; // Clear out roll - pev->angles = pev->v_angle; - - pev->fixangle = TRUE; // turn this way immediately - -// Copied from spawn() for now - m_bloodColor = BLOOD_COLOR_RED; - - g_ulModelIndexPlayer = pev->modelindex; - - if ( FBitSet(pev->flags, FL_DUCKING) ) - { - // Use the crouch HACK - //FixPlayerCrouchStuck( edict() ); - // Don't need to do this with new player prediction code. - UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); - } - else - { - UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); - } - - g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); - - if ( m_fLongJump ) - { - g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "1" ); - } - else - { - g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); - } - - RenewItems(); - - // HACK: This variable is saved/restored in CBaseMonster as a time variable, but we're using it - // as just a counter. Ideally, this needs its own variable that's saved as a plain float. - // Barring that, we clear it out here instead of using the incorrect restored time value. - m_flNextAttack = UTIL_WeaponTimeBase(); - - return status; -} - - - -void CBasePlayer::SelectNextItem( int iItem ) -{ - CBasePlayerItem *pItem; - - pItem = m_rgpPlayerItems[ iItem ]; - - if (!pItem) - return; - - if (pItem == m_pActiveItem) - { - // select the next one in the chain - pItem = m_pActiveItem->m_pNext; - if (! pItem) - { - return; - } - - CBasePlayerItem *pLast; - pLast = pItem; - while (pLast->m_pNext) - pLast = pLast->m_pNext; - - // relink chain - pLast->m_pNext = m_pActiveItem; - m_pActiveItem->m_pNext = NULL; - m_rgpPlayerItems[ iItem ] = pItem; - } - - ResetAutoaim( ); - - // FIX, this needs to queue them up and delay - if (m_pActiveItem) - { - m_pActiveItem->Holster( ); - } - - m_pActiveItem = pItem; - - if (m_pActiveItem) - { - m_pActiveItem->Deploy( ); - m_pActiveItem->UpdateItemInfo( ); - } -} - -void CBasePlayer::SelectItem(const char *pstr) -{ - if (!pstr) - return; - - CBasePlayerItem *pItem = NULL; - - for (int i = 0; i < MAX_ITEM_TYPES; i++) - { - if (m_rgpPlayerItems[i]) - { - pItem = m_rgpPlayerItems[i]; - - while (pItem) - { - if (FClassnameIs(pItem->pev, pstr)) - break; - pItem = pItem->m_pNext; - } - } - - if (pItem) - break; - } - - if (!pItem) - return; - - - if ((pItem == m_pActiveItem) || (m_pActiveItem != NULL && !m_pActiveItem->CanHolster())) - return; - - ResetAutoaim( ); - - // FIX, this needs to queue them up and delay - if (m_pActiveItem) - m_pActiveItem->Holster( ); - - m_pLastItem = m_pActiveItem; - - if (m_pLastItem) - { - m_pLastItemName = m_pLastItem->pev->classname; - } - else - { - m_pLastItemName = 0; - } - - m_pActiveItem = pItem; - - if (m_pActiveItem) - { - m_pActiveItem->Deploy( ); - m_pActiveItem->UpdateItemInfo( ); - } -} - -//note this should no longer be called, and asserts if used -void CBasePlayer::SelectLastItem(void) -{ - if (!m_pLastItem) - { - - // Try the last item name. - - if (m_pLastItemName != 0) - { - for (int i = 0; i < MAX_ITEM_TYPES; i++) - { - CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; - while (theCurrentItem) - { - if(FClassnameIs(theCurrentItem->pev, STRING(m_pLastItemName))) - { - m_pLastItem = theCurrentItem; - break; - } - theCurrentItem = theCurrentItem->m_pNext; - } - } - } - - } - - if (!m_pLastItem) - { - return; - } - - this->SelectItem(STRING(m_pLastItem->pev->classname)); //replaced copy-and-paste from SelectItem with actual SelectItem call -} - -BOOL CBasePlayer::HasItem(CBasePlayerItem* inWeapon) -{ - // Return true if we have a weapon of this type - bool theHasWeapon = false; - - for(int i = 0; (i < MAX_ITEM_TYPES) && !theHasWeapon; i++) - { - CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; - while(theCurrentItem && !theHasWeapon) - { - if(FClassnameIs(theCurrentItem->pev, STRING(inWeapon->pev->classname))) - { - theHasWeapon = true; - } - theCurrentItem = theCurrentItem->m_pNext; - } - } - - return theHasWeapon; -} - -BOOL CBasePlayer::HasItemWithFlag(int inFlag, CBasePlayerItem*& outItem) -{ - // Return true if we have a weapon of this type - bool theHasWeaponWithFlag = false; - - for(int i = 0; (i < MAX_ITEM_TYPES) && !theHasWeaponWithFlag; i++) - { - CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; - while(theCurrentItem && !theHasWeaponWithFlag) - { - ItemInfo theItemInfo; - theCurrentItem->GetItemInfo(&theItemInfo); - - int theFlags = theItemInfo.iFlags; - if(theFlags & inFlag) - { - theHasWeaponWithFlag = true; - outItem = theCurrentItem; - } - theCurrentItem = theCurrentItem->m_pNext; - } - } - - return theHasWeaponWithFlag; -} - -//============================================== -// HasWeapons - do I have any weapons at all? -//============================================== -BOOL CBasePlayer::HasWeapons( void ) -{ - int i; - - for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - if ( m_rgpPlayerItems[ i ] ) - { - return TRUE; - } - } - - return FALSE; -} - -void CBasePlayer::SelectPrevItem( int iItem ) -{ -} - - -char *CBasePlayer::TeamID( void ) -{ - if ( pev == NULL ) // Not fully connected yet - return ""; - - // return their team name - return m_szTeamName; -} - -void CBasePlayer::SetTeamID(const char* inTeamID) -{ - strncpy(this->m_szTeamName, inTeamID, TEAM_NAME_LENGTH); -} - -//============================================== -// !!!UNDONE:ultra temporary SprayCan entity to apply -// decal frame at a time. For PreAlpha CD -//============================================== -class CSprayCan : public CBaseEntity -{ -public: - void Spawn ( entvars_t *pevOwner ); - void Think( void ); - - virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; } -}; - -void CSprayCan::Spawn ( entvars_t *pevOwner ) -{ - pev->origin = pevOwner->origin + Vector ( 0 , 0 , 32 ); - pev->angles = pevOwner->v_angle; - pev->owner = ENT(pevOwner); - pev->frame = 0; - - pev->nextthink = gpGlobals->time + 0.1; - EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/sprayer.wav", 1, ATTN_NORM); -} - -void CSprayCan::Think( void ) -{ - TraceResult tr; - int playernum; - int nFrames; - CBasePlayer *pPlayer; - - pPlayer = (CBasePlayer *)GET_PRIVATE(pev->owner); - - if (pPlayer) - nFrames = pPlayer->GetCustomDecalFrames(); - else - nFrames = -1; - - playernum = ENTINDEX(pev->owner); - - // ALERT(at_console, "Spray by player %i, %i of %i\n", playernum, (int)(pev->frame + 1), nFrames); - - UTIL_MakeVectors(pev->angles); - UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr); - - // No customization present. - if (nFrames == -1) - { - UTIL_DecalTrace( &tr, DECAL_LAMBDA6 ); - UTIL_Remove( this ); - } - else - { - UTIL_PlayerDecalTrace( &tr, playernum, pev->frame, TRUE ); - // Just painted last custom frame. - if ( pev->frame++ >= (nFrames - 1)) - UTIL_Remove( this ); - } - - pev->nextthink = gpGlobals->time + 0.1; -} - -class CBloodSplat : public CBaseEntity -{ -public: - void Spawn ( entvars_t *pevOwner ); - void Spray ( void ); -}; - -void CBloodSplat::Spawn ( entvars_t *pevOwner ) -{ - pev->origin = pevOwner->origin + Vector ( 0 , 0 , 32 ); - pev->angles = pevOwner->v_angle; - pev->owner = ENT(pevOwner); - - SetThink ( &CBloodSplat::Spray ); - pev->nextthink = gpGlobals->time + 0.1; -} - -void CBloodSplat::Spray ( void ) -{ - TraceResult tr; - - if ( g_Language != LANGUAGE_GERMAN ) - { - UTIL_MakeVectors(pev->angles); - UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr); - - UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); - } - SetThink ( &CBloodSplat::SUB_Remove ); - pev->nextthink = gpGlobals->time + 0.1; -} - -//============================================== - - - -void CBasePlayer::GiveNamedItem( const char *pszName, bool inSendMessage ) -{ - edict_t *pent; - - int istr = MAKE_STRING(pszName); - - pent = CREATE_NAMED_ENTITY(istr); - if ( FNullEnt( pent ) ) - { - ALERT ( at_console, "NULL Ent in GiveNamedItem!\n" ); - return; - } - VARS( pent )->origin = pev->origin; - pent->v.spawnflags |= SF_NORESPAWN; - - DispatchSpawn( pent ); - DispatchTouch( pent, ENT( pev ) ); - - if(inSendMessage) - { - CBaseEntity* theEntity = CBaseEntity::Instance(ENT(pent)); - ASSERT(theEntity); - CBasePlayerWeapon* theWeapon = dynamic_cast(theEntity); - //ASSERT(theWeapon); - if(theWeapon) - { - int theWeaponID = theWeapon->m_iId; - NetMsg_WeapPickup( pev, theWeaponID ); - } - } -} - - - -CBaseEntity *FindEntityForward( CBaseEntity *pMe ) -{ - TraceResult tr; - - UTIL_MakeVectors(pMe->pev->v_angle); - UTIL_TraceLine(pMe->pev->origin + pMe->pev->view_ofs,pMe->pev->origin + pMe->pev->view_ofs + gpGlobals->v_forward * 8192,dont_ignore_monsters, pMe->edict(), &tr ); - if ( tr.flFraction != 1.0 && !FNullEnt( tr.pHit) ) - { - CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); - return pHit; - } - return NULL; -} - - -BOOL CBasePlayer :: FlashlightIsOn( void ) -{ - return FBitSet(pev->effects, EF_DIMLIGHT); -} - -const float kFlashlightVolume = .3f; - -void CBasePlayer :: FlashlightTurnOn( void ) -{ - if ( !g_pGameRules->FAllowFlashlight() ) - { - return; - } - - if ( (pev->weapons & (1<effects, EF_DIMLIGHT); - - /*MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); - WRITE_BYTE(1); - WRITE_BYTE(m_iFlashBattery); - MESSAGE_END(); - - m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time;*/ - - } -} - - -void CBasePlayer :: FlashlightTurnOff( void ) -{ - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_FLASHLIGHT_OFF, kFlashlightVolume, ATTN_NORM, 0, PITCH_NORM ); - ClearBits(pev->effects, EF_DIMLIGHT); - - /*MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); - WRITE_BYTE(0); - WRITE_BYTE(m_iFlashBattery); - MESSAGE_END(); - - m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time;*/ - -} - -/* -=============== -ForceClientDllUpdate - -When recording a demo, we need to have the server tell us the entire client state -so that the client side .dll can behave correctly. -Reset stuff so that the state is transmitted. -=============== -*/ -void CBasePlayer :: ForceClientDllUpdate( void ) -{ - m_iClientHealth = -1; - m_iClientBattery = -1; - m_iTrain |= TRAIN_NEW; // Force new train message. - m_fWeapon = FALSE; // Force weapon send - m_fKnownItem = FALSE; // Force weaponinit messages. - m_fInitHUD = TRUE; // Force HUD gmsgResetHUD message - - // Now force all the necessary messages - // to be sent. - UpdateClientData(); - // m_fWeapon = TRUE; // Force weapon send -} - -/* -============ -ImpulseCommands -============ -*/ -extern float g_flWeaponCheat; - -void CBasePlayer::ImpulseCommands( ) -{ - TraceResult tr;// UNDONE: kill me! This is temporary for PreAlpha CDs - - int iImpulse = (int)pev->impulse; - switch (iImpulse) - { - case IMPULSE_FLASHLIGHT: - // temporary flashlight for level designers - if ( FlashlightIsOn() ) - { - FlashlightTurnOff(); - } - else - { - FlashlightTurnOn(); - } - break; - - case IMPULSE_SPRAYPAINT:// paint decal - - if ( gpGlobals->time < m_flNextDecalTime ) - { - // too early! - break; - } - - UTIL_MakeVectors(pev->v_angle); - UTIL_TraceLine ( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + gpGlobals->v_forward * 128, ignore_monsters, ENT(pev), & tr); - - if ( tr.flFraction != 1.0 ) - {// line hit something, so paint a decal - m_flNextDecalTime = gpGlobals->time + decalfrequency.value; - CSprayCan *pCan = GetClassPtr((CSprayCan *)NULL); - pCan->Spawn( pev ); - } - - break; -// HLDSDK 2.3 update -// case IMPULSE_DEMORECORD: // Demo recording, update client dll specific data again. -// ForceClientDllUpdate(); -// break; - default: - // check all of the cheat impulse commands now - CheatImpulseCommands( iImpulse ); - break; - } - - pev->impulse = 0; -} - -//========================================================= -//========================================================= -void CBasePlayer::CheatImpulseCommands( int iImpulse ) -{ -} - -// -// Add a weapon to the player (Item == Weapon == Selectable Object) -// -int CBasePlayer::AddPlayerItem( CBasePlayerItem *pItem ) -{ - CBasePlayerItem *pInsert; - - pInsert = m_rgpPlayerItems[pItem->iItemSlot()]; - - while (pInsert) - { - if (FClassnameIs( pInsert->pev, STRING( pItem->pev->classname) )) - { - if (pItem->AddDuplicate( pInsert )) - { - g_pGameRules->PlayerGotWeapon ( this, pItem ); - pItem->CheckRespawn(); - - // ugly hack to update clip w/o an update clip message - pInsert->UpdateItemInfo( ); - if (m_pActiveItem) - m_pActiveItem->UpdateItemInfo( ); - - pItem->Kill( ); - } - else if (gEvilImpulse101) - { - // FIXME: remove anyway for deathmatch testing - pItem->Kill( ); - } - return FALSE; - } - pInsert = pInsert->m_pNext; - } - - if (pItem->AddToPlayer( this )) - { - g_pGameRules->PlayerGotWeapon ( this, pItem ); - pItem->CheckRespawn(); - - pItem->m_pNext = m_rgpPlayerItems[pItem->iItemSlot()]; - m_rgpPlayerItems[pItem->iItemSlot()] = pItem; - - // should we switch to this item? - if ( g_pGameRules->FShouldSwitchWeapon( this, pItem ) ) - { - SwitchWeapon( pItem ); - } - - return TRUE; - } - else if (gEvilImpulse101) - { - // FIXME: remove anyway for deathmatch testing - pItem->Kill( ); - } - return FALSE; -} - - - -int CBasePlayer::RemovePlayerItem( CBasePlayerItem *pItem ) -{ - - if (m_pActiveItem == pItem) - { - - ResetAutoaim( ); - pItem->Holster( ); - pItem->pev->nextthink = 0;// crowbar may be trying to swing again, etc. - pItem->SetThink( NULL ); - m_pActiveItem = NULL; - pev->viewmodel = 0; - pev->weaponmodel = 0; - - } - else if ( m_pLastItem == pItem ) - m_pLastItem = NULL; - - AvHBasePlayerWeapon* theWeapon = dynamic_cast(pItem); - if(theWeapon) - { - ItemInfo theInfo; - theWeapon->GetItemInfo(&theInfo); - int theID = theInfo.iId; - this->pev->weapons &= ~(1<iItemSlot()]; - - if (pPrev == pItem) - { - m_rgpPlayerItems[pItem->iItemSlot()] = pItem->m_pNext; - return TRUE; - } - else - { - while (pPrev && pPrev->m_pNext != pItem) - { - pPrev = pPrev->m_pNext; - } - if (pPrev) - { - pPrev->m_pNext = pItem->m_pNext; - return TRUE; - } - } - return FALSE; -} - - -// -// Returns the unique ID for the ammo, or -1 if error -// -int CBasePlayer :: GiveAmmo( int iCount, char *szName, int iMax ) -{ - if ( !szName ) - { - // no ammo. - return -1; - } - - if ( !g_pGameRules->CanHaveAmmo( this, szName, iMax ) ) - { - // game rules say I can't have any more of this ammo type. - return -1; - } - - int i = 0; - - i = GetAmmoIndex( szName ); - - if ( i < 0 || i >= MAX_AMMO_SLOTS ) - return -1; - - int iAdd = min( iCount, iMax - m_rgAmmo[i] ); - if ( iAdd < 1 ) - return i; - - m_rgAmmo[ i ] += iAdd; - - - // Send the message that ammo has been picked up - NetMsg_AmmoPickup( pev, GetAmmoIndex(szName), iAdd ); - - TabulateAmmo(); - - return i; -} - - -/* -============ -ItemPreFrame - -Called every frame by the player PreThink -============ -*/ -void CBasePlayer::ItemPreFrame() -{ - if ( m_flNextAttack > 0 ) - { - return; - } - - if (!m_pActiveItem) - return; - - m_pActiveItem->ItemPreFrame( ); -} - - -/* -============ -ItemPostFrame - -Called every frame by the player PostThink -============ -*/ -void CBasePlayer::ItemPostFrame() -{ - static int fInSelect = FALSE; - - // check if the player is using a tank - if ( m_pTank != NULL ) - return; - - ImpulseCommands(); - - if ( m_flNextAttack > 0 ) - { - return; - } - - if (!m_pActiveItem) - return; - - // Only update weapon once game has started (no firing before then) - //if(GetGameRules()->GetGameStarted()) - //{ - this->m_pActiveItem->ItemPostFrame( ); - //} -} - -int CBasePlayer::AmmoInventory( int iAmmoIndex ) -{ - if (iAmmoIndex == -1) - { - return -1; - } - - return m_rgAmmo[ iAmmoIndex ]; -} - -int CBasePlayer::GetAmmoIndex(const char *psz) -{ - int i; - - if (!psz) - return -1; - - for (i = 1; i < MAX_AMMO_SLOTS; i++) - { - if ( !CBasePlayerItem::AmmoInfoArray[i].pszName ) - continue; - - if (stricmp( psz, CBasePlayerItem::AmmoInfoArray[i].pszName ) == 0) - return i; - } - - return -1; -} - -int CBasePlayer::GetMaxWalkSpeed(void) const -{ - return 220; -} - -// Called from UpdateClientData -// makes sure the client has all the necessary ammo info, if values have changed -void CBasePlayer::SendAmmoUpdate(void) -{ - int theAmmoToSend[MAX_AMMO_SLOTS]; - memcpy(&theAmmoToSend, &this->m_rgAmmo, MAX_AMMO_SLOTS*sizeof(int)); - -// if(this->pev->iuser1 == OBS_IN_EYE) -// { -// CBasePlayer* theEntity = NULL; -// if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) -// { -// memcpy(&theAmmoToSend, &theEntity->m_rgAmmo, MAX_AMMO_SLOTS*sizeof(int)); -// } -// } - - for (int i=0; i < MAX_AMMO_SLOTS;i++) - { - if (theAmmoToSend[i] != m_rgAmmoLast[i]) - { - m_rgAmmoLast[i] = theAmmoToSend[i]; - - // TODO: Fix me! - //ASSERT( m_rgAmmo[i] >= 0 ); - //ASSERT( m_rgAmmo[i] < 255 ); - if((theAmmoToSend[i] >= 0) && (theAmmoToSend[i] < 255)) - { - // send "Ammo" update message - NetMsg_AmmoX( pev, i, max( min( theAmmoToSend[i], 254 ), 0 ) ); - } - } - } -} - -/* -========================================================= - UpdateClientData - -resends any changed player HUD info to the client. -Called every frame by PlayerPreThink -Also called at start of demo recording and playback by -ForceClientDllUpdate to ensure the demo gets messages -reflecting all of the HUD state info. -========================================================= -*/ -void CBasePlayer :: UpdateClientData( void ) -{ - CBasePlayer* thePlayerToUseForWeaponUpdates = this; -// if(this->pev->iuser1 == OBS_IN_EYE) -// { -// CBasePlayer* theSpectatingPlayer = NULL; -// if(AvHSUGetEntityFromIndex(this->pev->iuser2, theSpectatingPlayer)) -// { -// thePlayerToUseForWeaponUpdates = theSpectatingPlayer; -// } -// } - - if (m_fInitHUD) - { - m_fInitHUD = FALSE; - gInitHUD = FALSE; - - NetMsg_ResetHUD( pev ); - - if ( !m_fGameHUDInitialized ) - { - NetMsg_InitHUD( pev ); - - g_pGameRules->InitHUD( this ); - m_fGameHUDInitialized = TRUE; - if ( g_pGameRules->IsMultiplayer() ) - { - FireTargets( "game_playerjoin", this, this, USE_TOGGLE, 0 ); - } - } - FireTargets( "game_playerspawn", this, this, USE_TOGGLE, 0 ); - - InitStatusBar(); - } - - if ( m_iHideHUD != m_iClientHideHUD ) - { - NetMsg_HideWeapon( pev, m_iHideHUD ); - m_iClientHideHUD = m_iHideHUD; - } - - if ( m_iFOV != m_iClientFOV ) - { - NetMsg_SetFOV( pev, m_iFOV ); - // cache FOV change at end of function, so weapon updates can see that FOV has changed - } - - // HACKHACK -- send the message to display the game title - if (gDisplayTitle) - { - NetMsg_ShowGameTitle( pev ); - gDisplayTitle = 0; - } - - if ((int)pev->health != m_iClientHealth) //voogru: this cast to int is important, otherwise we spam the message, this is just a quick and easy fix. - { - NetMsg_Health( pev, max( pev->health, 0.0f ) ); - m_iClientHealth = (int)pev->health; - } - - if ((int)pev->armorvalue != m_iClientBattery) - { - NetMsg_Battery( pev, (int)pev->armorvalue ); - m_iClientBattery = (int)pev->armorvalue; - } - - if (pev->dmg_take || pev->dmg_save || m_bitsHUDDamage != m_bitsDamageType) - { - // Comes from inside me if not set - Vector damageOrigin = pev->origin; - // send "damage" message - // causes screen to flash, and pain compass to show direction of damage - edict_t *other = pev->dmg_inflictor; - if ( other ) - { - CBaseEntity *pEntity = CBaseEntity::Instance(other); - if ( pEntity ) - damageOrigin = pEntity->Center(); - } - - // only send down damage type that have hud art - int visibleDamageBits = m_bitsDamageType & DMG_SHOWNHUD; - - float origin[] = { damageOrigin.x, damageOrigin.y, damageOrigin.z }; - NetMsg_Damage( pev, pev->dmg_save, pev->dmg_take, 0, origin ); - - pev->dmg_take = 0; - pev->dmg_save = 0; - m_bitsHUDDamage = m_bitsDamageType; - - // Clear off non-time-based damage indicators - m_bitsDamageType &= DMG_TIMEBASED; - } - - if (m_iTrain & TRAIN_NEW) - { - // send "health" update message - NetMsg_Train( pev, m_iTrain & 0xF ); - m_iTrain &= ~TRAIN_NEW; - } - - // - // New Weapon? - // - - bool forceCurWeaponUpdate = false; - - if (!m_fKnownItem) - { - - m_fKnownItem = TRUE; - - // WeaponInit Message - // byte = # of weapons - // - // for each weapon: - // byte name str length (not including null) - // bytes... name - // byte Ammo Type - // byte Ammo2 Type - // byte bucket - // byte bucket pos - // byte flags - // ???? Icons - - // Send ALL the weapon info now - this->SendWeaponUpdate(); - - // tankefugl: HACK force an full curweapon update after each bunch of weaponlists sent - forceCurWeaponUpdate = true; - // :tankefugl - } - - - SendAmmoUpdate(); - - // Update all the items - for ( int i = 0; i < MAX_ITEM_TYPES; i++ ) - { - if (forceCurWeaponUpdate == true) - m_fWeapon = FALSE; - if ( thePlayerToUseForWeaponUpdates->m_rgpPlayerItems[i] ) // each item updates it's successors - thePlayerToUseForWeaponUpdates->m_rgpPlayerItems[i]->UpdateClientData( thePlayerToUseForWeaponUpdates ); - } - if (forceCurWeaponUpdate == true) - m_fWeapon = TRUE; - - // Cache and client weapon change - m_pClientActiveItem = thePlayerToUseForWeaponUpdates->m_pActiveItem; - m_iClientFOV = thePlayerToUseForWeaponUpdates->m_iFOV; - - // Update Status Bar if we're playing - AvHPlayer* thePlayer = dynamic_cast(this); - if(thePlayer && (thePlayer->GetPlayMode() == PLAYMODE_PLAYING)) - { - if ( m_flNextSBarUpdateTime < gpGlobals->time ) - { - UpdateStatusBar(); - m_flNextSBarUpdateTime = gpGlobals->time + 0.2; - } - } -} - -void CBasePlayer::SendWeaponUpdate() -{ - int i; - - for (i = 0; i < MAX_WEAPONS; i++) - { - ItemInfo& II = CBasePlayerItem::ItemInfoArray[i]; - - if ( !II.iId ) - continue; - - const char *pszName; - if (!II.pszName) - pszName = "Empty"; - else - pszName = II.pszName; - - WeaponList weapon; - weapon.weapon_name = pszName; - weapon.ammo1_type = GetAmmoIndex(II.pszAmmo1); - weapon.ammo2_type = GetAmmoIndex(II.pszAmmo2); - weapon.ammo1_max_amnt = II.iMaxAmmo1; - weapon.ammo2_max_amnt = II.iMaxAmmo2; - weapon.bucket = II.iSlot; - weapon.bucket_pos = II.iPosition; - weapon.bit_index = II.iId; - weapon.flags = II.iFlags; - - NetMsg_WeaponList( pev, weapon ); - } - -} - -//========================================================= -// FBecomeProne - Overridden for the player to set the proper -// physics flags when a barnacle grabs player. -//========================================================= -BOOL CBasePlayer :: FBecomeProne ( void ) -{ - m_afPhysicsFlags |= PFLAG_ONBARNACLE; - return TRUE; -} - -//========================================================= -// BarnacleVictimBitten - bad name for a function that is called -// by Barnacle victims when the barnacle pulls their head -// into its mouth. For the player, just die. -//========================================================= -void CBasePlayer :: BarnacleVictimBitten ( entvars_t *pevBarnacle ) -{ - TakeDamage ( pevBarnacle, pevBarnacle, pev->health + pev->armorvalue, DMG_SLASH | DMG_ALWAYSGIB ); -} - -//========================================================= -// BarnacleVictimReleased - overridden for player who has -// physics flags concerns. -//========================================================= -void CBasePlayer :: BarnacleVictimReleased ( void ) -{ - m_afPhysicsFlags &= ~PFLAG_ONBARNACLE; -} - - -//========================================================= -// Illumination -// return player light level plus virtual muzzle flash -//========================================================= -int CBasePlayer :: Illumination( void ) -{ - int iIllum = CBaseEntity::Illumination( ); - - iIllum += m_iWeaponFlash; - if (iIllum > 255) - return 255; - return iIllum; -} - - -void CBasePlayer :: EnableControl(BOOL fControl) -{ - if (!fControl) - pev->flags |= FL_FROZEN; - else - pev->flags &= ~FL_FROZEN; - -} - - -#define DOT_1DEGREE 0.9998476951564 -#define DOT_2DEGREE 0.9993908270191 -#define DOT_3DEGREE 0.9986295347546 -#define DOT_4DEGREE 0.9975640502598 -#define DOT_5DEGREE 0.9961946980917 -#define DOT_6DEGREE 0.9945218953683 -#define DOT_7DEGREE 0.9925461516413 -#define DOT_8DEGREE 0.9902680687416 -#define DOT_9DEGREE 0.9876883405951 -#define DOT_10DEGREE 0.9848077530122 -#define DOT_15DEGREE 0.9659258262891 -#define DOT_20DEGREE 0.9396926207859 -#define DOT_25DEGREE 0.9063077870367 - -//========================================================= -// Autoaim -// set crosshair position to point to enemey -//========================================================= -Vector CBasePlayer :: GetAutoaimVector( float flDelta ) -{ - if (g_iSkillLevel == SKILL_HARD) - { - UTIL_MakeVectors( pev->v_angle + pev->punchangle ); - return gpGlobals->v_forward; - } - - Vector vecSrc = GetGunPosition( ); - float flDist = 8192; - - // always use non-sticky autoaim - // UNDONE: use sever variable to chose! - if (1 || g_iSkillLevel == SKILL_MEDIUM) - { - m_vecAutoAim = Vector( 0, 0, 0 ); - // flDelta *= 0.5; - } - - BOOL m_fOldTargeting = m_fOnTarget; - Vector angles = AutoaimDeflection(vecSrc, flDist, flDelta ); - - // update ontarget if changed - if ( !g_pGameRules->AllowAutoTargetCrosshair() ) - m_fOnTarget = 0; - else if (m_fOldTargeting != m_fOnTarget) - { - m_pActiveItem->UpdateItemInfo( ); - } - - if (angles.x > 180) - angles.x -= 360; - if (angles.x < -180) - angles.x += 360; - if (angles.y > 180) - angles.y -= 360; - if (angles.y < -180) - angles.y += 360; - - if (angles.x > 25) - angles.x = 25; - if (angles.x < -25) - angles.x = -25; - if (angles.y > 12) - angles.y = 12; - if (angles.y < -12) - angles.y = -12; - - - // always use non-sticky autoaim - // UNDONE: use sever variable to chose! - if (0 || g_iSkillLevel == SKILL_EASY) - { - m_vecAutoAim = m_vecAutoAim * 0.67 + angles * 0.33; - } - else - { - m_vecAutoAim = angles * 0.9; - } - - // m_vecAutoAim = m_vecAutoAim * 0.99; - - // Don't send across network if sv_aim is 0 - if ( g_psv_aim->value != 0 ) - { - if ( m_vecAutoAim.x != m_lastx || - m_vecAutoAim.y != m_lasty ) - { - SET_CROSSHAIRANGLE( edict(), -m_vecAutoAim.x, m_vecAutoAim.y ); - - m_lastx = m_vecAutoAim.x; - m_lasty = m_vecAutoAim.y; - } - } - - // ALERT( at_console, "%f %f\n", angles.x, angles.y ); - - UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); - return gpGlobals->v_forward; -} - - -Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta ) -{ - edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); - CBaseEntity *pEntity; - float bestdot; - Vector bestdir; - edict_t *bestent; - TraceResult tr; - - if ( g_psv_aim->value == 0 ) - { - m_fOnTarget = FALSE; - return g_vecZero; - } - - UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); - - // try all possible entities - bestdir = gpGlobals->v_forward; - bestdot = flDelta; // +- 10 degrees - bestent = NULL; - - m_fOnTarget = FALSE; - - UTIL_TraceLine( vecSrc, vecSrc + bestdir * flDist, dont_ignore_monsters, edict(), &tr ); - - - if ( tr.pHit && tr.pHit->v.takedamage != DAMAGE_NO) - { - // don't look through water - if (!((pev->waterlevel != 3 && tr.pHit->v.waterlevel == 3) - || (pev->waterlevel == 3 && tr.pHit->v.waterlevel == 0))) - { - if (tr.pHit->v.takedamage == DAMAGE_AIM) - m_fOnTarget = TRUE; - - return m_vecAutoAim; - } - } - - for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) - { - Vector center; - Vector dir; - float dot; - - if ( pEdict->free ) // Not in use - continue; - - if (pEdict->v.takedamage != DAMAGE_AIM) - continue; - if (pEdict == edict()) - continue; -// if (pev->team > 0 && pEdict->v.team == pev->team) -// continue; // don't aim at teammate - if ( !g_pGameRules->ShouldAutoAim( this, pEdict ) ) - continue; - - pEntity = Instance( pEdict ); - if (pEntity == NULL) - continue; - - if (!pEntity->IsAlive()) - continue; - - // don't look through water - if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) - || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0)) - continue; - - center = pEntity->BodyTarget( vecSrc ); - - dir = (center - vecSrc).Normalize( ); - - // make sure it's in front of the player - if (DotProduct (dir, gpGlobals->v_forward ) < 0) - continue; - - dot = fabs( DotProduct (dir, gpGlobals->v_right ) ) - + fabs( DotProduct (dir, gpGlobals->v_up ) ) * 0.5; - - // tweek for distance - dot *= 1.0 + 0.2 * ((center - vecSrc).Length() / flDist); - - if (dot > bestdot) - continue; // to far to turn - - UTIL_TraceLine( vecSrc, center, dont_ignore_monsters, edict(), &tr ); - if (tr.flFraction != 1.0 && tr.pHit != pEdict) - { - // ALERT( at_console, "hit %s, can't see %s\n", STRING( tr.pHit->v.classname ), STRING( pEdict->v.classname ) ); - continue; - } - - // don't shoot at friends - if (IRelationship( pEntity ) < 0) - { - if ( !pEntity->IsPlayer() && !g_pGameRules->IsDeathmatch()) - // ALERT( at_console, "friend\n"); - continue; - } - - // can shoot at this one - bestdot = dot; - bestent = pEdict; - bestdir = dir; - } - - if (bestent) - { - bestdir = UTIL_VecToAngles (bestdir); - bestdir.x = -bestdir.x; - bestdir = bestdir - pev->v_angle - pev->punchangle; - - if (bestent->v.takedamage == DAMAGE_AIM) - m_fOnTarget = TRUE; - - return bestdir; - } - - return Vector( 0, 0, 0 ); -} - - -void CBasePlayer :: ResetAutoaim( ) -{ - if (m_vecAutoAim.x != 0 || m_vecAutoAim.y != 0) - { - m_vecAutoAim = Vector( 0, 0, 0 ); - SET_CROSSHAIRANGLE( edict(), 0, 0 ); - } - m_fOnTarget = FALSE; -} - -/* -============= -SetCustomDecalFrames - - UNDONE: Determine real frame limit, 8 is a placeholder. - Note: -1 means no custom frames present. -============= -*/ -void CBasePlayer :: SetCustomDecalFrames( int nFrames ) -{ - if (nFrames > 0 && - nFrames < 8) - m_nCustomSprayFrames = nFrames; - else - m_nCustomSprayFrames = -1; -} - -/* -============= -GetCustomDecalFrames - - Returns the # of custom frames this player's custom clan logo contains. -============= -*/ -int CBasePlayer :: GetCustomDecalFrames( void ) -{ - return m_nCustomSprayFrames; -} - - -//========================================================= -// DropPlayerItem - drop the named item, or if no name, -// the active item. -//========================================================= -void CBasePlayer::DropPlayerItem ( char *pszItemName ) -{ - if ( !g_pGameRules->IsMultiplayer() || (weaponstay.value > 0) ) - { - // no dropping in single player. - return; - } - - if ( !strlen( pszItemName ) ) - { - // if this string has no length, the client didn't type a name! - // assume player wants to drop the active item. - // make the string null to make future operations in this function easier - pszItemName = NULL; - } - - CBasePlayerItem *pWeapon; - int i; - - for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - pWeapon = m_rgpPlayerItems[ i ]; - - while ( pWeapon ) - { - if ( pszItemName ) - { - // try to match by name. - const char* theWeaponClassName = STRING( pWeapon->pev->classname ); - if ( !strcmp( pszItemName, theWeaponClassName) ) - { - // match! - break; - } - } - else - { - // trying to drop active item - if ( pWeapon == m_pActiveItem ) - { - // active item! - break; - } - } - - pWeapon = pWeapon->m_pNext; - } - - - // if we land here with a valid pWeapon pointer, that's because we found the - // item we want to drop and hit a BREAK; pWeapon is the item. - if ( pWeapon ) - { - g_pGameRules->GetNextBestWeapon( this, pWeapon ); - - UTIL_MakeVectors ( pev->angles ); - - pev->weapons &= ~(1<m_iId);// take item off hud - - CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin + gpGlobals->v_forward * 10, pev->angles, edict() ); - pWeaponBox->pev->angles.x = 0; - pWeaponBox->pev->angles.z = 0; - pWeaponBox->PackWeapon( pWeapon ); - pWeaponBox->pev->velocity = gpGlobals->v_forward * 300 + gpGlobals->v_forward * 100; - - // drop half of the ammo for this weapon. - int iAmmoIndex; - - iAmmoIndex = GetAmmoIndex ( pWeapon->pszAmmo1() ); // ??? - - if ( iAmmoIndex != -1 ) - { - // this weapon weapon uses ammo, so pack an appropriate amount. - if ( pWeapon->iFlags() & ITEM_FLAG_EXHAUSTIBLE ) - { - // pack up all the ammo, this weapon is its own ammo type - pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] ); - m_rgAmmo[ iAmmoIndex ] = 0; - - } - else - { - // pack half of the ammo - pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] / 2 ); - m_rgAmmo[ iAmmoIndex ] /= 2; - } - - } - - return;// we're done, so stop searching with the FOR loop. - } - } -} - -//========================================================= -// HasPlayerItem Does the player already have this item? -//========================================================= -BOOL CBasePlayer::HasPlayerItem( CBasePlayerItem *pCheckItem ) -{ - CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()]; - - while (pItem) - { - if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) )) - { - return TRUE; - } - pItem = pItem->m_pNext; - } - - return FALSE; -} - -//========================================================= -// HasNamedPlayerItem Does the player already have this item? -//========================================================= -CBasePlayerItem* CBasePlayer::HasNamedPlayerItem( const char *pszItemName ) -{ - CBasePlayerItem *pReturn = NULL; - CBasePlayerItem *pItem = NULL; - int i; - - for ( i = 0 ; (i < MAX_ITEM_TYPES) && !pReturn ; i++ ) - { - pItem = m_rgpPlayerItems[ i ]; - - while (pItem && !pReturn) - { - if ( !strcmp( pszItemName, STRING( pItem->pev->classname ) ) ) - { - pReturn = pItem; - } - pItem = pItem->m_pNext; - } - } - - return pReturn; -} - -//========================================================= -// -//========================================================= -BOOL CBasePlayer :: SwitchWeapon( CBasePlayerItem *pWeapon ) -{ - if ( !pWeapon->CanDeploy() ) - { - return FALSE; - } - - ResetAutoaim( ); - - if (m_pActiveItem) - { - m_pActiveItem->Holster( ); - } - - m_pActiveItem = pWeapon; - pWeapon->Deploy( ); - - return TRUE; -} - -//========================================================= -// Dead HEV suit prop -//========================================================= -class CDeadHEV : public CBaseMonster -{ -public: - void Spawn( void ); - int Classify ( void ) { return CLASS_HUMAN_MILITARY; } - - void KeyValue( KeyValueData *pkvd ); - - int m_iPose;// which sequence to display -- temporary, don't need to save - static char *m_szPoses[4]; -}; - -char *CDeadHEV::m_szPoses[] = { "deadback", "deadsitting", "deadstomach", "deadtable" }; - -void CDeadHEV::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "pose")) - { - m_iPose = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseMonster::KeyValue( pkvd ); -} - -LINK_ENTITY_TO_CLASS( monster_hevsuit_dead, CDeadHEV ); - -//========================================================= -// ********** DeadHEV SPAWN ********** -//========================================================= -void CDeadHEV :: Spawn( void ) -{ - PRECACHE_MODEL("models/player.mdl"); - SET_MODEL(ENT(pev), "models/player.mdl"); - - pev->effects = 0; - pev->yaw_speed = 8; - pev->sequence = 0; - pev->body = 1; - m_bloodColor = BLOOD_COLOR_RED; - - pev->sequence = LookupSequence( m_szPoses[m_iPose] ); - - if (pev->sequence == -1) - { - ALERT ( at_console, "Dead hevsuit with bad pose\n" ); - pev->sequence = 0; - pev->effects = EF_BRIGHTFIELD; - } - - // Corpses have less health - pev->health = 8; - - MonsterInitDead(); -} - - -class CStripWeapons : public CPointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - -private: -}; - -LINK_ENTITY_TO_CLASS( player_weaponstrip, CStripWeapons ); - -void CStripWeapons :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBasePlayer *pPlayer = NULL; - - if ( pActivator && pActivator->IsPlayer() ) - { - pPlayer = (CBasePlayer *)pActivator; - } - else if ( !g_pGameRules->IsDeathmatch() ) - { - pPlayer = (CBasePlayer *)CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) ); - } - - if ( pPlayer ) - pPlayer->RemoveAllItems( FALSE ); -} - - -class CRevertSaved : public CPointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT MessageThink( void ); - void EXPORT LoadThink( void ); - void KeyValue( KeyValueData *pkvd ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - inline float Duration( void ) { return pev->dmg_take; } - inline float HoldTime( void ) { return pev->dmg_save; } - inline float MessageTime( void ) { return m_messageTime; } - inline float LoadTime( void ) { return m_loadTime; } - - inline void SetDuration( float duration ) { pev->dmg_take = duration; } - inline void SetHoldTime( float hold ) { pev->dmg_save = hold; } - inline void SetMessageTime( float time ) { m_messageTime = time; } - inline void SetLoadTime( float time ) { m_loadTime = time; } - -private: - float m_messageTime; - float m_loadTime; -}; - -LINK_ENTITY_TO_CLASS( player_loadsaved, CRevertSaved ); - -TYPEDESCRIPTION CRevertSaved::m_SaveData[] = -{ - DEFINE_FIELD( CRevertSaved, m_messageTime, FIELD_FLOAT ), // These are not actual times, but durations, so save as floats - DEFINE_FIELD( CRevertSaved, m_loadTime, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE( CRevertSaved, CPointEntity ); - -void CRevertSaved :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "duration")) - { - SetDuration( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "holdtime")) - { - SetHoldTime( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "messagetime")) - { - SetMessageTime( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "loadtime")) - { - SetLoadTime( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -void CRevertSaved :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), pev->renderamt, FFADE_OUT ); - pev->nextthink = gpGlobals->time + MessageTime(); - SetThink( &CRevertSaved::MessageThink ); -} - - -void CRevertSaved :: MessageThink( void ) -{ - UTIL_ShowMessageAll( STRING(pev->message) ); - float nextThink = LoadTime() - MessageTime(); - if ( nextThink > 0 ) - { - pev->nextthink = gpGlobals->time + nextThink; - SetThink( &CRevertSaved::LoadThink ); - } - else - LoadThink(); -} - - -void CRevertSaved :: LoadThink( void ) -{ - if ( !gpGlobals->deathmatch ) - { - SERVER_COMMAND("reload\n"); - } -} - - -//========================================================= -// Multiplayer intermission spots. -//========================================================= -class CInfoIntermission:public CPointEntity -{ - void Spawn( void ); - void Think( void ); -}; - -void CInfoIntermission::Spawn( void ) -{ - UTIL_SetOrigin( pev, pev->origin ); - pev->solid = SOLID_NOT; - pev->effects = EF_NODRAW; - pev->v_angle = g_vecZero; - - pev->nextthink = gpGlobals->time + 2;// let targets spawn! - -} - -void CInfoIntermission::Think ( void ) -{ - edict_t *pTarget; - - // find my target - pTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) ); - - if ( !FNullEnt(pTarget) ) - { - pev->v_angle = UTIL_VecToAngles( (pTarget->v.origin - pev->origin).Normalize() ); - pev->v_angle.x = -pev->v_angle.x; - } -} - -LINK_ENTITY_TO_CLASS( info_intermission, CInfoIntermission ); - +// +// Copyright (c) 1999, Valve LLC. All rights reserved. +// +// This product contains software technology licensed from Id +// Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +// All Rights Reserved. +// +// Use, distribution, and modification of this source code and/or resulting +// object code is restricted to non-commercial enhancements to products from +// Valve LLC. All other use, distribution, or modification is prohibited +// without written permission from Valve LLC. +// +// +//===== player.cpp ======================================================== +// +// functions dealing with the player +// +// +// $Workfile: player.cpp $ +// $Date: 2002/11/22 21:12:55 $ +// +//------------------------------------------------------------------------------- +// $Log: player.cpp,v $ +// Revision 1.39 2002/11/22 21:12:55 Flayra +// - Added DMG_IGNOREARMOR flag for players (used for ending game quickly) +// - NULL checks for when owner of turret is removed after owner leaves team +// - Send deploy weapon switch to player, when "lastinv" is executed +// +// Revision 1.38 2002/11/15 19:09:40 Flayra +// - Reworked network metering to be easily toggleable +// +// Revision 1.37 2002/11/12 02:18:41 Flayra +// - Beginning of HLTV changes +// +// Revision 1.36 2002/10/24 21:18:23 Flayra +// - Major networking optimizations, to prevent overlflows after 20+ players on game reset +// +// Revision 1.35 2002/10/18 22:15:13 Flayra +// - Fixed problem where level5 gets stuck with redemption +// +// Revision 1.34 2002/10/16 00:40:39 Flayra +// - Fixed bug where player couldn't switch to next player while observing +// - Propagate health as short for larger aliens +// - Propagate auth mask +// +// Revision 1.33 2002/10/03 18:26:03 Flayra +// - Removed HL "flatline" sound on death +// +// Revision 1.32 2002/09/23 22:03:50 Flayra +// - Fixed problem where anims weren't playing properly in ready room +// - Fixed problem where gestation model anims were playing before model was updated +// +// Revision 1.31 2002/08/31 18:01:18 Flayra +// - Work at VALVe +// +// Revision 1.30 2002/08/09 00:17:03 Flayra +// - Allow moveleft and moveright to change targets, allow players to have only one primary weapon +// +// Revision 1.29 2002/07/23 16:48:21 Flayra +// - Refactored duplicate spawn code +// +// Revision 1.28 2002/07/10 14:36:09 Flayra +// - Fixed bug that caused "Battery" message to be sent every tick (this never showed up in HL because they never had a float armor value?) +// +// Revision 1.27 2002/07/08 16:31:39 Flayra +// - Level 1 doesn't make falling splat sound, dead players shouldn't block spawn points, replaced impulse hard-codes with constants +// +//=============================================================================== +#include +#include "extdll.h" +#include "util.h" + +#include "cbase.h" +#include "player.h" +#include "trains.h" +#include "nodes.h" +#include "weapons.h" +#include "soundent.h" +#include "monsters.h" +#include "engine/shake.h" +#include "decals.h" +#include "gamerules.h" +#include "mod/AvHEntities.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHHulls.h" +#include "mod/AvHMovementUtil.h" +#include "game.h" +#include "common/hltv.h" +#include "mod/AvHNetworkMessages.h" +#include "util/MathUtil.h" + +// #define DUCKFIX + +// Allow assignment within conditional +#pragma warning (disable: 4706) + +extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; +extern DLL_GLOBAL BOOL g_fGameOver; +extern DLL_GLOBAL BOOL g_fDrawLines; +int gEvilImpulse101; +extern DLL_GLOBAL int g_iSkillLevel, gDisplayTitle; + + +BOOL gInitHUD = TRUE; + +extern void CopyToBodyQue(entvars_t* pev); +extern void respawn(entvars_t *pev, BOOL fCopyCorpse); +extern Vector VecBModelOrigin(entvars_t *pevBModel ); +//extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ); + +// the world node graph +extern CGraph WorldGraph; + +#define TRAIN_ACTIVE 0x80 +#define TRAIN_NEW 0xc0 +#define TRAIN_OFF 0x00 +#define TRAIN_NEUTRAL 0x01 +#define TRAIN_SLOW 0x02 +#define TRAIN_MEDIUM 0x03 +#define TRAIN_FAST 0x04 +#define TRAIN_BACK 0x05 + +#define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes +#define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit) + +// Global Savedata for player +TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = +{ + DEFINE_FIELD( CBasePlayer, m_afButtonLast, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_afButtonPressed, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_afButtonReleased, FIELD_INTEGER ), + + DEFINE_ARRAY( CBasePlayer, m_rgItems, FIELD_INTEGER, MAX_ITEMS ), + DEFINE_FIELD( CBasePlayer, m_afPhysicsFlags, FIELD_INTEGER ), + + DEFINE_FIELD( CBasePlayer, m_flTimeStepSound, FIELD_TIME ), + DEFINE_FIELD( CBasePlayer, m_flTimeWeaponIdle, FIELD_TIME ), + DEFINE_FIELD( CBasePlayer, m_flSwimTime, FIELD_TIME ), + DEFINE_FIELD( CBasePlayer, m_flDuckTime, FIELD_TIME ), + DEFINE_FIELD( CBasePlayer, m_flWallJumpTime, FIELD_TIME ), + + DEFINE_FIELD( CBasePlayer, m_flSuitUpdate, FIELD_TIME ), + DEFINE_ARRAY( CBasePlayer, m_rgSuitPlayList, FIELD_INTEGER, CSUITPLAYLIST ), + DEFINE_FIELD( CBasePlayer, m_iSuitPlayNext, FIELD_INTEGER ), + DEFINE_ARRAY( CBasePlayer, m_rgiSuitNoRepeat, FIELD_INTEGER, CSUITNOREPEAT ), + DEFINE_ARRAY( CBasePlayer, m_rgflSuitNoRepeatTime, FIELD_TIME, CSUITNOREPEAT ), + DEFINE_FIELD( CBasePlayer, m_lastDamageAmount, FIELD_INTEGER ), + + DEFINE_ARRAY( CBasePlayer, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ), + DEFINE_FIELD( CBasePlayer, m_pActiveItem, FIELD_CLASSPTR ), + DEFINE_FIELD( CBasePlayer, m_pLastItem, FIELD_CLASSPTR ), + + DEFINE_ARRAY( CBasePlayer, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ), + DEFINE_FIELD( CBasePlayer, m_idrowndmg, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_idrownrestored, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_tSneaking, FIELD_TIME ), + + DEFINE_FIELD( CBasePlayer, m_iTrain, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_bitsHUDDamage, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_flFallVelocity, FIELD_FLOAT ), + DEFINE_FIELD( CBasePlayer, m_iTargetVolume, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_iWeaponVolume, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_iExtraSoundTypes, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_iWeaponFlash, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_fLongJump, FIELD_BOOLEAN ), + DEFINE_FIELD( CBasePlayer, m_fInitHUD, FIELD_BOOLEAN ), + DEFINE_FIELD( CBasePlayer, m_tbdPrev, FIELD_TIME ), + + DEFINE_FIELD( CBasePlayer, m_pTank, FIELD_EHANDLE ), + DEFINE_FIELD( CBasePlayer, m_iHideHUD, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_iFOV, FIELD_INTEGER ), +}; + +void CBasePlayer :: Pain( void ) +{ + float flRndSound;//sound randomizer + + flRndSound = RANDOM_FLOAT ( 0 , 1 ); + + if ( flRndSound <= 0.33 ) + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM); + else if ( flRndSound <= 0.66 ) + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM); + else + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM); +} + +/* + * + */ +Vector VecVelocityForDamage(float flDamage) +{ + Vector vec(RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); + + if (flDamage > -50) + vec = vec * 0.7; + else if (flDamage > -200) + vec = vec * 2; + else + vec = vec * 10; + + return vec; +} + +#if 0 /* +static void ThrowGib(entvars_t *pev, char *szGibModel, float flDamage) +{ + edict_t *pentNew = CREATE_ENTITY(); + entvars_t *pevNew = VARS(pentNew); + + pevNew->origin = pev->origin; + SET_MODEL(ENT(pevNew), szGibModel); + UTIL_SetSize(pevNew, g_vecZero, g_vecZero); + + pevNew->velocity = VecVelocityForDamage(flDamage); + pevNew->movetype = MOVETYPE_BOUNCE; + pevNew->solid = SOLID_NOT; + pevNew->avelocity.x = RANDOM_FLOAT(0,600); + pevNew->avelocity.y = RANDOM_FLOAT(0,600); + pevNew->avelocity.z = RANDOM_FLOAT(0,600); + CHANGE_METHOD(ENT(pevNew), em_think, SUB_Remove); + pevNew->ltime = gpGlobals->time; + pevNew->nextthink = gpGlobals->time + RANDOM_FLOAT(10,20); + pevNew->frame = 0; + pevNew->flags = 0; +} + + +static void ThrowHead(entvars_t *pev, char *szGibModel, floatflDamage) +{ + SET_MODEL(ENT(pev), szGibModel); + pev->frame = 0; + pev->nextthink = -1; + pev->movetype = MOVETYPE_BOUNCE; + pev->takedamage = DAMAGE_NO; + pev->solid = SOLID_NOT; + pev->view_ofs = Vector(0,0,8); + UTIL_SetSize(pev, Vector(-16,-16,0), Vector(16,16,56)); + pev->velocity = VecVelocityForDamage(flDamage); + pev->avelocity = RANDOM_FLOAT(-1,1) * Vector(0,600,0); + pev->origin.z -= 24; + ClearBits(pev->flags, FL_ONGROUND); +} + + +*/ +#endif + +int TrainSpeed(int iSpeed, int iMax) +{ + float fSpeed, fMax; + int iRet = 0; + + fMax = (float)iMax; + fSpeed = iSpeed; + + fSpeed = fSpeed/fMax; + + if (iSpeed < 0) + iRet = TRAIN_BACK; + else if (iSpeed == 0) + iRet = TRAIN_NEUTRAL; + else if (fSpeed < 0.33) + iRet = TRAIN_SLOW; + else if (fSpeed < 0.66) + iRet = TRAIN_MEDIUM; + else + iRet = TRAIN_FAST; + + return iRet; +} + +void CBasePlayer :: DeathSound( void ) +{ + // water death sounds + /* + if (pev->waterlevel == 3) + { + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/h2odeath.wav", 1, ATTN_NONE); + return; + } + */ + + // temporarily using pain sounds for death sounds + switch (RANDOM_LONG(1,5)) + { + case 1: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM); + break; + case 2: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM); + break; + case 3: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM); + break; + } + + // play one of the suit death alarms + //EMIT_GROUPNAME_SUIT(ENT(pev), "HEV_DEAD"); +} + +// override takehealth +// bitsDamageType indicates type of damage healed. + +int CBasePlayer :: TakeHealth( float flHealth, int bitsDamageType ) +{ + return CBaseMonster :: TakeHealth (flHealth, bitsDamageType); + +} + +Vector CBasePlayer :: GetGunPosition( ) +{ +// UTIL_MakeVectors(pev->v_angle); +// m_HackedGunPos = pev->view_ofs; + Vector origin; + + origin = pev->origin + pev->view_ofs; + + return origin; +} + +//========================================================= +// TraceAttack +//========================================================= +void CBasePlayer :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if ( pev->takedamage ) + { + m_LastHitGroup = ptr->iHitgroup; + + switch ( ptr->iHitgroup ) + { + case HITGROUP_GENERIC: + break; + case HITGROUP_HEAD: + flDamage *= gSkillData.plrHead; + break; + case HITGROUP_CHEST: + flDamage *= gSkillData.plrChest; + break; + case HITGROUP_STOMACH: + flDamage *= gSkillData.plrStomach; + break; + case HITGROUP_LEFTARM: + case HITGROUP_RIGHTARM: + flDamage *= gSkillData.plrArm; + break; + case HITGROUP_LEFTLEG: + case HITGROUP_RIGHTLEG: + flDamage *= gSkillData.plrLeg; + break; + default: + break; + } + + SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. + TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); + AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); + } +} + +void CBasePlayer::SpawnClientSideCorpse() +{ +// char *infobuffer = g_engfuncs.pfnGetInfoKeyBuffer( edict() ); +// char *pModel = g_engfuncs.pfnInfoKeyValue( infobuffer, "model" ); +// +// MESSAGE_BEGIN( MSG_ALL, gmsgSendCorpse ); +// WRITE_STRING( pModel ); +// WRITE_LONG ( pev->origin.x * 128.0); +// WRITE_LONG ( pev->origin.y * 128.0); +// WRITE_LONG ( pev->origin.z * 128.0); +// WRITE_COORD ( pev->angles.x ); +// WRITE_COORD ( pev->angles.y ); +// WRITE_COORD ( pev->angles.z ); +// WRITE_LONG ( (pev->animtime - gpGlobals->time) * 100.0f ); +// WRITE_BYTE ( pev->sequence ); +// WRITE_BYTE ( pev->body ); +// MESSAGE_END(); +} + +/* + Take some damage. + NOTE: each call to TakeDamage with bitsDamageType set to a time-based damage + type will cause the damage time countdown to be reset. Thus the ongoing effects of poison, radiation + etc are implemented with subsequent calls to TakeDamage using DMG_GENERIC. +*/ + +int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // have suit diagnose the problem - ie: report damage type + int bitsDamage = bitsDamageType; + int ffound = TRUE; + int fmajor; + int fcritical; + int fTookDamage; + int ftrivial; + //float flRatio; + //float flBonus; + float flHealthPrev = pev->health; + + //flBonus = AvHPlayerUpgrade::GetArmorBonus(this->pev->iuser4); + //flRatio = AvHPlayerUpgrade::GetArmorRatio(this->pev->iuser4); + +// if ( ( bitsDamageType & DMG_BLAST ) && g_pGameRules->IsMultiplayer() ) +// { +// // blasts damage armor more. +// flBonus *= 2; +// } + + // Already dead + if ( !IsAlive() ) + return 0; + // go take the damage first + + + CBaseEntity *pAttacker = NULL; + + if(pevAttacker) + { + pAttacker = CBaseEntity::Instance(pevAttacker); + } + + if ( !g_pGameRules->FPlayerCanTakeDamage( this, pAttacker ) ) + { + // Refuse the damage + return 0; + } + + // keep track of amount of damage last sustained + m_lastDamageAmount = flDamage; + + if(!(bitsDamageType & DMG_IGNOREARMOR)) + { + flDamage = AvHPlayerUpgrade::CalculateDamageLessArmor((AvHUser3)this->pev->iuser3, this->pev->iuser4, flDamage, this->pev->armorvalue, bitsDamageType, GetGameRules()->GetNumActiveHives((AvHTeamNumber)this->pev->team)); + } + else + { + int a = 0; + } + + // Armor. +// if (pev->armorvalue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage! +// { +// float flNew = flDamage * flRatio; +// +// float flArmor; +// +// flArmor = (flDamage - flNew) * flBonus; +// +// // Does this use more armor than we have? +// if (flArmor > pev->armorvalue) +// { +// flArmor = pev->armorvalue; +// flArmor *= (1/flBonus); +// flNew = flDamage - flArmor; +// pev->armorvalue = 0; +// } +// else +// pev->armorvalue -= flArmor; +// +// flDamage = flNew; +// } + + fTookDamage = CBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + + // reset damage time countdown for each type of time based damage player just sustained + + { + for (int i = 0; i < CDMG_TIMEBASED; i++) + if (bitsDamageType & (DMG_PARALYZE << i)) + m_rgbTimeBasedDamage[i] = 0; + } + + // tell director about it + MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR ); + WRITE_BYTE(DIRECTOR_MESSAGE_SIZE); + WRITE_BYTE ( DRC_CMD_EVENT ); // take damage event + WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity + WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) ); // index number of secondary entity + WRITE_LONG( 5 ); // eventflags (priority and flags) + MESSAGE_END(); + + + // how bad is it, doc? + + ftrivial = (pev->health > 75 || m_lastDamageAmount < 5); + fmajor = (m_lastDamageAmount > 25); + fcritical = (pev->health < 30); + + // handle all bits set in this damage message, + // let the suit give player the diagnosis + + // UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash ) + + // UNDONE: still need to record damage and heal messages for the following types + + // DMG_BURN + // DMG_FREEZE + // DMG_BLAST + // DMG_SHOCK + + m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client + m_bitsHUDDamage = -1; // make sure the damage bits get resent + + while (fTookDamage && (!ftrivial || (bitsDamage & DMG_TIMEBASED)) && ffound && bitsDamage) + { + ffound = FALSE; + + if (bitsDamage & DMG_CLUB) + { + if (fmajor) + SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture + bitsDamage &= ~DMG_CLUB; + ffound = TRUE; + } + if (bitsDamage & (DMG_FALL | DMG_CRUSH)) + { + if (fmajor) + SetSuitUpdate("!HEV_DMG5", FALSE, SUIT_NEXT_IN_30SEC); // major fracture + else + SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture + + bitsDamage &= ~(DMG_FALL | DMG_CRUSH); + ffound = TRUE; + } + + if (bitsDamage & DMG_BULLET) + { + if (m_lastDamageAmount > 5) + SetSuitUpdate("!HEV_DMG6", FALSE, SUIT_NEXT_IN_30SEC); // blood loss detected + //else + // SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration + + bitsDamage &= ~DMG_BULLET; + ffound = TRUE; + } + + if (bitsDamage & DMG_SLASH) + { + if (fmajor) + SetSuitUpdate("!HEV_DMG1", FALSE, SUIT_NEXT_IN_30SEC); // major laceration + else + SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration + + bitsDamage &= ~DMG_SLASH; + ffound = TRUE; + } + + if (bitsDamage & DMG_SONIC) + { + if (fmajor) + SetSuitUpdate("!HEV_DMG2", FALSE, SUIT_NEXT_IN_1MIN); // internal bleeding + bitsDamage &= ~DMG_SONIC; + ffound = TRUE; + } + + if (bitsDamage & (DMG_POISON | DMG_PARALYZE)) + { + SetSuitUpdate("!HEV_DMG3", FALSE, SUIT_NEXT_IN_1MIN); // blood toxins detected + bitsDamage &= ~(DMG_POISON | DMG_PARALYZE); + ffound = TRUE; + } + + if (bitsDamage & DMG_ACID) + { + SetSuitUpdate("!HEV_DET1", FALSE, SUIT_NEXT_IN_1MIN); // hazardous chemicals detected + bitsDamage &= ~DMG_ACID; + ffound = TRUE; + } + + if (bitsDamage & DMG_NERVEGAS) + { + SetSuitUpdate("!HEV_DET0", FALSE, SUIT_NEXT_IN_1MIN); // biohazard detected + bitsDamage &= ~DMG_NERVEGAS; + ffound = TRUE; + } + + if (bitsDamage & DMG_RADIATION) + { + SetSuitUpdate("!HEV_DET2", FALSE, SUIT_NEXT_IN_1MIN); // radiation detected + bitsDamage &= ~DMG_RADIATION; + ffound = TRUE; + } + if (bitsDamage & DMG_SHOCK) + { + bitsDamage &= ~DMG_SHOCK; + ffound = TRUE; + } + } + + pev->punchangle.x = -2; + + if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75) + { + // first time we take major damage... + // turn automedic on if not on + SetSuitUpdate("!HEV_MED1", FALSE, SUIT_NEXT_IN_30MIN); // automedic on + + // give morphine shot if not given recently + SetSuitUpdate("!HEV_HEAL7", FALSE, SUIT_NEXT_IN_30MIN); // morphine shot + } + + if (fTookDamage && !ftrivial && fcritical && flHealthPrev < 75) + { + + // already took major damage, now it's critical... + if (pev->health < 6) + SetSuitUpdate("!HEV_HLTH3", FALSE, SUIT_NEXT_IN_10MIN); // near death + else if (pev->health < 20) + SetSuitUpdate("!HEV_HLTH2", FALSE, SUIT_NEXT_IN_10MIN); // health critical + + // give critical health warnings + if (!RANDOM_LONG(0,3) && flHealthPrev < 50) + SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention + } + + // if we're taking time based damage, warn about its continuing effects + if (fTookDamage && (bitsDamageType & DMG_TIMEBASED) && flHealthPrev < 75) + { + if (flHealthPrev < 50) + { + if (!RANDOM_LONG(0,3)) + SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention + } + else + SetSuitUpdate("!HEV_HLTH1", FALSE, SUIT_NEXT_IN_10MIN); // health dropping + } + + //return fTookDamage; + int theReturnValue = 0; + if(fTookDamage) + { + theReturnValue = flDamage; + } + return theReturnValue; +} + +//========================================================= +// PackDeadPlayerItems - call this when a player dies to +// pack up the appropriate weapons and ammo items, and to +// destroy anything that shouldn't be packed. +// +// This is pretty brute force :( +//========================================================= +void CBasePlayer::PackDeadPlayerItems( void ) +{ + int iWeaponRules; + int iAmmoRules; + int i; + CBasePlayerWeapon *rgpPackWeapons[ 20 ];// 20 hardcoded for now. How to determine exactly how many weapons we have? + int iPackAmmo[ MAX_AMMO_SLOTS + 1]; + int iPW = 0;// index into packweapons array + int iPA = 0;// index into packammo array + + memset(rgpPackWeapons, NULL, sizeof(rgpPackWeapons) ); + memset(iPackAmmo, -1, sizeof(iPackAmmo) ); + + // get the game rules + iWeaponRules = g_pGameRules->DeadPlayerWeapons( this ); + iAmmoRules = g_pGameRules->DeadPlayerAmmo( this ); + + if ( iWeaponRules == GR_PLR_DROP_GUN_NO && iAmmoRules == GR_PLR_DROP_AMMO_NO ) + { + // nothing to pack. Remove the weapons and return. Don't call create on the box! + RemoveAllItems( TRUE ); + return; + } + +// go through all of the weapons and make a list of the ones to pack + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + if ( m_rgpPlayerItems[ i ] ) + { + // there's a weapon here. Should I pack it? + CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[ i ]; + + while ( pPlayerItem ) + { + switch( iWeaponRules ) + { + case GR_PLR_DROP_GUN_ACTIVE: + if ( m_pActiveItem && pPlayerItem == m_pActiveItem ) + { + // this is the active item. Pack it. + rgpPackWeapons[ iPW++ ] = (CBasePlayerWeapon *)pPlayerItem; + } + break; + + case GR_PLR_DROP_GUN_ALL: + rgpPackWeapons[ iPW++ ] = (CBasePlayerWeapon *)pPlayerItem; + break; + + default: + break; + } + + pPlayerItem = pPlayerItem->m_pNext; + } + } + } + +// now go through ammo and make a list of which types to pack. + if ( iAmmoRules != GR_PLR_DROP_AMMO_NO ) + { + for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) + { + if ( m_rgAmmo[ i ] > 0 ) + { + // player has some ammo of this type. + switch ( iAmmoRules ) + { + case GR_PLR_DROP_AMMO_ALL: + iPackAmmo[ iPA++ ] = i; + break; + + case GR_PLR_DROP_AMMO_ACTIVE: + if ( m_pActiveItem && i == m_pActiveItem->PrimaryAmmoIndex() ) + { + // this is the primary ammo type for the active weapon + iPackAmmo[ iPA++ ] = i; + } + else if ( m_pActiveItem && i == m_pActiveItem->SecondaryAmmoIndex() ) + { + // this is the secondary ammo type for the active weapon + iPackAmmo[ iPA++ ] = i; + } + break; + + default: + break; + } + } + } + } + +// create a box to pack the stuff into. + CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin, pev->angles, edict() ); + + pWeaponBox->pev->angles.x = 0;// don't let weaponbox tilt. + pWeaponBox->pev->angles.z = 0; + + pWeaponBox->SetThink( &CWeaponBox::Kill ); + pWeaponBox->pev->nextthink = gpGlobals->time + 120; + +// back these two lists up to their first elements + iPA = 0; + iPW = 0; + +// pack the ammo + while ( iPackAmmo[ iPA ] != -1 ) + { + // Only pack items with ammo + const char* theAmmoName = CBasePlayerItem::AmmoInfoArray[ iPackAmmo[ iPA ] ].pszName; + if(theAmmoName && theAmmoName != "") + pWeaponBox->PackAmmo( MAKE_STRING(theAmmoName), m_rgAmmo[ iPackAmmo[ iPA ] ] ); + + iPA++; + } + +// now pack all of the items in the lists + while ( rgpPackWeapons[ iPW ] ) + { + // weapon unhooked from the player. Pack it into der box. + pWeaponBox->PackWeapon( rgpPackWeapons[ iPW ] ); + + iPW++; + } + + pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some. + + RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above. +} + +void CBasePlayer::DestroyAllItems(BOOL removeSuit) +{ + if (m_pActiveItem) + { + ResetAutoaim( ); + m_pActiveItem->Holster( ); + m_pActiveItem = NULL; + } + + m_pLastItem = NULL; + + int i; + CBasePlayerItem *pPendingItem; + for (i = 0; i < MAX_ITEM_TYPES; i++) + { + m_pActiveItem = m_rgpPlayerItems[i]; + while (m_pActiveItem) + { + pPendingItem = m_pActiveItem->m_pNext; + m_pActiveItem->DestroyItem(); + m_pActiveItem = pPendingItem; + } + m_rgpPlayerItems[i] = NULL; + } + m_pActiveItem = NULL; + + pev->viewmodel = 0; + pev->weaponmodel = 0; + + if ( removeSuit ) + pev->weapons = 0; + else + pev->weapons &= ~WEAPON_ALLWEAPONS; + + for ( i = 0; i < MAX_AMMO_SLOTS;i++) + m_rgAmmo[i] = 0; + + // send Selected Weapon Message to our client + NetMsg_CurWeapon( pev, 0, 0, 0 ); +} + +void CBasePlayer::RemoveAllItems( BOOL removeSuit ) +{ + if (m_pActiveItem) + { + ResetAutoaim( ); + m_pActiveItem->Holster( ); + m_pActiveItem = NULL; + } + + m_pLastItem = NULL; + + int i; + CBasePlayerItem *pPendingItem; + for (i = 0; i < MAX_ITEM_TYPES; i++) + { + m_pActiveItem = m_rgpPlayerItems[i]; + while (m_pActiveItem) + { + pPendingItem = m_pActiveItem->m_pNext; + m_pActiveItem->Drop( ); + m_pActiveItem = pPendingItem; + } + m_rgpPlayerItems[i] = NULL; + } + m_pActiveItem = NULL; + + pev->viewmodel = 0; + pev->weaponmodel = 0; + + if ( removeSuit ) + pev->weapons = 0; + else + pev->weapons &= ~WEAPON_ALLWEAPONS; + + for ( i = 0; i < MAX_AMMO_SLOTS;i++) + m_rgAmmo[i] = 0; + +// UpdateClientData(); +// // send Selected Weapon Message to our client +// MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev ); +// WRITE_BYTE(0); +// WRITE_BYTE(0); +// WRITE_BYTE(0); +// MESSAGE_END(); +} + +/* + * GLOBALS ASSUMED SET: g_ulModelIndexPlayer + * + * ENTITY_METHOD(PlayerDie) + */ +entvars_t *g_pevLastInflictor; // Set in combat.cpp. Used to pass the damage inflictor for death messages. + // Better solution: Add as parameter to all Killed() functions. + +void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) +{ + CSound *pSound; + + // Holster weapon immediately, to allow it to cleanup + if ( m_pActiveItem ) + m_pActiveItem->Holster( ); + + GetGameRules()->PlayerKilled( this, pevAttacker, g_pevLastInflictor ); + + if ( m_pTank != NULL ) + { + m_pTank->Use( this, this, USE_OFF, 0 ); + m_pTank = NULL; + } + + // this client isn't going to be thinking for a while, so reset the sound until they respawn + pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) ); + { + if ( pSound ) + { + pSound->Reset(); + } + } + + SetAnimation( PLAYER_DIE ); + + m_iRespawnFrames = 0; + + //pev->modelindex = g_ulModelIndexPlayer; // don't use eyes + + // Clear buttons held down on death, fixes bug where player can't switch observer targets + m_afButtonLast = 0; + + pev->deadflag = DEAD_DYING; + pev->movetype = MOVETYPE_TOSS; + ClearBits( pev->flags, FL_ONGROUND ); + if (pev->velocity.z < 10) + pev->velocity.z += RANDOM_FLOAT(0,300); + + // clear out the suit message cache so we don't keep chattering + SetSuitUpdate(NULL, FALSE, 0); + + // send "health" update message to zero + m_iClientHealth = 0; + NetMsg_Health( pev, m_iClientHealth ); + + // Tell Ammo Hud that the player is dead + NetMsg_CurWeapon( pev, 0, 0xFF, 0xFF ); + + // reset FOV + pev->fov = m_iFOV = m_iClientFOV = 0; + NetMsg_SetFOV( pev, 0 ); + + + // UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12 + // UTIL_ScreenFade( edict(), Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE ); + + if ( ( pev->health < -40 && iGib != GIB_NEVER ) || iGib == GIB_ALWAYS ) + { + pev->solid = SOLID_NOT; + GibMonster(); // This clears pev->model + pev->effects |= EF_NODRAW; + return; + } + + DeathSound(); + + pev->angles.x = 0; + pev->angles.z = 0; + + SetThink(&CBasePlayer::PlayerDeathThink); + pev->nextthink = gpGlobals->time + 0.1; +} + +void CBasePlayer::Suicide(void) +{ + AvHPlayer* thePlayer = dynamic_cast(this); + + ASSERT(thePlayer); + + if(thePlayer && thePlayer->GetUsedKilled())//: prevent exploitation of "kill" command. + return; + + // have the player kill themself + float theKillDelay = ns_cvar_float(&avh_killdelay); + + #ifdef DEBUG + #ifndef AVH_EXTERNAL_BUILD + theKillDelay = 0; + #endif + #endif + + if(theKillDelay > 0.0f) + { + char theMessage[256]; + sprintf(theMessage, "Suiciding in %.1f seconds...\n", theKillDelay); + UTIL_SayText(theMessage, this, ENTINDEX(this->edict())); + } + + thePlayer->SetUsedKilled(true); + SetThink(&CBasePlayer::SuicideThink); + this->pev->nextthink = gpGlobals->time + theKillDelay; +} + +void CBasePlayer::SuicideThink(void) +{ + AvHPlayer* thePlayer = dynamic_cast(this); + if(thePlayer && thePlayer->GetCanBeAffectedByEnemies()) + { + this->pev->health = 0; + this->Killed(this->pev, GIB_NEVER); + } +} + +// Set the activity based on an event or current state +void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim ) +{ + int animDesired; + int gaitDesired; + float speed; + char szAnim[64]; + bool theFoundAnim = true; + int theDebugAnimations = BALANCE_VAR(kDebugAnimations); + bool reloadAnim = false; + + // Make sure the model is set, as gestating models aren't set before the animation starts playing + AvHPlayer* theAvHPlayer = dynamic_cast(this); + ASSERT(theAvHPlayer); + theAvHPlayer->SetModelFromState(); + + speed = pev->velocity.Length2D(); + + if (pev->flags & FL_FROZEN) + { + speed = 0; + playerAnim = PLAYER_IDLE; + } + + switch (playerAnim) + { + case PLAYER_JUMP: + m_IdealActivity = ACT_HOP; + break; + + case PLAYER_SUPERJUMP: + m_IdealActivity = ACT_LEAP; + break; + + case PLAYER_DIE: + m_IdealActivity = ACT_DIESIMPLE; + m_IdealActivity = GetDeathActivity( ); + + //this->GetAnimationForActivity(this->m_IdealActivity, szAnim); + //animDesired = LookupSequence( szAnim ); + //if (animDesired == -1) + //{ + // animDesired = 0; + //} + //this->pev->sequence = animDesired; + break; + + case PLAYER_ATTACK1: + switch( m_Activity ) + { + case ACT_HOVER: + case ACT_SWIM: + case ACT_HOP: + case ACT_LEAP: + case ACT_DIESIMPLE: + m_IdealActivity = m_Activity; + break; + default: + m_IdealActivity = ACT_RANGE_ATTACK1; + break; + } + break; + + case PLAYER_PRIME: + switch( m_Activity ) + { + case ACT_HOVER: + case ACT_SWIM: + case ACT_HOP: + case ACT_LEAP: + case ACT_DIESIMPLE: + m_IdealActivity = m_Activity; + break; + default: + m_IdealActivity = ACT_RANGE_PRIME; + break; + } + break; + + case PLAYER_RELOAD: + switch( m_Activity ) + { + case ACT_HOVER: + case ACT_SWIM: + case ACT_HOP: + case ACT_LEAP: + case ACT_DIESIMPLE: + m_IdealActivity = m_Activity; + break; + default: + m_IdealActivity = ACT_RELOAD; + break; + } + break; + case PLAYER_RELOAD_START: + switch( m_Activity ) + { + case ACT_HOVER: + case ACT_SWIM: + case ACT_HOP: + case ACT_LEAP: + case ACT_DIESIMPLE: + m_IdealActivity = m_Activity; + break; + default: + m_IdealActivity = ACT_RELOAD_START; + break; + } + break; + case PLAYER_RELOAD_INSERT: + switch( m_Activity ) + { + case ACT_HOVER: + case ACT_SWIM: + case ACT_HOP: + case ACT_LEAP: + case ACT_DIESIMPLE: + m_IdealActivity = m_Activity; + break; + default: + m_IdealActivity = ACT_RELOAD_INSERT; + break; + } + break; + case PLAYER_RELOAD_END: + switch( m_Activity ) + { + case ACT_HOVER: + case ACT_SWIM: + case ACT_HOP: + case ACT_LEAP: + case ACT_DIESIMPLE: + m_IdealActivity = m_Activity; + break; + default: + m_IdealActivity = ACT_RELOAD_END; + break; + } + break; + case PLAYER_IDLE: + case PLAYER_WALK: + if ( !FBitSet( pev->flags, FL_ONGROUND ) && (m_Activity == ACT_HOP || m_Activity == ACT_LEAP) ) // Still jumping + { + m_IdealActivity = m_Activity; + } + else if ( pev->waterlevel > 1 ) + { + if ( speed == 0 ) + m_IdealActivity = ACT_HOVER; + else + m_IdealActivity = ACT_SWIM; + } + else + { + if((playerAnim == PLAYER_IDLE) && !strcmp(this->m_szAnimExtention, "") && (theAvHPlayer->GetPlayMode() != PLAYMODE_READYROOM)) + { + m_IdealActivity = ACT_IDLE; + } + else + { + m_IdealActivity = ACT_WALK; + } + } + break; + } + + switch (m_IdealActivity) + { + + case ACT_DIESIMPLE: + case ACT_DIEBACKWARD: + case ACT_DIEFORWARD: + case ACT_DIEVIOLENT: + default: + if ( m_Activity == m_IdealActivity) + return; + m_Activity = m_IdealActivity; + + //animDesired = LookupActivity( m_Activity ); + this->GetAnimationForActivity(this->m_Activity, szAnim); + animDesired = LookupSequence( szAnim ); + + // Already using the desired animation? + if (pev->sequence == animDesired) + return; + + #ifdef DEBUG + if(theDebugAnimations) + { + char theMessage[256]; + sprintf(theMessage, "Setting full-body animation (%s): %d (no gaitsequence)\n", szAnim, animDesired); + ALERT(at_console, theMessage); + } + #endif + + pev->gaitsequence = 0; + pev->sequence = animDesired; + pev->frame = 0; + ResetSequenceInfo( ); + return; + + // create seperate case for these so that they don't interfere with a reload - Elven + case ACT_HOVER: + case ACT_LEAP: + case ACT_SWIM: + case ACT_HOP: + reloadAnim = (m_Activity == ACT_RELOAD) || (m_Activity == ACT_RELOAD_START) || (m_Activity == ACT_RELOAD_INSERT) || (m_Activity == ACT_RELOAD_END); + if ( m_Activity == m_IdealActivity || reloadAnim) + return; + m_Activity = m_IdealActivity; + + //animDesired = LookupActivity( m_Activity ); + this->GetAnimationForActivity(this->m_Activity, szAnim); + animDesired = LookupSequence( szAnim ); + + // Already using the desired animation? + if (pev->sequence == animDesired) + return; + pev->gaitsequence = 0; + pev->sequence = animDesired; + pev->frame = 0; + ResetSequenceInfo( ); + return; + + + case ACT_RANGE_ATTACK1: + case ACT_RANGE_PRIME: + this->GetAnimationForActivity(this->m_IdealActivity, szAnim); + animDesired = LookupSequence( szAnim ); + + if (animDesired == -1) + { + animDesired = 0; + theFoundAnim = false; + } + + if ( pev->sequence != animDesired || !m_fSequenceLoops ) + { + pev->frame = 0; + } + + if (!m_fSequenceLoops) + { + pev->effects |= EF_NOINTERP; + } + + m_Activity = m_IdealActivity; + + if(theFoundAnim) + { + #ifdef DEBUG + if(theDebugAnimations) + { + char theMessage[256]; + sprintf(theMessage, "%s%s\n", "Setting attack animation: ", szAnim); + ALERT(at_console, theMessage); + } + #endif + } + + pev->sequence = animDesired; + ResetSequenceInfo( ); + break; + + case ACT_RELOAD: + case ACT_RELOAD_START: + case ACT_RELOAD_INSERT: + case ACT_RELOAD_END: + this->GetAnimationForActivity(this->m_IdealActivity, szAnim); + animDesired = LookupSequence( szAnim ); + + if ( pev->sequence != animDesired || !m_fSequenceLoops ) + { + pev->frame = 0; + } + m_Activity = m_IdealActivity; + break; + + + + case ACT_WALK: + reloadAnim = (m_Activity == ACT_RELOAD) || (m_Activity == ACT_RELOAD_START) || + (m_Activity == ACT_RELOAD_INSERT) || (m_Activity == ACT_RELOAD_END); + if ((m_Activity != ACT_RANGE_ATTACK1 && m_Activity != ACT_RANGE_PRIME && !reloadAnim/*m_Activity != ACT_RELOAD*/ ) || m_fSequenceFinished) + { + this->GetAnimationForActivity(this->m_IdealActivity, szAnim); + animDesired = LookupSequence( szAnim ); + if (animDesired == -1) + { + animDesired = 0; + } + m_Activity = ACT_WALK; + } + else + { + animDesired = pev->sequence; + } + break; + } + + // Now handle gaitsequence + if(FBitSet(this->pev->flags, FL_DUCKING)) + { + if(speed == 0) + { + this->GetAnimationForActivity(ACT_CROUCHIDLE, szAnim, true); + } + else + { + this->GetAnimationForActivity(ACT_CROUCH, szAnim, true); + } + } + else if (speed > this->GetMaxWalkSpeed() ) + { + this->GetAnimationForActivity(ACT_RUN, szAnim, true); + } + else if (speed > 0) + { + this->GetAnimationForActivity(ACT_WALK, szAnim, true); + } + else + { + this->GetAnimationForActivity(ACT_IDLE, szAnim, true); + } + + // Set the gaitsequence + gaitDesired = LookupSequence(szAnim, 1); + if(gaitDesired == -1) + { + gaitDesired = 0; + } + +#ifdef DEBUG + if(theDebugAnimations) + { + if((this->pev->gaitsequence != gaitDesired) || (this->pev->sequence != animDesired)) + { + ALERT(at_console, "Anim desired (%s): %d, gaitsequence: %d gaitdesired: %d \n", szAnim, animDesired, pev->gaitsequence, gaitDesired); + } + } +#endif + + this->pev->gaitsequence = gaitDesired; + + AvHPlayer* thePlayer = dynamic_cast(this); + if(thePlayer && (thePlayer->GetPlayMode() == PLAYMODE_READYROOM)) + { + // Play some animation on top and bottom when not holding any weapons + animDesired = gaitDesired; + } + + // Already using the desired animation? + if (pev->sequence == animDesired) + return; + + // Reset to first frame of desired animation + pev->sequence = animDesired; + pev->frame = 0; + ResetSequenceInfo( ); +} + +/* +=========== +TabulateAmmo +This function is used to find and store +all the ammo we have into the ammo vars. +============ +*/ +void CBasePlayer::TabulateAmmo() +{ + ammo_9mm = AmmoInventory( GetAmmoIndex( "9mm" ) ); + ammo_357 = AmmoInventory( GetAmmoIndex( "357" ) ); + ammo_argrens = AmmoInventory( GetAmmoIndex( "ARgrenades" ) ); + ammo_bolts = AmmoInventory( GetAmmoIndex( "bolts" ) ); + ammo_buckshot = AmmoInventory( GetAmmoIndex( "buckshot" ) ); + ammo_rockets = AmmoInventory( GetAmmoIndex( "rockets" ) ); + ammo_uranium = AmmoInventory( GetAmmoIndex( "uranium" ) ); + ammo_hornets = AmmoInventory( GetAmmoIndex( "Hornets" ) ); +} + + +/* +=========== +WaterMove +============ +*/ +#define AIRTIME 12 // lung full of air lasts this many seconds + +void CBasePlayer::WaterMove() +{ + int air; + + if (pev->movetype == MOVETYPE_NOCLIP) + return; + + if (pev->health < 0) + return; + + // waterlevel 0 - not in water + // waterlevel 1 - feet in water + // waterlevel 2 - waist in water + // waterlevel 3 - head in water + + if (pev->waterlevel != 3) + { + // not underwater + + // play 'up for air' sound + if (pev->air_finished < gpGlobals->time) + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade1.wav", 1, ATTN_NORM); + else if (pev->air_finished < gpGlobals->time + 9) + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade2.wav", 1, ATTN_NORM); + + pev->air_finished = gpGlobals->time + AIRTIME; + pev->dmg = 2; + + // if we took drowning damage, give it back slowly + if (m_idrowndmg > m_idrownrestored) + { + // set drowning damage bit. hack - dmg_drownrecover actually + // makes the time based damage code 'give back' health over time. + // make sure counter is cleared so we start count correctly. + + // NOTE: this actually causes the count to continue restarting + // until all drowning damage is healed. + + m_bitsDamageType |= DMG_DROWNRECOVER; + m_bitsDamageType &= ~DMG_DROWN; + m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; + } + + } + else + { // fully under water + // stop restoring damage while underwater + m_bitsDamageType &= ~DMG_DROWNRECOVER; + m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; + + if (pev->air_finished < gpGlobals->time) // drown! + { + if (pev->pain_finished < gpGlobals->time) + { + // take drowning damage + pev->dmg += 1; + if (pev->dmg > 5) + pev->dmg = 5; + TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), pev->dmg, DMG_DROWN); + pev->pain_finished = gpGlobals->time + 1; + + // track drowning damage, give it back when + // player finally takes a breath + + m_idrowndmg += pev->dmg; + } + } + else + { + m_bitsDamageType &= ~DMG_DROWN; + } + } + + if (!pev->waterlevel) + { + if (FBitSet(pev->flags, FL_INWATER)) + { + ClearBits(pev->flags, FL_INWATER); + } + return; + } + + // make bubbles + + air = (int)(pev->air_finished - gpGlobals->time); + if (!RANDOM_LONG(0,0x1f) && RANDOM_LONG(0,AIRTIME-1) >= air) + { + switch (RANDOM_LONG(0,3)) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim1.wav", 0.8, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim2.wav", 0.8, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim3.wav", 0.8, ATTN_NORM); break; + case 3: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim4.wav", 0.8, ATTN_NORM); break; + } + } + + if (pev->watertype == CONTENT_LAVA) // do damage + { + if (pev->dmgtime < gpGlobals->time) + TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 10 * pev->waterlevel, DMG_BURN); + } + else if (pev->watertype == CONTENT_SLIME) // do damage + { + pev->dmgtime = gpGlobals->time + 1; + TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 4 * pev->waterlevel, DMG_ACID); + } + + if (!FBitSet(pev->flags, FL_INWATER)) + { + SetBits(pev->flags, FL_INWATER); + pev->dmgtime = 0; + } +} + +void CBasePlayer::InitPlayerFromSpawn(edict_t* inSpawn) +{ + if(!FNullEnt(inSpawn)) + { + int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(this->pev->iuser3)); + this->pev->origin = VARS(inSpawn)->origin + Vector(0, 0, theOffset); + + this->pev->v_angle = VARS(inSpawn)->v_angle; + this->pev->velocity = g_vecZero; + this->pev->angles = VARS(inSpawn)->angles; + this->pev->punchangle = g_vecZero; + this->pev->fixangle = TRUE; + } +} + +// TRUE if the player is attached to a ladder +BOOL CBasePlayer::IsOnLadder( void ) +{ + return ( pev->movetype == MOVETYPE_FLY ); +} + +void CBasePlayer::PlayerDeathThink(void) +{ + float flForward; + + if (FBitSet(pev->flags, FL_ONGROUND)) + { + flForward = pev->velocity.Length() - 20; + if (flForward <= 0) + pev->velocity = g_vecZero; + else + pev->velocity = flForward * pev->velocity.Normalize(); + } + + if ( HasWeapons() ) + { + // we drop the guns here because weapons that have an area effect and can kill their user + // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the + // player class sometimes is freed. It's safer to manipulate the weapons once we know + // we aren't calling into any of their code anymore through the player pointer. + PackDeadPlayerItems(); + + // Assumes that all players have at least one weapon when they die + //SpawnClientSideCorpse(); + } + + if (pev->modelindex && (!m_fSequenceFinished) && (pev->deadflag == DEAD_DYING)) + { + StudioFrameAdvance( ); + + m_iRespawnFrames++; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands + if ( m_iRespawnFrames < 120 ) // Animations should be no longer than this + return; + } + + // once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore + // this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn + if ( pev->movetype != MOVETYPE_NONE && FBitSet(pev->flags, FL_ONGROUND) ) + pev->movetype = MOVETYPE_NONE; + + if (pev->deadflag == DEAD_DYING) + pev->deadflag = DEAD_DEAD; + + StopAnimation(); + + pev->effects |= EF_NOINTERP; + pev->framerate = 0.0; + + BOOL fAnyButtonDown = (pev->button & ~IN_SCORE ); + + // wait for all buttons released + if (pev->deadflag == DEAD_DEAD) + { + //if (fAnyButtonDown) + // return; + + //if ( g_pGameRules->FPlayerCanRespawn( this ) ) + //{ + m_fDeadTime = gpGlobals->time; + pev->deadflag = DEAD_RESPAWNABLE; + //} + + return; + } + +// if the player has been dead for one second longer than allowed by forcerespawn, +// forcerespawn isn't on. Send the player off to an intermission camera until they +// choose to respawn. + //if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->time > (m_fDeadTime + 6) ) && !(m_afPhysicsFlags & PFLAG_OBSERVER) ) + //{ + // // go to dead camera. + // StartDeathCam(); + //} + + + // From Counter-strike + // if the player has been dead for one second longer than allowed by forcerespawn, + // forcerespawn isn't on. Send the player off to an intermission camera until they + // choose to respawn. +// if ( g_pGameRules->IsMultiplayer() && +// ( gpGlobals->time > (m_fDeadTime + 3) ) && +// !( m_afPhysicsFlags & PFLAG_OBSERVER) ) +// { +// //Send message to everybody to spawn a corpse. +// SpawnClientSideCorpse(); +// +// // go to dead camera. +// //StartDeathCam(); +// } + + const float kMinDeathTime = .5f; + +// wait for any button down, or mp_forcerespawn is set and the respawn time is up + if ((!fAnyButtonDown || ( gpGlobals->time < (m_fDeadTime + kMinDeathTime) )) + && !( g_pGameRules->IsMultiplayer() && forcerespawn.value > 0 && (gpGlobals->time > (m_fDeadTime + kMinDeathTime))) ) + return; + + pev->button = 0; + m_iRespawnFrames = 0; + + // Player is done with the death cam, continue + AvHPlayer* thePlayer = dynamic_cast(this); + AvHGamerules* theGameRules = dynamic_cast(g_pGameRules); + + theGameRules->PlayerDeathEnd(thePlayer); +} + +//========================================================= +// StartDeathCam - find an intermission spot and send the +// player off into observer mode +//========================================================= +void CBasePlayer::StartDeathCam( void ) +{ + edict_t *pSpot, *pNewSpot; + int iRand; + + if ( pev->view_ofs == g_vecZero ) + { + // don't accept subsequent attempts to StartDeathCam() + return; + } + + pSpot = FIND_ENTITY_BY_CLASSNAME( NULL, "info_intermission"); + + if ( !FNullEnt( pSpot ) ) + { + // at least one intermission spot in the world. + iRand = RANDOM_LONG( 0, 3 ); + + while ( iRand > 0 ) + { + pNewSpot = FIND_ENTITY_BY_CLASSNAME( pSpot, "info_intermission"); + + if ( pNewSpot ) + { + pSpot = pNewSpot; + } + + iRand--; + } + + CopyToBodyQue( pev ); + StartObserver( pSpot->v.origin, pSpot->v.v_angle ); + } + else + { + // no intermission spot. Push them up in the air, looking down at their corpse + TraceResult tr; + CopyToBodyQue( pev ); + UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, 128 ), ignore_monsters, edict(), &tr ); + StartObserver( tr.vecEndPos, UTIL_VecToAngles( tr.vecEndPos - pev->origin ) ); + return; + } +} + +void CBasePlayer::StartObserver( Vector vecPosition, Vector vecViewAngle ) +{ +// m_afPhysicsFlags |= PFLAG_OBSERVER; +// +// pev->view_ofs = g_vecZero; +// pev->angles = pev->v_angle = vecViewAngle; +// pev->fixangle = TRUE; +// pev->solid = SOLID_NOT; +// pev->takedamage = DAMAGE_NO; +// pev->movetype = MOVETYPE_NONE; +// pev->modelindex = 0; +// UTIL_SetOrigin( pev, vecPosition ); + + m_iHideHUD |= (HIDEHUD_HEALTH | HIDEHUD_WEAPONS); + m_afPhysicsFlags |= PFLAG_OBSERVER; + + pev->effects = EF_NODRAW; + pev->view_ofs = g_vecZero; + pev->angles = pev->v_angle = vecViewAngle; + pev->fixangle = TRUE; + pev->solid = SOLID_NOT; + pev->takedamage = DAMAGE_NO; + pev->movetype = MOVETYPE_NONE; + UTIL_SetOrigin( pev, vecPosition ); + + ClearBits( m_afPhysicsFlags, PFLAG_DUCKING ); + ClearBits( pev->flags, FL_DUCKING ); + // pev->flags = FL_CLIENT | FL_SPECTATOR; // Should we set Spectator flag? Or is it reserver for people connecting with spectator 1? + pev->deadflag = DEAD_RESPAWNABLE; + + // Tell the physics code that this player's now in observer mode + Observer_SetMode(this->GetDefaultSpectatingMode()); + Observer_SpectatePlayer(this->GetDefaultSpectatingTarget()); + m_flNextObserverInput = 0; +} + +void CBasePlayer::StopObserver() +{ + this->pev->solid = SOLID_SLIDEBOX; + this->pev->effects = 0; + this->pev->takedamage = DAMAGE_YES; + this->pev->movetype = MOVETYPE_WALK; + ClearBits(this->m_afPhysicsFlags, PFLAG_OBSERVER); + ClearBits(this->m_afPhysicsFlags, PFLAG_DUCKING ); + ClearBits(this->pev->flags, FL_DUCKING ); + this->pev->iuser1 = this->pev->iuser2 = 0; +} + +// +// PlayerUse - handles USE keypress +// +#define PLAYER_SEARCH_RADIUS (float)64 + +void CBasePlayer::ClearKeys() +{ + // clear attack/use commands from player + this->m_afButtonPressed = 0; + this->pev->button = 0; + this->m_afButtonReleased = 0; +} + +void CBasePlayer::PlayerUse ( void ) +{ + AvHPlayer* theAvHPlayer = dynamic_cast(this); + + // Was use pressed or released? + if ( ! ((pev->button | m_afButtonPressed | m_afButtonReleased) & IN_USE) ) + return; + + //: Dont do this on commanders to prevent phantom use sounds. + if(theAvHPlayer->GetIsInTopDownMode()) + return; + + // Hit Use on a train? + if ( m_afButtonPressed & IN_USE ) + { + if ( m_pTank != NULL ) + { + // Stop controlling the tank + // TODO: Send HUD Update + m_pTank->Use( this, this, USE_OFF, 0 ); + m_pTank = NULL; + return; + } + else + { + if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) + { + m_afPhysicsFlags &= ~PFLAG_ONTRAIN; + m_iTrain = TRAIN_NEW|TRAIN_OFF; + return; + } + else + { // Start controlling the train! + CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); + + if ( pTrain && !(pev->button & IN_JUMP) && FBitSet(pev->flags, FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(pev) ) + { + m_afPhysicsFlags |= PFLAG_ONTRAIN; + m_iTrain = TrainSpeed(pTrain->pev->speed, pTrain->pev->impulse); + m_iTrain |= TRAIN_NEW; + EMIT_SOUND( ENT(pev), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM); + return; + } + } + } + } + + CBaseEntity *pObject = NULL; + CBaseEntity *pClosest = NULL; + Vector vecLOS; + float flMaxDot = VIEW_FIELD_NARROW; + float flDot; + + UTIL_MakeVectors ( pev->v_angle );// so we know which way we are facing + + + while ((pObject = UTIL_FindEntityInSphere( pObject, pev->origin, PLAYER_SEARCH_RADIUS )) != NULL) + { + + if (pObject->ObjectCaps() & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE)) + { + // !!!PERFORMANCE- should this check be done on a per case basis AFTER we've determined that + // this object is actually usable? This dot is being done for every object within PLAYER_SEARCH_RADIUS + // when player hits the use key. How many objects can be in that area, anyway? (sjb) + vecLOS = (VecBModelOrigin( pObject->pev ) - (pev->origin + pev->view_ofs)); + + // This essentially moves the origin of the target to the corner nearest the player to test to see + // if it's "hull" is in the view cone + vecLOS = UTIL_ClampVectorToBox( vecLOS, pObject->pev->size * 0.5 ); + + flDot = DotProduct (vecLOS , gpGlobals->v_forward); + if (flDot > flMaxDot ) + {// only if the item is in front of the user + pClosest = pObject; + flMaxDot = flDot; +// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); + } +// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); + } + } + + pObject = pClosest; + + // Add los test for aliens looking at hives. + if ( pObject == NULL && AvHGetIsAlien(this->pev->iuser3) ) { + Vector vecAiming = gpGlobals->v_forward; + Vector vecSrc = this->GetGunPosition( ) + vecAiming; + Vector vecEnd = vecSrc + vecAiming*800; + + TraceResult theTraceResult; + UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, this->edict(), &theTraceResult); + + edict_t* theEntityHit = theTraceResult.pHit; + AvHHive *theHive = dynamic_cast(CBaseEntity::Instance(theEntityHit)); + if ( theHive) { + + float the2DDistance = VectorDistance2D(this->pev->origin, theHive->pev->origin); + + // Enabled state is true + if(the2DDistance <= 150.0 && (this->pev->origin < theHive->pev->origin) ) + { + pObject=theHive; + } + } + } + + // Found an object + if (pObject ) + { + //!!!UNDONE: traceline here to prevent USEing buttons through walls + int caps = pObject->ObjectCaps(); + + if ( m_afButtonPressed & IN_USE ) + { + //EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_select.wav", 0.4, ATTN_NORM); + const char* theSound = AvHSHUGetCommonSoundName(theAvHPlayer->GetIsAlien(), WEAPON_SOUND_SELECT); + EMIT_SOUND( ENT(pev), CHAN_ITEM, theSound, 0.4, ATTN_NORM); + } + + if ( ( (pev->button & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) || + ( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) ) + { + if ( caps & FCAP_CONTINUOUS_USE ) + m_afPhysicsFlags |= PFLAG_USING; + + pObject->Use( this, this, USE_SET, 1 ); + } + // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away + else if ( (m_afButtonReleased & IN_USE) && (pObject->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use + { + pObject->Use( this, this, USE_SET, 0 ); + } + } + else + { + if ( m_afButtonPressed & IN_USE ) + { + //EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_denyselect.wav", 0.4, ATTN_NORM); + const char* theSound = AvHSHUGetCommonSoundName(theAvHPlayer->GetIsAlien(), WEAPON_SOUND_DENYSELECT); + EMIT_SOUND( ENT(pev), CHAN_ITEM, theSound, 0.4, ATTN_NORM); + } + } +} + + + +void CBasePlayer::Jump() +{ + Vector vecWallCheckDir;// direction we're tracing a line to find a wall when walljumping + Vector vecAdjustedVelocity; + Vector vecSpot; + TraceResult tr; + + if (FBitSet(pev->flags, FL_WATERJUMP)) + return; + + if (pev->waterlevel >= 2) + { + return; + } + + // jump velocity is sqrt( height * gravity * 2) + + // If this isn't the first frame pressing the jump button, break out. + if ( !FBitSet( m_afButtonPressed, IN_JUMP ) ) + return; // don't pogo stick + + if ( !(pev->flags & FL_ONGROUND) || !pev->groundentity ) + { + return; + } + +// many features in this function use v_forward, so makevectors now. + UTIL_MakeVectors (pev->angles); + + // ClearBits(pev->flags, FL_ONGROUND); // don't stairwalk + + SetAnimation( PLAYER_JUMP ); + + if ( m_fLongJump && + (pev->button & IN_DUCK) && + ( pev->flDuckTime > 0 ) && + pev->velocity.Length() > 50 ) + { + SetAnimation( PLAYER_SUPERJUMP ); + } + + // If you're standing on a conveyor, add it's velocity to yours (for momentum) + entvars_t *pevGround = VARS(pev->groundentity); + if ( pevGround && (pevGround->flags & FL_CONVEYOR) ) + { + pev->velocity = pev->velocity + pev->basevelocity; + } +} + + + +// This is a glorious hack to find free space when you've crouched into some solid space +// Our crouching collisions do not work correctly for some reason and this is easier +// than fixing the problem :( +void FixPlayerCrouchStuck( edict_t *pPlayer ) +{ + TraceResult trace; + + // Move up as many as 18 pixels if the player is stuck. + for ( int i = 0; i < 18; i++ ) + { + UTIL_TraceHull( pPlayer->v.origin, pPlayer->v.origin, dont_ignore_monsters, head_hull, pPlayer, &trace ); + if ( trace.fStartSolid ) + pPlayer->v.origin.z ++; + else + break; + } +} + +void CBasePlayer::Duck( ) +{ + if (pev->button & IN_DUCK) + { + if ( m_IdealActivity != ACT_LEAP ) + { + SetAnimation( PLAYER_WALK ); + } + } +} + +// +// ID's player as such. +// +int CBasePlayer::Classify ( void ) +{ + return CLASS_PLAYER; +} + + +void CBasePlayer::AddPoints( int score, BOOL bAllowNegativeScore ) +{ + // Positive score always adds + if ( score < 0 ) + { + if ( !bAllowNegativeScore ) + { + if ( pev->frags < 0 ) // Can't go more negative + return; + + if ( -score > pev->frags ) // Will this go negative? + { + score = -pev->frags; // Sum will be 0 + } + } + } + + pev->frags += score; +} + +void CBasePlayer::EffectivePlayerClassChanged() +{ +} + +void CBasePlayer::NeedsTeamUpdate() +{ +} + +void CBasePlayer::SendTeamUpdate() +{ +} + +void CBasePlayer::AddPointsToTeam( int score, BOOL bAllowNegativeScore ) +{ + int index = entindex(); + + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + + if ( pPlayer && i != index ) + { + if ( g_pGameRules->PlayerRelationship( this, pPlayer ) == GR_TEAMMATE ) + { + pPlayer->AddPoints( score, bAllowNegativeScore ); + } + } + } +} + +//Player ID +void CBasePlayer::InitStatusBar() +{ + m_flStatusBarDisappearDelay = 0; + m_SbarString1[0] = m_SbarString0[0] = 0; +} + +void CBasePlayer::UpdateStatusBar() +{ + int newSBarState[ SBAR_END ]; + char sbuf0[ SBAR_STRING_SIZE ]; + char sbuf1[ SBAR_STRING_SIZE ]; + + int i; + + for (i = 0; i < SBAR_END; ++i) + { + newSBarState[i] = -1; + } + + //memset( newSBarState, 0, sizeof(newSBarState) ); + + strcpy( sbuf0, m_SbarString0 ); + strcpy( sbuf1, m_SbarString1 ); + + // Find an ID Target + TraceResult tr; + UTIL_MakeVectors( pev->v_angle + pev->punchangle ); + Vector vecSrc = EyePosition(); + Vector vecEnd = vecSrc + (gpGlobals->v_forward * MAX_ID_RANGE); + UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, edict(), &tr); + + bool theIsCombatMode = GetGameRules()->GetIsCombatMode(); + int theMaxEnumToSend = SBAR_ID_TARGETARMOR; + if(theIsCombatMode) + { + theMaxEnumToSend = SBAR_ID_TARGETLEVEL; + } + + if (tr.flFraction != 1.0) + { + if ( !FNullEnt( tr.pHit ) ) + { + CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); + + if(!pEntity) + return; + + if (pEntity->Classify() == CLASS_PLAYER ) + { + newSBarState[ SBAR_ID_TARGETNAME ] = ENTINDEX( pEntity->edict() ); + + // Name Health: X% Armor: Y% + //strcpy( sbuf1, "1 %p1\n2 Health: %i2%%\n3 Armor: %i3%%" ); + + // (health: X armor: Y) + if(!theIsCombatMode) + { + strcpy( sbuf1, "2 (health: %i2%%\n3 armor: %i3%%)" ); + } + else + { + strcpy( sbuf1, "2 (health: %i2%%\n3 armor: %i3%%\n4 level: %i4)" ); + } + + // allies and medics get to see the targets health + if ( g_pGameRules->PlayerRelationship( this, pEntity ) == GR_TEAMMATE ) + { + int theLevel = 1; + AvHPlayer* thePlayer = dynamic_cast(pEntity); + if(thePlayer) + { + theLevel = thePlayer->GetExperienceLevel(); + } + + //newSBarState[ SBAR_ID_TARGETHEALTH ] = 100 * (pEntity->pev->health / pEntity->pev->max_health); + //newSBarState[ SBAR_ID_TARGETARMOR ] = pEntity->pev->armorvalue; //No need to get it % based since 100 it's the max. + float theHealthPercent = pEntity->pev->health/AvHPlayerUpgrade::GetMaxHealth(pEntity->pev->iuser4, (AvHUser3)pEntity->pev->iuser3, theLevel); + float theArmorPercent = pEntity->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(pEntity->pev->iuser4, (AvHUser3)pEntity->pev->iuser3); + newSBarState[ SBAR_ID_TARGETHEALTH ] = max(theHealthPercent*100+0.5f, 0.0f); + newSBarState[ SBAR_ID_TARGETARMOR ] = max(theArmorPercent*100+0.5f, 0.0f); + newSBarState[ SBAR_ID_TARGETLEVEL ] = theLevel; + } + } + else + { + bool theSuccess = false; + + // Show hive health for friendlies + if(this->pev->team == pEntity->pev->team) + { + AvHUser3 theUser3 = (AvHUser3)pEntity->pev->iuser3; + switch(theUser3) + { + // Place user3s to draw here + case AVH_USER3_HIVE: + theSuccess = true; + break; + } + } + + if(theSuccess) + { + newSBarState[ SBAR_ID_TARGETNAME ] = ENTINDEX( pEntity->edict() ); + + strcpy( sbuf1, "2 (health: %i2%%)\n" ); + + float theHealthPercentage = pEntity->pev->health/pEntity->pev->max_health; + newSBarState[ SBAR_ID_TARGETHEALTH ] = max(theHealthPercentage*100+0.5f, 0.0f); + } + } + + m_flStatusBarDisappearDelay = gpGlobals->time + 1.0; + } + else if ( m_flStatusBarDisappearDelay > gpGlobals->time ) + { + // hold the values for a short amount of time after viewing the object + newSBarState[ SBAR_ID_TARGETNAME ] = m_izSBarState[ SBAR_ID_TARGETNAME ]; + newSBarState[ SBAR_ID_TARGETHEALTH ] = m_izSBarState[ SBAR_ID_TARGETHEALTH ]; + newSBarState[ SBAR_ID_TARGETARMOR ] = m_izSBarState[ SBAR_ID_TARGETARMOR ]; + newSBarState[ SBAR_ID_TARGETLEVEL ] = m_izSBarState[ SBAR_ID_TARGETLEVEL ]; + } + } + + BOOL bForceResend = FALSE; + + if ( strcmp( sbuf0, m_SbarString0 ) ) + { + NetMsg_StatusText( pev, 0, string(sbuf0) ); + strcpy( m_SbarString0, sbuf0 ); + + // make sure everything's resent + bForceResend = TRUE; + } + + if ( strcmp( sbuf1, m_SbarString1 ) ) + { + NetMsg_StatusText( pev, 1, string(sbuf1) ); + strcpy( m_SbarString1, sbuf1 ); + + // make sure everything's resent + bForceResend = TRUE; + } + + // Check values and send if they don't match + for (i = 1; i <= theMaxEnumToSend; i++) + { + if ( newSBarState[i] != m_izSBarState[i] || bForceResend ) + { + NetMsg_StatusValue( pev, i, newSBarState[i] ); + m_izSBarState[i] = newSBarState[i]; + } + } +} + + + + + + + + + +#define CLIMB_SHAKE_FREQUENCY 22 // how many frames in between screen shakes when climbing +#define MAX_CLIMB_SPEED 200 // fastest vertical climbing speed possible +#define CLIMB_SPEED_DEC 15 // climbing deceleration rate +#define CLIMB_PUNCH_X -7 // how far to 'punch' client X axis when climbing +#define CLIMB_PUNCH_Z 7 // how far to 'punch' client Z axis when climbing + +void CBasePlayer::PreThink(void) +{ + int buttonsChanged = (m_afButtonLast ^ pev->button); // These buttons have changed this frame + + // Debounced button codes for pressed/released + // UNDONE: Do we need auto-repeat? + m_afButtonPressed = buttonsChanged & pev->button; // The changed ones still down are "pressed" + m_afButtonReleased = buttonsChanged & (~pev->button); // The ones not down are "released" + + g_pGameRules->PlayerThink( this ); + + if ( g_fGameOver ) + return; // intermission or finale + + UTIL_MakeVectors(pev->v_angle); // is this still used? + + ItemPreFrame( ); + WaterMove(); + + if ( g_pGameRules && g_pGameRules->FAllowFlashlight() ) + m_iHideHUD &= ~HIDEHUD_FLASHLIGHT; + else + m_iHideHUD |= HIDEHUD_FLASHLIGHT; + + + // JOHN: checks if new client data (for HUD and view control) needs to be sent to the client + UpdateClientData(); + + CheckTimeBasedDamage(); + + // Observer Button Handling + if (this->IsObserver() ) + { + Observer_HandleButtons(); + pev->impulse = 0; + return; + } + + CheckSuitUpdate(); + + if (pev->deadflag >= DEAD_DYING) + { + PlayerDeathThink(); + return; + } + + // So the correct flags get sent to client asap. + // + if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) + pev->flags |= FL_ONTRAIN; + else + pev->flags &= ~FL_ONTRAIN; + + // Train speed control + if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) + { + CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); + float vel; + + if ( !pTrain ) + { + TraceResult trainTrace; + // Maybe this is on the other side of a level transition + UTIL_TraceLine( pev->origin, pev->origin + Vector(0,0,-38), ignore_monsters, ENT(pev), &trainTrace ); + + // HACKHACK - Just look for the func_tracktrain classname + if ( trainTrace.flFraction != 1.0 && trainTrace.pHit ) + pTrain = CBaseEntity::Instance( trainTrace.pHit ); + + + if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(pev) ) + { + //ALERT( at_error, "In train mode with no train!\n" ); + m_afPhysicsFlags &= ~PFLAG_ONTRAIN; + m_iTrain = TRAIN_NEW|TRAIN_OFF; + return; + } + } + else if ( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) || (pev->button & (IN_MOVELEFT|IN_MOVERIGHT) ) ) + { + // Turn off the train if you jump, strafe, or the train controls go dead + m_afPhysicsFlags &= ~PFLAG_ONTRAIN; + m_iTrain = TRAIN_NEW|TRAIN_OFF; + return; + } + + pev->velocity = g_vecZero; + vel = 0; + if ( m_afButtonPressed & IN_FORWARD ) + { + vel = 1; + pTrain->Use( this, this, USE_SET, (float)vel ); + } + else if ( m_afButtonPressed & IN_BACK ) + { + vel = -1; + pTrain->Use( this, this, USE_SET, (float)vel ); + } + + if (vel) + { + m_iTrain = TrainSpeed(pTrain->pev->speed, pTrain->pev->impulse); + m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW; + } + + } else if (m_iTrain & TRAIN_ACTIVE) + m_iTrain = TRAIN_NEW; // turn off train + + if (pev->button & IN_JUMP) + { + // If on a ladder, jump off the ladder + // else Jump + Jump(); + } + + + // If trying to duck, already ducked, or in the process of ducking + if ((pev->button & IN_DUCK) || FBitSet(pev->flags,FL_DUCKING) || (m_afPhysicsFlags & PFLAG_DUCKING) ) { + if ( AvHMUGetCanDuck(this->pev->iuser3) ) + Duck(); + } + + if ( !FBitSet ( pev->flags, FL_ONGROUND ) ) + { + m_flFallVelocity = -pev->velocity.z; + } + + // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating? + + // Clear out ladder pointer + m_hEnemy = NULL; + + if ( m_afPhysicsFlags & PFLAG_ONBARNACLE ) + { + pev->velocity = g_vecZero; + } +} +/* Time based Damage works as follows: + 1) There are several types of timebased damage: + + #define DMG_PARALYZE (1 << 14) // slows affected creature down + #define DMG_NERVEGAS (1 << 15) // nerve toxins, very bad + #define DMG_POISON (1 << 16) // blood poisioning + #define DMG_RADIATION (1 << 17) // radiation exposure + #define DMG_DROWNRECOVER (1 << 18) // drown recovery + #define DMG_ACID (1 << 19) // toxic chemicals or acid burns + #define DMG_SLOWBURN (1 << 20) // in an oven + #define DMG_SLOWFREEZE (1 << 21) // in a subzero freezer + + 2) A new hit inflicting tbd restarts the tbd counter - each monster has an 8bit counter, + per damage type. The counter is decremented every second, so the maximum time + an effect will last is 255/60 = 4.25 minutes. Of course, staying within the radius + of a damaging effect like fire, nervegas, radiation will continually reset the counter to max. + + 3) Every second that a tbd counter is running, the player takes damage. The damage + is determined by the type of tdb. + Paralyze - 1/2 movement rate, 30 second duration. + Nervegas - 5 points per second, 16 second duration = 80 points max dose. + Poison - 2 points per second, 25 second duration = 50 points max dose. + Radiation - 1 point per second, 50 second duration = 50 points max dose. + Drown - 5 points per second, 2 second duration. + Acid/Chemical - 5 points per second, 10 second duration = 50 points max. + Burn - 10 points per second, 2 second duration. + Freeze - 3 points per second, 10 second duration = 30 points max. + + 4) Certain actions or countermeasures counteract the damaging effects of tbds: + + Armor/Heater/Cooler - Chemical(acid),burn, freeze all do damage to armor power, then to body + - recharged by suit recharger + Air In Lungs - drowning damage is done to air in lungs first, then to body + - recharged by poking head out of water + - 10 seconds if swiming fast + Air In SCUBA - drowning damage is done to air in tanks first, then to body + - 2 minutes in tanks. Need new tank once empty. + Radiation Syringe - Each syringe full provides protection vs one radiation dosage + Antitoxin Syringe - Each syringe full provides protection vs one poisoning (nervegas or poison). + Health kit - Immediate stop to acid/chemical, fire or freeze damage. + Radiation Shower - Immediate stop to radiation damage, acid/chemical or fire damage. + + +*/ + +// If player is taking time based damage, continue doing damage to player - +// this simulates the effect of being poisoned, gassed, dosed with radiation etc - +// anything that continues to do damage even after the initial contact stops. +// Update all time based damage counters, and shut off any that are done. + +// The m_bitsDamageType bit MUST be set if any damage is to be taken. +// This routine will detect the initial on value of the m_bitsDamageType +// and init the appropriate counter. Only processes damage every second. + +//#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage +//#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval + +//#define NERVEGAS_DURATION 16 +//#define NERVEGAS_DAMAGE 5.0 + +//#define POISON_DURATION 25 +//#define POISON_DAMAGE 2.0 + +//#define RADIATION_DURATION 50 +//#define RADIATION_DAMAGE 1.0 + +//#define ACID_DURATION 10 +//#define ACID_DAMAGE 5.0 + +//#define SLOWBURN_DURATION 2 +//#define SLOWBURN_DAMAGE 1.0 + +//#define SLOWFREEZE_DURATION 1.0 +//#define SLOWFREEZE_DAMAGE 3.0 + +/* */ + + +void CBasePlayer::CheckTimeBasedDamage() +{ + int i; + BYTE bDuration = 0; + + static float gtbdPrev = 0.0; + + if (!(m_bitsDamageType & DMG_TIMEBASED)) + return; + + // only check for time based damage approx. every 2 seconds + if (abs(gpGlobals->time - m_tbdPrev) < 2.0) + return; + + m_tbdPrev = gpGlobals->time; + + for (i = 0; i < CDMG_TIMEBASED; i++) + { + // make sure bit is set for damage type + if (m_bitsDamageType & (DMG_PARALYZE << i)) + { + switch (i) + { + case itbd_Paralyze: + // UNDONE - flag movement as half-speed + bDuration = PARALYZE_DURATION; + break; + case itbd_NerveGas: +// TakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC); + bDuration = NERVEGAS_DURATION; + break; + case itbd_Poison: + TakeDamage(pev, pev, POISON_DAMAGE, DMG_GENERIC); + bDuration = POISON_DURATION; + break; + case itbd_Radiation: +// TakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC); + bDuration = RADIATION_DURATION; + break; + case itbd_DrownRecover: + // NOTE: this hack is actually used to RESTORE health + // after the player has been drowning and finally takes a breath + if (m_idrowndmg > m_idrownrestored) + { + int idif = min(m_idrowndmg - m_idrownrestored, 10); + + TakeHealth(idif, DMG_GENERIC); + m_idrownrestored += idif; + } + bDuration = 4; // get up to 5*10 = 50 points back + break; + case itbd_Acid: +// TakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC); + bDuration = ACID_DURATION; + break; + case itbd_SlowBurn: +// TakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC); + bDuration = SLOWBURN_DURATION; + break; + case itbd_SlowFreeze: +// TakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC); + bDuration = SLOWFREEZE_DURATION; + break; + default: + bDuration = 0; + } + + if (m_rgbTimeBasedDamage[i]) + { + // use up an antitoxin on poison or nervegas after a few seconds of damage + if (((i == itbd_NerveGas) && (m_rgbTimeBasedDamage[i] < NERVEGAS_DURATION)) || + ((i == itbd_Poison) && (m_rgbTimeBasedDamage[i] < POISON_DURATION))) + { + if (m_rgItems[ITEM_ANTIDOTE]) + { + m_rgbTimeBasedDamage[i] = 0; + m_rgItems[ITEM_ANTIDOTE]--; + SetSuitUpdate("!HEV_HEAL4", FALSE, SUIT_REPEAT_OK); + } + } + + + // decrement damage duration, detect when done. + if (!m_rgbTimeBasedDamage[i] || --m_rgbTimeBasedDamage[i] == 0) + { + m_rgbTimeBasedDamage[i] = 0; + // if we're done, clear damage bits + m_bitsDamageType &= ~(DMG_PARALYZE << i); + } + } + else + // first time taking this damage type - init damage duration + m_rgbTimeBasedDamage[i] = bDuration; + } + } +} + +/* +THE POWER SUIT + +The Suit provides 3 main functions: Protection, Notification and Augmentation. +Some functions are automatic, some require power. +The player gets the suit shortly after getting off the train in C1A0 and it stays +with him for the entire game. + +Protection + + Heat/Cold + When the player enters a hot/cold area, the heating/cooling indicator on the suit + will come on and the battery will drain while the player stays in the area. + After the battery is dead, the player starts to take damage. + This feature is built into the suit and is automatically engaged. + Radiation Syringe + This will cause the player to be immune from the effects of radiation for N seconds. Single use item. + Anti-Toxin Syringe + This will cure the player from being poisoned. Single use item. + Health + Small (1st aid kits, food, etc.) + Large (boxes on walls) + Armor + The armor works using energy to create a protective field that deflects a + percentage of damage projectile and explosive attacks. After the armor has been deployed, + it will attempt to recharge itself to full capacity with the energy reserves from the battery. + It takes the armor N seconds to fully charge. + +Notification (via the HUD) + +x Health +x Ammo +x Automatic Health Care + Notifies the player when automatic healing has been engaged. +x Geiger counter + Classic Geiger counter sound and status bar at top of HUD + alerts player to dangerous levels of radiation. This is not visible when radiation levels are normal. +x Poison + Armor + Displays the current level of armor. + +Augmentation + + Reanimation (w/adrenaline) + Causes the player to come back to life after he has been dead for 3 seconds. + Will not work if player was gibbed. Single use. + Long Jump + Used by hitting the ??? key(s). Caused the player to further than normal. + SCUBA + Used automatically after picked up and after player enters the water. + Works for N seconds. Single use. + +Things powered by the battery + + Armor + Uses N watts for every M units of damage. + Heat/Cool + Uses N watts for every second in hot/cold area. + Long Jump + Uses N watts for every jump. + Alien Cloak + Uses N watts for each use. Each use lasts M seconds. + Alien Shield + Augments armor. Reduces Armor drain by one half + +*/ + +// if in range of radiation source, ping geiger counter + +#define GEIGERDELAY 0.25 + +void CBasePlayer :: UpdateGeigerCounter( void ) +{ + BYTE range; + + // delay per update ie: don't flood net with these msgs + if (gpGlobals->time < m_flgeigerDelay) + return; + + m_flgeigerDelay = gpGlobals->time + GEIGERDELAY; + + // send range to radition source to client + + range = (BYTE) (m_flgeigerRange / 4); + + if (range != m_igeigerRangePrev) + { + m_igeigerRangePrev = range; + NetMsg_GeigerRange( pev, range ); + } + + // reset counter and semaphore + if (!RANDOM_LONG(0,3)) + m_flgeigerRange = 1000; + +} + +/* +================ +CheckSuitUpdate + +Play suit update if it's time +================ +*/ + +#define SUITUPDATETIME 3.5 +#define SUITFIRSTUPDATETIME 0.1 + +void CBasePlayer::CheckSuitUpdate() +{ + int i; + int isentence = 0; + int isearch = m_iSuitPlayNext; + + // Ignore suit updates if no suit + if ( !(pev->weapons & (1<IsMultiplayer() ) + { + // don't bother updating HEV voice in multiplayer. + return; + } + + if ( gpGlobals->time >= m_flSuitUpdate && m_flSuitUpdate > 0) + { + // play a sentence off of the end of the queue + for (i = 0; i < CSUITPLAYLIST; i++) + { + if (isentence = m_rgSuitPlayList[isearch]) + break; + + if (++isearch == CSUITPLAYLIST) + isearch = 0; + } + + if (isentence) + { + m_rgSuitPlayList[isearch] = 0; + if (isentence > 0) + { + // play sentence number + + char sentence[CBSENTENCENAME_MAX+1]; + strcpy(sentence, "!"); + strcat(sentence, gszallsentencenames[isentence]); + EMIT_SOUND_SUIT(ENT(pev), sentence); + } + else + { + // play sentence group + EMIT_GROUPID_SUIT(ENT(pev), -isentence); + } + m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME; + } + else + // queue is empty, don't check + m_flSuitUpdate = 0; + } +} + +// add sentence to suit playlist queue. if fgroup is true, then +// name is a sentence group (HEV_AA), otherwise name is a specific +// sentence name ie: !HEV_AA0. If iNoRepeat is specified in +// seconds, then we won't repeat playback of this word or sentence +// for at least that number of seconds. + +void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) +{ + int i; + int isentence; + int iempty = -1; + + + // Ignore suit updates if no suit + if ( !(pev->weapons & (1<IsMultiplayer() ) + { + // due to static channel design, etc. We don't play HEV sounds in multiplayer right now. + return; + } + + // if name == NULL, then clear out the queue + + if (!name) + { + for (i = 0; i < CSUITPLAYLIST; i++) + m_rgSuitPlayList[i] = 0; + return; + } + // get sentence or group number + if (!fgroup) + { + isentence = SENTENCEG_Lookup(name, NULL); + if (isentence < 0) + return; + } + else + // mark group number as negative + isentence = -SENTENCEG_GetIndex(name); + + // check norepeat list - this list lets us cancel + // the playback of words or sentences that have already + // been played within a certain time. + + for (i = 0; i < CSUITNOREPEAT; i++) + { + if (isentence == m_rgiSuitNoRepeat[i]) + { + // this sentence or group is already in + // the norepeat list + + if (m_rgflSuitNoRepeatTime[i] < gpGlobals->time) + { + // norepeat time has expired, clear it out + m_rgiSuitNoRepeat[i] = 0; + m_rgflSuitNoRepeatTime[i] = 0.0; + iempty = i; + break; + } + else + { + // don't play, still marked as norepeat + return; + } + } + // keep track of empty slot + if (!m_rgiSuitNoRepeat[i]) + iempty = i; + } + + // sentence is not in norepeat list, save if norepeat time was given + + if (iNoRepeatTime) + { + if (iempty < 0) + iempty = RANDOM_LONG(0, CSUITNOREPEAT-1); // pick random slot to take over + m_rgiSuitNoRepeat[iempty] = isentence; + m_rgflSuitNoRepeatTime[iempty] = iNoRepeatTime + gpGlobals->time; + } + + // find empty spot in queue, or overwrite last spot + + m_rgSuitPlayList[m_iSuitPlayNext++] = isentence; + if (m_iSuitPlayNext == CSUITPLAYLIST) + m_iSuitPlayNext = 0; + + if (m_flSuitUpdate <= gpGlobals->time) + { + if (m_flSuitUpdate == 0) + // play queue is empty, don't delay too long before playback + m_flSuitUpdate = gpGlobals->time + SUITFIRSTUPDATETIME; + else + m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME; + } + +} + +/* +================ +CheckPowerups + +Check for turning off powerups + +GLOBALS ASSUMED SET: g_ulModelIndexPlayer +================ +*/ + static void +CheckPowerups(entvars_t *pev) +{ + if (pev->health <= 0) + return; + + //pev->modelindex = g_ulModelIndexPlayer; // don't use eyes +} + + +//========================================================= +// UpdatePlayerSound - updates the position of the player's +// reserved sound slot in the sound list. +//========================================================= +void CBasePlayer :: UpdatePlayerSound ( void ) +{ + int iBodyVolume; + int iVolume; + CSound *pSound; + + pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt :: ClientSoundIndex( edict() ) ); + + if ( !pSound ) + { + ALERT ( at_console, "Client lost reserved sound!\n" ); + return; + } + + pSound->m_iType = bits_SOUND_NONE; + + // now calculate the best target volume for the sound. If the player's weapon + // is louder than his body/movement, use the weapon volume, else, use the body volume. + + if ( FBitSet ( pev->flags, FL_ONGROUND ) ) + { + iBodyVolume = pev->velocity.Length(); + + // clamp the noise that can be made by the body, in case a push trigger, + // weapon recoil, or anything shoves the player abnormally fast. + if ( iBodyVolume > 512 ) + { + iBodyVolume = 512; + } + } + else + { + iBodyVolume = 0; + } + + if ( pev->button & IN_JUMP ) + { + iBodyVolume += 100; + } + +// convert player move speed and actions into sound audible by monsters. + if ( m_iWeaponVolume > iBodyVolume ) + { + m_iTargetVolume = m_iWeaponVolume; + + // OR in the bits for COMBAT sound if the weapon is being louder than the player. + pSound->m_iType |= bits_SOUND_COMBAT; + } + else + { + m_iTargetVolume = iBodyVolume; + } + + // decay weapon volume over time so bits_SOUND_COMBAT stays set for a while + m_iWeaponVolume -= 250 * gpGlobals->frametime; + if ( m_iWeaponVolume < 0 ) + { + iVolume = 0; + } + + + // if target volume is greater than the player sound's current volume, we paste the new volume in + // immediately. If target is less than the current volume, current volume is not set immediately to the + // lower volume, rather works itself towards target volume over time. This gives monsters a much better chance + // to hear a sound, especially if they don't listen every frame. + iVolume = pSound->m_iVolume; + + if ( m_iTargetVolume > iVolume ) + { + iVolume = m_iTargetVolume; + } + else if ( iVolume > m_iTargetVolume ) + { + iVolume -= 250 * gpGlobals->frametime; + + if ( iVolume < m_iTargetVolume ) + { + iVolume = 0; + } + } + + if ( m_fNoPlayerSound ) + { + // debugging flag, lets players move around and shoot without monsters hearing. + iVolume = 0; + } + + if ( gpGlobals->time > m_flStopExtraSoundTime ) + { + // since the extra sound that a weapon emits only lasts for one client frame, we keep that sound around for a server frame or two + // after actual emission to make sure it gets heard. + m_iExtraSoundTypes = 0; + } + + if ( pSound ) + { + pSound->m_vecOrigin = pev->origin; + pSound->m_iType |= ( bits_SOUND_PLAYER | m_iExtraSoundTypes ); + pSound->m_iVolume = iVolume; + } + + // keep track of virtual muzzle flash + m_iWeaponFlash -= 256 * gpGlobals->frametime; + if (m_iWeaponFlash < 0) + m_iWeaponFlash = 0; + + //UTIL_MakeVectors ( pev->angles ); + //gpGlobals->v_forward.z = 0; + + // Below are a couple of useful little bits that make it easier to determine just how much noise the + // player is making. + // UTIL_ParticleEffect ( pev->origin + gpGlobals->v_forward * iVolume, g_vecZero, 255, 25 ); + //ALERT ( at_console, "%d/%d\n", iVolume, m_iTargetVolume ); +} + + +void CBasePlayer::PostThink() +{ + if ( g_fGameOver ) + goto pt_end; // intermission or finale + + if (!IsAlive()) + goto pt_end; + + // Handle Tank controlling + if ( m_pTank != NULL ) + { // if they've moved too far from the gun, or selected a weapon, unuse the gun + if ( m_pTank->OnControls( pev ) && !pev->weaponmodel ) + { + m_pTank->Use( this, this, USE_SET, 2 ); // try fire the gun + } + else + { // they've moved off the platform + m_pTank->Use( this, this, USE_OFF, 0 ); + m_pTank = NULL; + } + } + +// do weapon stuff + ItemPostFrame( ); + +// check to see if player landed hard enough to make a sound +// falling farther than half of the maximum safe distance, but not as far a max safe distance will +// play a bootscrape sound, and no damage will be inflicted. Fallling a distance shorter than half +// of maximum safe distance will make no sound. Falling farther than max safe distance will play a +// fallpain sound, and damage will be inflicted based on how far the player fell + + if ( (FBitSet(pev->flags, FL_ONGROUND)) && (pev->health > 0) && m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD ) + { + // ALERT ( at_console, "%f\n", m_flFallVelocity ); + + if (pev->watertype == CONTENT_WATER) + { + // Did he hit the world or a non-moving entity? + // BUG - this happens all the time in water, especially when + // BUG - water has current force + // if ( !pev->groundentity || VARS(pev->groundentity)->velocity.z == 0 ) + // EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM); + } + // skulks, lerks, fades and jetpackers don't take falling damage + else if ((m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED) && (this->pev->iuser3 != AVH_USER3_ALIEN_PLAYER1) && (this->pev->iuser3 != AVH_USER3_ALIEN_PLAYER3) && (this->pev->iuser3 != AVH_USER3_ALIEN_PLAYER4) && (this->pev->iuser3 != AVH_USER3_ALIEN_EMBRYO) && (!GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_7) || !(this->pev->iuser3 == AVH_USER3_MARINE_PLAYER)) ) + {// after this point, we start doing damage + + float flFallDamage = g_pGameRules->FlPlayerFallDamage( this ); + + if ( flFallDamage > pev->health ) + {//splat + // note: play on item channel because we play footstep landing on body channel + // : 243 don't play gib sound if being digested + if ( AvHGetIsAlien(this->pev->iuser3) || !GetHasUpgrade(this->pev->iuser4, MASK_DIGESTING) ) + EMIT_SOUND(ENT(pev), CHAN_ITEM, "common/bodysplat.wav", 1, ATTN_NORM); + } + + if ( flFallDamage > 0 ) + { + TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), flFallDamage, DMG_FALL ); + pev->punchangle.x = 0; + } + } + + if ( IsAlive() ) + { + SetAnimation( PLAYER_WALK ); + } + } + + if (FBitSet(pev->flags, FL_ONGROUND)) + { + if (m_flFallVelocity > 64 && !g_pGameRules->IsMultiplayer()) + { + CSoundEnt::InsertSound ( bits_SOUND_PLAYER, pev->origin, m_flFallVelocity, 0.2 ); + // ALERT( at_console, "fall %f\n", m_flFallVelocity ); + } + m_flFallVelocity = 0; + } + + // select the proper animation for the player character + if ( IsAlive() ) + { + if (!pev->velocity.x && !pev->velocity.y) + SetAnimation( PLAYER_IDLE ); + else if ((pev->velocity.x || pev->velocity.y) && (FBitSet(pev->flags, FL_ONGROUND))) + SetAnimation( PLAYER_WALK ); + else if (pev->waterlevel > 1) + SetAnimation( PLAYER_WALK ); + } + + StudioFrameAdvance( ); + CheckPowerups(pev); + + UpdatePlayerSound(); + + // Track button info so we can detect 'pressed' and 'released' buttons next frame + m_afButtonLast = pev->button; + +pt_end: + // Decay timers on weapons + // go through all of the weapons and make a list of the ones to pack + for ( int i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + if ( m_rgpPlayerItems[ i ] ) + { + CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[ i ]; + + while ( pPlayerItem ) + { + CBasePlayerWeapon *gun; + + gun = (CBasePlayerWeapon *)pPlayerItem->GetWeaponPtr(); + + if ( gun && gun->UseDecrement() ) + { + gun->m_flNextPrimaryAttack = max( gun->m_flNextPrimaryAttack - gpGlobals->frametime, -1.0f ); + gun->m_flNextSecondaryAttack = max( gun->m_flNextSecondaryAttack - gpGlobals->frametime, -0.001f ); + + if ( gun->m_flTimeWeaponIdle != 1000 ) + { + gun->m_flTimeWeaponIdle = max( gun->m_flTimeWeaponIdle - gpGlobals->frametime, -0.001f ); + } + + if ( gun->pev->fuser1 != 1000 ) + { + gun->pev->fuser1 = max( gun->pev->fuser1 - gpGlobals->frametime, -0.001f ); + } + + // Only decrement if not flagged as NO_DECREMENT +// if ( gun->m_flPumpTime != 1000 ) + // { + // gun->m_flPumpTime = max( gun->m_flPumpTime - gpGlobals->frametime, -0.001f ); + // } + + } + + pPlayerItem = pPlayerItem->m_pNext; + } + } + } + + m_flNextAttack -= gpGlobals->frametime; + if ( m_flNextAttack < -0.001 ) + m_flNextAttack = -0.001; + + if ( m_flNextAmmoBurn != 1000 ) + { + m_flNextAmmoBurn -= gpGlobals->frametime; + + if ( m_flNextAmmoBurn < -0.001 ) + m_flNextAmmoBurn = -0.001; + } + + if ( m_flAmmoStartCharge != 1000 ) + { + m_flAmmoStartCharge -= gpGlobals->frametime; + + if ( m_flAmmoStartCharge < -0.001 ) + m_flAmmoStartCharge = -0.001; + } +} + + +// checks if the spot is clear of players +BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ) +{ + CBaseEntity *ent = NULL; + + if ( !pSpot->IsTriggered( pPlayer ) ) + { + return FALSE; + } + + AvHPlayer* thePlayer = dynamic_cast(pPlayer); + ASSERT(thePlayer); + + Vector theMinSize; + Vector theMaxSize; + thePlayer->GetSize(theMinSize, theMaxSize); + + int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(thePlayer->pev->iuser3)); + Vector theOriginToSpawn = pSpot->pev->origin; + theOriginToSpawn.z += theOffset; + + if(!AvHSUGetIsEnoughRoomForHull(theOriginToSpawn, AvHMUGetHull(false, thePlayer->pev->iuser3), thePlayer->edict())) + return FALSE; + + //while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 )) != NULL ) + + // Assumes UTIL_FindEntityInSphere doesn't take size of other object into account. Players should be 2*HULL0_MAXX from each other, add another for safety + while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 3*HULL0_MAXX)) != NULL ) + { + // if ent is a client, don't spawn on 'em (but dead players don't block) + if ( ent->IsPlayer() && (ent != pPlayer) && ent->IsAlive() ) + return FALSE; + + } + + return TRUE; +} + +BOOL CBasePlayer::IsAlive() const +{ + // Take into account spectators + return (pev->deadflag == DEAD_NO) && pev->health > 0; +} + +BOOL CBasePlayer::IsAlive(bool inIncludeSpectating) const +{ + // Take into account spectators + BOOL theIsAlive = this->IsAlive(); + if(inIncludeSpectating) + { + CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + theIsAlive = theSpectatingEntity->IsAlive(); + } + } + + return theIsAlive; +} + +CBaseEntity* CBasePlayer::GetSpectatingEntity() const +{ + CBaseEntity* theSpectatingEntity = NULL; + + if(this->pev && this->pev->iuser1 && this->pev->iuser2) + { + int theEntityIndex = this->pev->iuser2; + theSpectatingEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theEntityIndex)); + } + + return theSpectatingEntity; +} + +void CBasePlayer::Spawn( void ) +{ + pev->classname = MAKE_STRING("player"); +// pev->health = 100; +// pev->armorvalue = 0; +// pev->max_health = pev->health; + pev->takedamage = DAMAGE_AIM; + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_WALK; + pev->flags &= FL_PROXY; // keep proxy flag sey by engine + pev->flags |= FL_CLIENT; + pev->air_finished = gpGlobals->time + 12; + pev->dmg = 2; // initial water damage + pev->effects = 0; + pev->deadflag = DEAD_NO; + pev->dmg_take = 0; + pev->dmg_save = 0; + pev->friction = 1.0; + pev->gravity = 1.0; + m_bitsHUDDamage = -1; + m_bitsDamageType = 0; + m_afPhysicsFlags = 0; + m_fLongJump = FALSE;// no longjump module. + + g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); + g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); + + pev->fov = m_iFOV = 0;// init field of view. + m_iClientFOV = -1; // make sure fov reset is sent + + m_flNextDecalTime = 0;// let this player decal as soon as he spawns. + + m_flgeigerDelay = gpGlobals->time + 2.0; // wait a few seconds until user-defined message registrations + // are recieved by all clients + + m_flTimeStepSound = 0; + m_iStepLeft = 0; + m_flFieldOfView = 0.5;// some monsters use this to determine whether or not the player is looking at them. + + m_bloodColor = BLOOD_COLOR_RED; + m_flNextAttack = UTIL_WeaponTimeBase(); + StartSneaking(); + +// m_iFlashBattery = 99; +// m_flFlashLightTime = 1; // force first message + +// dont let uninitialized value here hurt the player + m_flFallVelocity = 0; + + GetGameRules()->SetDefaultPlayerTeam( this ); + edict_t* theSpawnPoint = GetGameRules()->SelectSpawnPoint( this ); + ASSERT(theSpawnPoint); + this->InitPlayerFromSpawn(theSpawnPoint); + + //AvHPlayer* thePlayer = dynamic_cast(this); + //char* thePlayerModel = thePlayer->GetPlayerModel(); + //SET_MODEL(ENT(pev), thePlayerModel); + SET_MODEL(ENT(pev), "models/player.mdl"); + + g_ulModelIndexPlayer = pev->modelindex; + //pev->sequence = LookupActivity( ACT_IDLE ); + pev->sequence = LookupSequence("idle1"); + + if ( FBitSet(pev->flags, FL_DUCKING) ) + UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); + else + UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); + + pev->view_ofs = VEC_VIEW; + Precache(); + m_HackedGunPos = Vector( 0, 32, 0 ); + + if ( m_iPlayerSound == SOUNDLIST_EMPTY ) + { + ALERT ( at_console, "Couldn't alloc player sound slot!\n" ); + } + + m_fNoPlayerSound = FALSE;// normal sound behavior. + + m_pLastItem = NULL; + m_pLastItemName = 0; // Added by mmcguire. + + m_fInitHUD = TRUE; + m_iClientHideHUD = -1; // force this to be recalculated + m_fWeapon = FALSE; + m_pClientActiveItem = NULL; + m_iClientBattery = -1; + + // reset all ammo values to 0 + for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) + { + m_rgAmmo[i] = 0; + m_rgAmmoLast[i] = 0; // client ammo values also have to be reset (the death hud clear messages does on the client side) + } + + m_lastx = m_lasty = 0; + + m_flNextChatTime = gpGlobals->time; + + g_pGameRules->PlayerSpawn( this ); +} + + +void CBasePlayer :: Precache( void ) +{ + // in the event that the player JUST spawned, and the level node graph + // was loaded, fix all of the node graph pointers before the game starts. + + // !!!BUGBUG - now that we have multiplayer, this needs to be moved! + if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet ) + { + if ( !WorldGraph.FSetGraphPointers() ) + { + ALERT ( at_console, "**Graph pointers were not set!\n"); + } + else + { + ALERT ( at_console, "**Graph Pointers Set!\n" ); + } + } + + // SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific) + // because they need to precache before any clients have connected + + Net_InitializeMessages(); + + // init geiger counter vars during spawn and each time + // we cross a level transition + + m_flgeigerRange = 1000; + m_igeigerRangePrev = 1000; + + m_bitsDamageType = 0; + m_bitsHUDDamage = -1; + + m_iClientBattery = -1; + + m_iTrain = TRAIN_NEW; + + m_iUpdateTime = 5; // won't update for 1/2 a second + + if ( gInitHUD ) + m_fInitHUD = TRUE; +} + + +int CBasePlayer::Save( CSave &save ) +{ + if ( !CBaseMonster::Save(save) ) + return 0; + + return save.WriteFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) ); +} + + +// +// Marks everything as new so the player will resend this to the hud. +// +void CBasePlayer::RenewItems(void) +{ + +} + +int CBasePlayer::Restore( CRestore &restore ) +{ + if ( !CBaseMonster::Restore(restore) ) + return 0; + + int status = restore.ReadFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) ); + + SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData; + // landmark isn't present. + if ( !pSaveData->fUseLandmark ) + { + ALERT( at_console, "No Landmark:%s\n", pSaveData->szLandmarkName ); + + // default to normal spawn + //edict_t* pentSpawnSpot = EntSelectSpawnPoint( this ); + ASSERT(FALSE); + //pev->origin = VARS(pentSpawnSpot)->origin + Vector(0,0,1); + //pev->angles = VARS(pentSpawnSpot)->angles; + } + pev->v_angle.z = 0; // Clear out roll + pev->angles = pev->v_angle; + + pev->fixangle = TRUE; // turn this way immediately + +// Copied from spawn() for now + m_bloodColor = BLOOD_COLOR_RED; + + g_ulModelIndexPlayer = pev->modelindex; + + if ( FBitSet(pev->flags, FL_DUCKING) ) + { + // Use the crouch HACK + //FixPlayerCrouchStuck( edict() ); + // Don't need to do this with new player prediction code. + UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); + } + else + { + UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); + } + + g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); + + if ( m_fLongJump ) + { + g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "1" ); + } + else + { + g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); + } + + RenewItems(); + + // HACK: This variable is saved/restored in CBaseMonster as a time variable, but we're using it + // as just a counter. Ideally, this needs its own variable that's saved as a plain float. + // Barring that, we clear it out here instead of using the incorrect restored time value. + m_flNextAttack = UTIL_WeaponTimeBase(); + + return status; +} + + + +void CBasePlayer::SelectNextItem( int iItem ) +{ + CBasePlayerItem *pItem; + + pItem = m_rgpPlayerItems[ iItem ]; + + if (!pItem) + return; + + if (pItem == m_pActiveItem) + { + // select the next one in the chain + pItem = m_pActiveItem->m_pNext; + if (! pItem) + { + return; + } + + CBasePlayerItem *pLast; + pLast = pItem; + while (pLast->m_pNext) + pLast = pLast->m_pNext; + + // relink chain + pLast->m_pNext = m_pActiveItem; + m_pActiveItem->m_pNext = NULL; + m_rgpPlayerItems[ iItem ] = pItem; + } + + ResetAutoaim( ); + + // FIX, this needs to queue them up and delay + if (m_pActiveItem) + { + m_pActiveItem->Holster( ); + } + + m_pActiveItem = pItem; + + if (m_pActiveItem) + { + m_pActiveItem->Deploy( ); + m_pActiveItem->UpdateItemInfo( ); + } +} + +void CBasePlayer::SelectItem(const char *pstr) +{ + if (!pstr) + return; + + CBasePlayerItem *pItem = NULL; + + for (int i = 0; i < MAX_ITEM_TYPES; i++) + { + if (m_rgpPlayerItems[i]) + { + pItem = m_rgpPlayerItems[i]; + + while (pItem) + { + if (FClassnameIs(pItem->pev, pstr)) + break; + pItem = pItem->m_pNext; + } + } + + if (pItem) + break; + } + + if (!pItem) + return; + + + if ((pItem == m_pActiveItem) || (m_pActiveItem != NULL && !m_pActiveItem->CanHolster())) + return; + + ResetAutoaim( ); + + // FIX, this needs to queue them up and delay + if (m_pActiveItem) + m_pActiveItem->Holster( ); + + m_pLastItem = m_pActiveItem; + + if (m_pLastItem) + { + m_pLastItemName = m_pLastItem->pev->classname; + } + else + { + m_pLastItemName = 0; + } + + m_pActiveItem = pItem; + + if (m_pActiveItem) + { + m_pActiveItem->Deploy( ); + m_pActiveItem->UpdateItemInfo( ); + } +} + +//note this should no longer be called, and asserts if used +void CBasePlayer::SelectLastItem(void) +{ + if (!m_pLastItem) + { + + // Try the last item name. + + if (m_pLastItemName != 0) + { + for (int i = 0; i < MAX_ITEM_TYPES; i++) + { + CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; + while (theCurrentItem) + { + if(FClassnameIs(theCurrentItem->pev, STRING(m_pLastItemName))) + { + m_pLastItem = theCurrentItem; + break; + } + theCurrentItem = theCurrentItem->m_pNext; + } + } + } + + } + + if (!m_pLastItem) + { + return; + } + + this->SelectItem(STRING(m_pLastItem->pev->classname)); //replaced copy-and-paste from SelectItem with actual SelectItem call +} + +BOOL CBasePlayer::HasItem(CBasePlayerItem* inWeapon) +{ + // Return true if we have a weapon of this type + bool theHasWeapon = false; + + for(int i = 0; (i < MAX_ITEM_TYPES) && !theHasWeapon; i++) + { + CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; + while(theCurrentItem && !theHasWeapon) + { + if(FClassnameIs(theCurrentItem->pev, STRING(inWeapon->pev->classname))) + { + theHasWeapon = true; + } + theCurrentItem = theCurrentItem->m_pNext; + } + } + + return theHasWeapon; +} + +BOOL CBasePlayer::HasItemWithFlag(int inFlag, CBasePlayerItem*& outItem) +{ + // Return true if we have a weapon of this type + bool theHasWeaponWithFlag = false; + + for(int i = 0; (i < MAX_ITEM_TYPES) && !theHasWeaponWithFlag; i++) + { + CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; + while(theCurrentItem && !theHasWeaponWithFlag) + { + ItemInfo theItemInfo; + theCurrentItem->GetItemInfo(&theItemInfo); + + int theFlags = theItemInfo.iFlags; + if(theFlags & inFlag) + { + theHasWeaponWithFlag = true; + outItem = theCurrentItem; + } + theCurrentItem = theCurrentItem->m_pNext; + } + } + + return theHasWeaponWithFlag; +} + +//============================================== +// HasWeapons - do I have any weapons at all? +//============================================== +BOOL CBasePlayer::HasWeapons( void ) +{ + int i; + + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + if ( m_rgpPlayerItems[ i ] ) + { + return TRUE; + } + } + + return FALSE; +} + +void CBasePlayer::SelectPrevItem( int iItem ) +{ +} + + +char *CBasePlayer::TeamID( void ) +{ + if ( pev == NULL ) // Not fully connected yet + return ""; + + // return their team name + return m_szTeamName; +} + +void CBasePlayer::SetTeamID(const char* inTeamID) +{ + strncpy(this->m_szTeamName, inTeamID, TEAM_NAME_LENGTH); +} + +//============================================== +// !!!UNDONE:ultra temporary SprayCan entity to apply +// decal frame at a time. For PreAlpha CD +//============================================== +class CSprayCan : public CBaseEntity +{ +public: + void Spawn ( entvars_t *pevOwner ); + void Think( void ); + + virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; } +}; + +void CSprayCan::Spawn ( entvars_t *pevOwner ) +{ + pev->origin = pevOwner->origin + Vector ( 0 , 0 , 32 ); + pev->angles = pevOwner->v_angle; + pev->owner = ENT(pevOwner); + pev->frame = 0; + + pev->nextthink = gpGlobals->time + 0.1; + EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/sprayer.wav", 1, ATTN_NORM); +} + +void CSprayCan::Think( void ) +{ + TraceResult tr; + int playernum; + int nFrames; + CBasePlayer *pPlayer; + + pPlayer = (CBasePlayer *)GET_PRIVATE(pev->owner); + + if (pPlayer) + nFrames = pPlayer->GetCustomDecalFrames(); + else + nFrames = -1; + + playernum = ENTINDEX(pev->owner); + + // ALERT(at_console, "Spray by player %i, %i of %i\n", playernum, (int)(pev->frame + 1), nFrames); + + UTIL_MakeVectors(pev->angles); + UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr); + + // No customization present. + if (nFrames == -1) + { + UTIL_DecalTrace( &tr, DECAL_LAMBDA6 ); + UTIL_Remove( this ); + } + else + { + UTIL_PlayerDecalTrace( &tr, playernum, pev->frame, TRUE ); + // Just painted last custom frame. + if ( pev->frame++ >= (nFrames - 1)) + UTIL_Remove( this ); + } + + pev->nextthink = gpGlobals->time + 0.1; +} + +class CBloodSplat : public CBaseEntity +{ +public: + void Spawn ( entvars_t *pevOwner ); + void Spray ( void ); +}; + +void CBloodSplat::Spawn ( entvars_t *pevOwner ) +{ + pev->origin = pevOwner->origin + Vector ( 0 , 0 , 32 ); + pev->angles = pevOwner->v_angle; + pev->owner = ENT(pevOwner); + + SetThink ( &CBloodSplat::Spray ); + pev->nextthink = gpGlobals->time + 0.1; +} + +void CBloodSplat::Spray ( void ) +{ + TraceResult tr; + + if ( g_Language != LANGUAGE_GERMAN ) + { + UTIL_MakeVectors(pev->angles); + UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr); + + UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); + } + SetThink ( &CBloodSplat::SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; +} + +//============================================== + + + +void CBasePlayer::GiveNamedItem( const char *pszName, bool inSendMessage ) +{ + edict_t *pent; + + int istr = MAKE_STRING(pszName); + + pent = CREATE_NAMED_ENTITY(istr); + if ( FNullEnt( pent ) ) + { + ALERT ( at_console, "NULL Ent in GiveNamedItem!\n" ); + return; + } + VARS( pent )->origin = pev->origin; + pent->v.spawnflags |= SF_NORESPAWN; + + DispatchSpawn( pent ); + DispatchTouch( pent, ENT( pev ) ); + + if(inSendMessage) + { + CBaseEntity* theEntity = CBaseEntity::Instance(ENT(pent)); + ASSERT(theEntity); + CBasePlayerWeapon* theWeapon = dynamic_cast(theEntity); + //ASSERT(theWeapon); + if(theWeapon) + { + int theWeaponID = theWeapon->m_iId; + NetMsg_WeapPickup( pev, theWeaponID ); + } + } +} + + + +CBaseEntity *FindEntityForward( CBaseEntity *pMe ) +{ + TraceResult tr; + + UTIL_MakeVectors(pMe->pev->v_angle); + UTIL_TraceLine(pMe->pev->origin + pMe->pev->view_ofs,pMe->pev->origin + pMe->pev->view_ofs + gpGlobals->v_forward * 8192,dont_ignore_monsters, pMe->edict(), &tr ); + if ( tr.flFraction != 1.0 && !FNullEnt( tr.pHit) ) + { + CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); + return pHit; + } + return NULL; +} + + +BOOL CBasePlayer :: FlashlightIsOn( void ) +{ + return FBitSet(pev->effects, EF_DIMLIGHT); +} + +const float kFlashlightVolume = .3f; + +void CBasePlayer :: FlashlightTurnOn( void ) +{ + if ( !g_pGameRules->FAllowFlashlight() ) + { + return; + } + + if ( (pev->weapons & (1<effects, EF_DIMLIGHT); + + /*MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); + WRITE_BYTE(1); + WRITE_BYTE(m_iFlashBattery); + MESSAGE_END(); + + m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time;*/ + + } +} + + +void CBasePlayer :: FlashlightTurnOff( void ) +{ + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_FLASHLIGHT_OFF, kFlashlightVolume, ATTN_NORM, 0, PITCH_NORM ); + ClearBits(pev->effects, EF_DIMLIGHT); + + /*MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); + WRITE_BYTE(0); + WRITE_BYTE(m_iFlashBattery); + MESSAGE_END(); + + m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time;*/ + +} + +/* +=============== +ForceClientDllUpdate + +When recording a demo, we need to have the server tell us the entire client state +so that the client side .dll can behave correctly. +Reset stuff so that the state is transmitted. +=============== +*/ +void CBasePlayer :: ForceClientDllUpdate( void ) +{ + m_iClientHealth = -1; + m_iClientBattery = -1; + m_iTrain |= TRAIN_NEW; // Force new train message. + m_fWeapon = FALSE; // Force weapon send + m_fKnownItem = FALSE; // Force weaponinit messages. + m_fInitHUD = TRUE; // Force HUD gmsgResetHUD message + + // Now force all the necessary messages + // to be sent. + UpdateClientData(); + // m_fWeapon = TRUE; // Force weapon send +} + +/* +============ +ImpulseCommands +============ +*/ +extern float g_flWeaponCheat; + +void CBasePlayer::ImpulseCommands( ) +{ + TraceResult tr;// UNDONE: kill me! This is temporary for PreAlpha CDs + + int iImpulse = (int)pev->impulse; + switch (iImpulse) + { + case IMPULSE_FLASHLIGHT: + // temporary flashlight for level designers + if ( FlashlightIsOn() ) + { + FlashlightTurnOff(); + } + else + { + FlashlightTurnOn(); + } + break; + + case IMPULSE_SPRAYPAINT:// paint decal + + if ( gpGlobals->time < m_flNextDecalTime ) + { + // too early! + break; + } + + UTIL_MakeVectors(pev->v_angle); + UTIL_TraceLine ( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + gpGlobals->v_forward * 128, ignore_monsters, ENT(pev), & tr); + + if ( tr.flFraction != 1.0 ) + {// line hit something, so paint a decal + m_flNextDecalTime = gpGlobals->time + decalfrequency.value; + CSprayCan *pCan = GetClassPtr((CSprayCan *)NULL); + pCan->Spawn( pev ); + } + + break; +// HLDSDK 2.3 update +// case IMPULSE_DEMORECORD: // Demo recording, update client dll specific data again. +// ForceClientDllUpdate(); +// break; + default: + // check all of the cheat impulse commands now + CheatImpulseCommands( iImpulse ); + break; + } + + pev->impulse = 0; +} + +//========================================================= +//========================================================= +void CBasePlayer::CheatImpulseCommands( int iImpulse ) +{ +} + +// +// Add a weapon to the player (Item == Weapon == Selectable Object) +// +int CBasePlayer::AddPlayerItem( CBasePlayerItem *pItem ) +{ + CBasePlayerItem *pInsert; + + pInsert = m_rgpPlayerItems[pItem->iItemSlot()]; + ItemInfo ii; + pItem->GetItemInfo(&ii); + + if ( pItem->iItemSlot() == 1 || ii.iId == AVH_WEAPON_MINE || ii.iId == AVH_WEAPON_WELDER) { + this->EffectivePlayerClassChanged(); + } + while (pInsert) + { + if (FClassnameIs( pInsert->pev, STRING( pItem->pev->classname) )) + { + if (pItem->AddDuplicate( pInsert )) + { + g_pGameRules->PlayerGotWeapon ( this, pItem ); + pItem->CheckRespawn(); + + // ugly hack to update clip w/o an update clip message + pInsert->UpdateItemInfo( ); + if (m_pActiveItem) + m_pActiveItem->UpdateItemInfo( ); + + pItem->Kill( ); + } + else if (gEvilImpulse101) + { + // FIXME: remove anyway for deathmatch testing + pItem->Kill( ); + } + return FALSE; + } + pInsert = pInsert->m_pNext; + } + + if (pItem->AddToPlayer( this )) + { + g_pGameRules->PlayerGotWeapon ( this, pItem ); + pItem->CheckRespawn(); + + pItem->m_pNext = m_rgpPlayerItems[pItem->iItemSlot()]; + m_rgpPlayerItems[pItem->iItemSlot()] = pItem; + + // should we switch to this item? + if ( g_pGameRules->FShouldSwitchWeapon( this, pItem ) ) + { + SwitchWeapon( pItem ); + } + + return TRUE; + } + else if (gEvilImpulse101) + { + // FIXME: remove anyway for deathmatch testing + pItem->Kill( ); + } + return FALSE; +} + + + +int CBasePlayer::RemovePlayerItem( CBasePlayerItem *pItem ) +{ + + if (m_pActiveItem == pItem) + { + + ResetAutoaim( ); + pItem->Holster( ); + pItem->pev->nextthink = 0;// crowbar may be trying to swing again, etc. + pItem->SetThink( NULL ); + m_pActiveItem = NULL; + pev->viewmodel = 0; + pev->weaponmodel = 0; + + } + else if ( m_pLastItem == pItem ) + m_pLastItem = NULL; + + AvHBasePlayerWeapon* theWeapon = dynamic_cast(pItem); + if(theWeapon) + { + ItemInfo theInfo; + theWeapon->GetItemInfo(&theInfo); + int theID = theInfo.iId; + this->pev->weapons &= ~(1<iItemSlot()]; + + if (pPrev == pItem) + { + m_rgpPlayerItems[pItem->iItemSlot()] = pItem->m_pNext; + return TRUE; + } + else + { + while (pPrev && pPrev->m_pNext != pItem) + { + pPrev = pPrev->m_pNext; + } + if (pPrev) + { + pPrev->m_pNext = pItem->m_pNext; + return TRUE; + } + } + return FALSE; +} + + +// +// Returns the unique ID for the ammo, or -1 if error +// +int CBasePlayer :: GiveAmmo( int iCount, char *szName, int iMax ) +{ + if ( !szName ) + { + // no ammo. + return -1; + } + + if ( !g_pGameRules->CanHaveAmmo( this, szName, iMax ) ) + { + // game rules say I can't have any more of this ammo type. + return -1; + } + + int i = 0; + + i = GetAmmoIndex( szName ); + + if ( i < 0 || i >= MAX_AMMO_SLOTS ) + return -1; + + int iAdd = min( iCount, iMax - m_rgAmmo[i] ); + if ( iAdd < 1 ) + return i; + + m_rgAmmo[ i ] += iAdd; + + + // Send the message that ammo has been picked up + NetMsg_AmmoPickup( pev, GetAmmoIndex(szName), iAdd ); + + TabulateAmmo(); + + return i; +} + + +/* +============ +ItemPreFrame + +Called every frame by the player PreThink +============ +*/ +void CBasePlayer::ItemPreFrame() +{ + if ( m_flNextAttack > 0 ) + { + return; + } + + if (!m_pActiveItem) + return; + + m_pActiveItem->ItemPreFrame( ); +} + + +/* +============ +ItemPostFrame + +Called every frame by the player PostThink +============ +*/ +void CBasePlayer::ItemPostFrame() +{ + static int fInSelect = FALSE; + + // check if the player is using a tank + if ( m_pTank != NULL ) + return; + + ImpulseCommands(); + + if ( m_flNextAttack > 0 ) + { + return; + } + + if (!m_pActiveItem) + return; + + // Only update weapon once game has started (no firing before then) + //if(GetGameRules()->GetGameStarted()) + //{ + this->m_pActiveItem->ItemPostFrame( ); + //} +} + +int CBasePlayer::AmmoInventory( int iAmmoIndex ) +{ + if (iAmmoIndex == -1) + { + return -1; + } + + return m_rgAmmo[ iAmmoIndex ]; +} + +int CBasePlayer::GetAmmoIndex(const char *psz) +{ + int i; + + if (!psz) + return -1; + + for (i = 1; i < MAX_AMMO_SLOTS; i++) + { + if ( !CBasePlayerItem::AmmoInfoArray[i].pszName ) + continue; + + if (stricmp( psz, CBasePlayerItem::AmmoInfoArray[i].pszName ) == 0) + return i; + } + + return -1; +} + +int CBasePlayer::GetMaxWalkSpeed(void) const +{ + return 220; +} + +// Called from UpdateClientData +// makes sure the client has all the necessary ammo info, if values have changed +void CBasePlayer::SendAmmoUpdate(void) +{ + int theAmmoToSend[MAX_AMMO_SLOTS]; + memcpy(&theAmmoToSend, &this->m_rgAmmo, MAX_AMMO_SLOTS*sizeof(int)); + +// if(this->pev->iuser1 == OBS_IN_EYE) +// { +// CBasePlayer* theEntity = NULL; +// if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) +// { +// memcpy(&theAmmoToSend, &theEntity->m_rgAmmo, MAX_AMMO_SLOTS*sizeof(int)); +// } +// } + + for (int i=0; i < MAX_AMMO_SLOTS;i++) + { + if (theAmmoToSend[i] != m_rgAmmoLast[i]) + { + m_rgAmmoLast[i] = theAmmoToSend[i]; + + // TODO: Fix me! + //ASSERT( m_rgAmmo[i] >= 0 ); + //ASSERT( m_rgAmmo[i] < 255 ); + if((theAmmoToSend[i] >= 0) && (theAmmoToSend[i] < 255)) + { + // send "Ammo" update message + NetMsg_AmmoX( pev, i, max( min( theAmmoToSend[i], 254 ), 0 ) ); + } + } + } +} + +/* +========================================================= + UpdateClientData + +resends any changed player HUD info to the client. +Called every frame by PlayerPreThink +Also called at start of demo recording and playback by +ForceClientDllUpdate to ensure the demo gets messages +reflecting all of the HUD state info. +========================================================= +*/ +void CBasePlayer :: UpdateClientData( void ) +{ + CBasePlayer* thePlayerToUseForWeaponUpdates = this; +// if(this->pev->iuser1 == OBS_IN_EYE) +// { +// CBasePlayer* theSpectatingPlayer = NULL; +// if(AvHSUGetEntityFromIndex(this->pev->iuser2, theSpectatingPlayer)) +// { +// thePlayerToUseForWeaponUpdates = theSpectatingPlayer; +// } +// } + + if (m_fInitHUD) + { + m_fInitHUD = FALSE; + gInitHUD = FALSE; + + NetMsg_ResetHUD( pev ); + + if ( !m_fGameHUDInitialized ) + { + NetMsg_InitHUD( pev ); + + g_pGameRules->InitHUD( this ); + m_fGameHUDInitialized = TRUE; + if ( g_pGameRules->IsMultiplayer() ) + { + FireTargets( "game_playerjoin", this, this, USE_TOGGLE, 0 ); + } + } + FireTargets( "game_playerspawn", this, this, USE_TOGGLE, 0 ); + + InitStatusBar(); + } + + if ( m_iHideHUD != m_iClientHideHUD ) + { + NetMsg_HideWeapon( pev, m_iHideHUD ); + m_iClientHideHUD = m_iHideHUD; + } + + if ( m_iFOV != m_iClientFOV ) + { + NetMsg_SetFOV( pev, m_iFOV ); + // cache FOV change at end of function, so weapon updates can see that FOV has changed + } + + // HACKHACK -- send the message to display the game title + if (gDisplayTitle) + { + NetMsg_ShowGameTitle( pev ); + gDisplayTitle = 0; + } + + if ((int)pev->health != m_iClientHealth) //: this cast to int is important, otherwise we spam the message, this is just a quick and easy fix. + { + NetMsg_Health( pev, max( pev->health, 0.0f ) ); + m_iClientHealth = (int)pev->health; + } + + if ((int)pev->armorvalue != m_iClientBattery) + { + NetMsg_Battery( pev, (int)pev->armorvalue ); + m_iClientBattery = (int)pev->armorvalue; + } + + if (pev->dmg_take || pev->dmg_save || m_bitsHUDDamage != m_bitsDamageType) + { + // Comes from inside me if not set + Vector damageOrigin = pev->origin; + // send "damage" message + // causes screen to flash, and pain compass to show direction of damage + edict_t *other = pev->dmg_inflictor; + if ( other ) + { + CBaseEntity *pEntity = CBaseEntity::Instance(other); + if ( pEntity ) + damageOrigin = pEntity->Center(); + } + + // only send down damage type that have hud art + int visibleDamageBits = m_bitsDamageType & DMG_SHOWNHUD; + + float origin[] = { damageOrigin.x, damageOrigin.y, damageOrigin.z }; + NetMsg_Damage( pev, pev->dmg_save, pev->dmg_take, 0, origin ); + + pev->dmg_take = 0; + pev->dmg_save = 0; + m_bitsHUDDamage = m_bitsDamageType; + + // Clear off non-time-based damage indicators + m_bitsDamageType &= DMG_TIMEBASED; + } + + if (m_iTrain & TRAIN_NEW) + { + // send "health" update message + NetMsg_Train( pev, m_iTrain & 0xF ); + m_iTrain &= ~TRAIN_NEW; + } + + // + // New Weapon? + // + + bool forceCurWeaponUpdate = false; + + if (!m_fKnownItem) + { + + m_fKnownItem = TRUE; + + // WeaponInit Message + // byte = # of weapons + // + // for each weapon: + // byte name str length (not including null) + // bytes... name + // byte Ammo Type + // byte Ammo2 Type + // byte bucket + // byte bucket pos + // byte flags + // ???? Icons + + // Send ALL the weapon info now + this->SendWeaponUpdate(); + + // : HACK force an full curweapon update after each bunch of weaponlists sent + forceCurWeaponUpdate = true; + // : + } + + + SendAmmoUpdate(); + + // Update all the items + for ( int i = 0; i < MAX_ITEM_TYPES; i++ ) + { + if (forceCurWeaponUpdate == true) + m_fWeapon = FALSE; + if ( thePlayerToUseForWeaponUpdates->m_rgpPlayerItems[i] ) // each item updates it's successors + thePlayerToUseForWeaponUpdates->m_rgpPlayerItems[i]->UpdateClientData( thePlayerToUseForWeaponUpdates ); + } + if (forceCurWeaponUpdate == true) + m_fWeapon = TRUE; + + // Cache and client weapon change + m_pClientActiveItem = thePlayerToUseForWeaponUpdates->m_pActiveItem; + m_iClientFOV = thePlayerToUseForWeaponUpdates->m_iFOV; + + // Update Status Bar if we're playing + AvHPlayer* thePlayer = dynamic_cast(this); + if(thePlayer && (thePlayer->GetPlayMode() == PLAYMODE_PLAYING)) + { + if ( m_flNextSBarUpdateTime < gpGlobals->time ) + { + UpdateStatusBar(); + m_flNextSBarUpdateTime = gpGlobals->time + 0.2; + } + } +} + +void CBasePlayer::SendWeaponUpdate() +{ + int i; + + for (i = 0; i < MAX_WEAPONS; i++) + { + ItemInfo& II = CBasePlayerItem::ItemInfoArray[i]; + + if ( !II.iId ) + continue; + + const char *pszName; + if (!II.pszName) + pszName = "Empty"; + else + pszName = II.pszName; + + WeaponList weapon; + weapon.weapon_name = pszName; + weapon.ammo1_type = GetAmmoIndex(II.pszAmmo1); + weapon.ammo2_type = GetAmmoIndex(II.pszAmmo2); + weapon.ammo1_max_amnt = II.iMaxAmmo1; + weapon.ammo2_max_amnt = II.iMaxAmmo2; + weapon.bucket = II.iSlot; + weapon.bucket_pos = II.iPosition; + weapon.bit_index = II.iId; + weapon.flags = II.iFlags; + + NetMsg_WeaponList( pev, weapon ); + } + +} + +//========================================================= +// FBecomeProne - Overridden for the player to set the proper +// physics flags when a barnacle grabs player. +//========================================================= +BOOL CBasePlayer :: FBecomeProne ( void ) +{ + m_afPhysicsFlags |= PFLAG_ONBARNACLE; + return TRUE; +} + +//========================================================= +// BarnacleVictimBitten - bad name for a function that is called +// by Barnacle victims when the barnacle pulls their head +// into its mouth. For the player, just die. +//========================================================= +void CBasePlayer :: BarnacleVictimBitten ( entvars_t *pevBarnacle ) +{ + TakeDamage ( pevBarnacle, pevBarnacle, pev->health + pev->armorvalue, DMG_SLASH | DMG_ALWAYSGIB ); +} + +//========================================================= +// BarnacleVictimReleased - overridden for player who has +// physics flags concerns. +//========================================================= +void CBasePlayer :: BarnacleVictimReleased ( void ) +{ + m_afPhysicsFlags &= ~PFLAG_ONBARNACLE; +} + + +//========================================================= +// Illumination +// return player light level plus virtual muzzle flash +//========================================================= +int CBasePlayer :: Illumination( void ) +{ + int iIllum = CBaseEntity::Illumination( ); + + iIllum += m_iWeaponFlash; + if (iIllum > 255) + return 255; + return iIllum; +} + + +void CBasePlayer :: EnableControl(BOOL fControl) +{ + if (!fControl) + pev->flags |= FL_FROZEN; + else + pev->flags &= ~FL_FROZEN; + +} + + +#define DOT_1DEGREE 0.9998476951564 +#define DOT_2DEGREE 0.9993908270191 +#define DOT_3DEGREE 0.9986295347546 +#define DOT_4DEGREE 0.9975640502598 +#define DOT_5DEGREE 0.9961946980917 +#define DOT_6DEGREE 0.9945218953683 +#define DOT_7DEGREE 0.9925461516413 +#define DOT_8DEGREE 0.9902680687416 +#define DOT_9DEGREE 0.9876883405951 +#define DOT_10DEGREE 0.9848077530122 +#define DOT_15DEGREE 0.9659258262891 +#define DOT_20DEGREE 0.9396926207859 +#define DOT_25DEGREE 0.9063077870367 + +//========================================================= +// Autoaim +// set crosshair position to point to enemey +//========================================================= +Vector CBasePlayer :: GetAutoaimVector( float flDelta ) +{ + if (g_iSkillLevel == SKILL_HARD) + { + UTIL_MakeVectors( pev->v_angle + pev->punchangle ); + return gpGlobals->v_forward; + } + + Vector vecSrc = GetGunPosition( ); + float flDist = 8192; + + // always use non-sticky autoaim + // UNDONE: use sever variable to chose! + if (1 || g_iSkillLevel == SKILL_MEDIUM) + { + m_vecAutoAim = Vector( 0, 0, 0 ); + // flDelta *= 0.5; + } + + BOOL m_fOldTargeting = m_fOnTarget; + Vector angles = AutoaimDeflection(vecSrc, flDist, flDelta ); + + // update ontarget if changed + if ( !g_pGameRules->AllowAutoTargetCrosshair() ) + m_fOnTarget = 0; + else if (m_fOldTargeting != m_fOnTarget) + { + m_pActiveItem->UpdateItemInfo( ); + } + + if (angles.x > 180) + angles.x -= 360; + if (angles.x < -180) + angles.x += 360; + if (angles.y > 180) + angles.y -= 360; + if (angles.y < -180) + angles.y += 360; + + if (angles.x > 25) + angles.x = 25; + if (angles.x < -25) + angles.x = -25; + if (angles.y > 12) + angles.y = 12; + if (angles.y < -12) + angles.y = -12; + + + // always use non-sticky autoaim + // UNDONE: use sever variable to chose! + if (0 || g_iSkillLevel == SKILL_EASY) + { + m_vecAutoAim = m_vecAutoAim * 0.67 + angles * 0.33; + } + else + { + m_vecAutoAim = angles * 0.9; + } + + // m_vecAutoAim = m_vecAutoAim * 0.99; + + // Don't send across network if sv_aim is 0 + if ( g_psv_aim->value != 0 ) + { + if ( m_vecAutoAim.x != m_lastx || + m_vecAutoAim.y != m_lasty ) + { + SET_CROSSHAIRANGLE( edict(), -m_vecAutoAim.x, m_vecAutoAim.y ); + + m_lastx = m_vecAutoAim.x; + m_lasty = m_vecAutoAim.y; + } + } + + // ALERT( at_console, "%f %f\n", angles.x, angles.y ); + + UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); + return gpGlobals->v_forward; +} + + +Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta ) +{ + edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); + CBaseEntity *pEntity; + float bestdot; + Vector bestdir; + edict_t *bestent; + TraceResult tr; + + if ( g_psv_aim->value == 0 ) + { + m_fOnTarget = FALSE; + return g_vecZero; + } + + UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); + + // try all possible entities + bestdir = gpGlobals->v_forward; + bestdot = flDelta; // +- 10 degrees + bestent = NULL; + + m_fOnTarget = FALSE; + + UTIL_TraceLine( vecSrc, vecSrc + bestdir * flDist, dont_ignore_monsters, edict(), &tr ); + + + if ( tr.pHit && tr.pHit->v.takedamage != DAMAGE_NO) + { + // don't look through water + if (!((pev->waterlevel != 3 && tr.pHit->v.waterlevel == 3) + || (pev->waterlevel == 3 && tr.pHit->v.waterlevel == 0))) + { + if (tr.pHit->v.takedamage == DAMAGE_AIM) + m_fOnTarget = TRUE; + + return m_vecAutoAim; + } + } + + for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) + { + Vector center; + Vector dir; + float dot; + + if ( pEdict->free ) // Not in use + continue; + + if (pEdict->v.takedamage != DAMAGE_AIM) + continue; + if (pEdict == edict()) + continue; +// if (pev->team > 0 && pEdict->v.team == pev->team) +// continue; // don't aim at teammate + if ( !g_pGameRules->ShouldAutoAim( this, pEdict ) ) + continue; + + pEntity = Instance( pEdict ); + if (pEntity == NULL) + continue; + + if (!pEntity->IsAlive()) + continue; + + // don't look through water + if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) + || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0)) + continue; + + center = pEntity->BodyTarget( vecSrc ); + + dir = (center - vecSrc).Normalize( ); + + // make sure it's in front of the player + if (DotProduct (dir, gpGlobals->v_forward ) < 0) + continue; + + dot = fabs( DotProduct (dir, gpGlobals->v_right ) ) + + fabs( DotProduct (dir, gpGlobals->v_up ) ) * 0.5; + + // tweek for distance + dot *= 1.0 + 0.2 * ((center - vecSrc).Length() / flDist); + + if (dot > bestdot) + continue; // to far to turn + + UTIL_TraceLine( vecSrc, center, dont_ignore_monsters, edict(), &tr ); + if (tr.flFraction != 1.0 && tr.pHit != pEdict) + { + // ALERT( at_console, "hit %s, can't see %s\n", STRING( tr.pHit->v.classname ), STRING( pEdict->v.classname ) ); + continue; + } + + // don't shoot at friends + if (IRelationship( pEntity ) < 0) + { + if ( !pEntity->IsPlayer() && !g_pGameRules->IsDeathmatch()) + // ALERT( at_console, "friend\n"); + continue; + } + + // can shoot at this one + bestdot = dot; + bestent = pEdict; + bestdir = dir; + } + + if (bestent) + { + bestdir = UTIL_VecToAngles (bestdir); + bestdir.x = -bestdir.x; + bestdir = bestdir - pev->v_angle - pev->punchangle; + + if (bestent->v.takedamage == DAMAGE_AIM) + m_fOnTarget = TRUE; + + return bestdir; + } + + return Vector( 0, 0, 0 ); +} + + +void CBasePlayer :: ResetAutoaim( ) +{ + if (m_vecAutoAim.x != 0 || m_vecAutoAim.y != 0) + { + m_vecAutoAim = Vector( 0, 0, 0 ); + SET_CROSSHAIRANGLE( edict(), 0, 0 ); + } + m_fOnTarget = FALSE; +} + +/* +============= +SetCustomDecalFrames + + UNDONE: Determine real frame limit, 8 is a placeholder. + Note: -1 means no custom frames present. +============= +*/ +void CBasePlayer :: SetCustomDecalFrames( int nFrames ) +{ + if (nFrames > 0 && + nFrames < 8) + m_nCustomSprayFrames = nFrames; + else + m_nCustomSprayFrames = -1; +} + +/* +============= +GetCustomDecalFrames + + Returns the # of custom frames this player's custom clan logo contains. +============= +*/ +int CBasePlayer :: GetCustomDecalFrames( void ) +{ + return m_nCustomSprayFrames; +} + + +//========================================================= +// DropPlayerItem - drop the named item, or if no name, +// the active item. +//========================================================= +void CBasePlayer::DropPlayerItem ( char *pszItemName ) +{ + if ( !g_pGameRules->IsMultiplayer() || (weaponstay.value > 0) ) + { + // no dropping in single player. + return; + } + + if ( !strlen( pszItemName ) ) + { + // if this string has no length, the client didn't type a name! + // assume player wants to drop the active item. + // make the string null to make future operations in this function easier + pszItemName = NULL; + } + + CBasePlayerItem *pWeapon; + int i; + + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + pWeapon = m_rgpPlayerItems[ i ]; + + while ( pWeapon ) + { + if ( pszItemName ) + { + // try to match by name. + const char* theWeaponClassName = STRING( pWeapon->pev->classname ); + if ( !strcmp( pszItemName, theWeaponClassName) ) + { + // match! + break; + } + } + else + { + // trying to drop active item + if ( pWeapon == m_pActiveItem ) + { + // active item! + break; + } + } + + pWeapon = pWeapon->m_pNext; + } + + + // if we land here with a valid pWeapon pointer, that's because we found the + // item we want to drop and hit a BREAK; pWeapon is the item. + if ( pWeapon ) + { + g_pGameRules->GetNextBestWeapon( this, pWeapon ); + + UTIL_MakeVectors ( pev->angles ); + + pev->weapons &= ~(1<m_iId);// take item off hud + + CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin + gpGlobals->v_forward * 10, pev->angles, edict() ); + pWeaponBox->pev->angles.x = 0; + pWeaponBox->pev->angles.z = 0; + pWeaponBox->PackWeapon( pWeapon ); + pWeaponBox->pev->velocity = gpGlobals->v_forward * 300 + gpGlobals->v_forward * 100; + + // drop half of the ammo for this weapon. + int iAmmoIndex; + + iAmmoIndex = GetAmmoIndex ( pWeapon->pszAmmo1() ); // ??? + + if ( iAmmoIndex != -1 ) + { + // this weapon weapon uses ammo, so pack an appropriate amount. + if ( pWeapon->iFlags() & ITEM_FLAG_EXHAUSTIBLE ) + { + // pack up all the ammo, this weapon is its own ammo type + pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] ); + m_rgAmmo[ iAmmoIndex ] = 0; + + } + else + { + // pack half of the ammo + pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] / 2 ); + m_rgAmmo[ iAmmoIndex ] /= 2; + } + + } + + return;// we're done, so stop searching with the FOR loop. + } + } +} + +//========================================================= +// HasPlayerItem Does the player already have this item? +//========================================================= +BOOL CBasePlayer::HasPlayerItem( CBasePlayerItem *pCheckItem ) +{ + CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()]; + + while (pItem) + { + if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) )) + { + return TRUE; + } + pItem = pItem->m_pNext; + } + + return FALSE; +} + +//========================================================= +// HasNamedPlayerItem Does the player already have this item? +//========================================================= +CBasePlayerItem* CBasePlayer::HasNamedPlayerItem( const char *pszItemName ) +{ + CBasePlayerItem *pReturn = NULL; + CBasePlayerItem *pItem = NULL; + int i; + + for ( i = 0 ; (i < MAX_ITEM_TYPES) && !pReturn ; i++ ) + { + pItem = m_rgpPlayerItems[ i ]; + + while (pItem && !pReturn) + { + if ( !strcmp( pszItemName, STRING( pItem->pev->classname ) ) ) + { + pReturn = pItem; + } + pItem = pItem->m_pNext; + } + } + + return pReturn; +} + +//========================================================= +// +//========================================================= +BOOL CBasePlayer :: SwitchWeapon( CBasePlayerItem *pWeapon ) +{ + if ( !pWeapon->CanDeploy() ) + { + return FALSE; + } + + ResetAutoaim( ); + + if (m_pActiveItem) + { + m_pActiveItem->Holster( ); + } + + m_pActiveItem = pWeapon; + pWeapon->Deploy( ); + + return TRUE; +} + +//========================================================= +// Dead HEV suit prop +//========================================================= +class CDeadHEV : public CBaseMonster +{ +public: + void Spawn( void ); + int Classify ( void ) { return CLASS_HUMAN_MILITARY; } + + void KeyValue( KeyValueData *pkvd ); + + int m_iPose;// which sequence to display -- temporary, don't need to save + static char *m_szPoses[4]; +}; + +char *CDeadHEV::m_szPoses[] = { "deadback", "deadsitting", "deadstomach", "deadtable" }; + +void CDeadHEV::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "pose")) + { + m_iPose = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseMonster::KeyValue( pkvd ); +} + +LINK_ENTITY_TO_CLASS( monster_hevsuit_dead, CDeadHEV ); + +//========================================================= +// ********** DeadHEV SPAWN ********** +//========================================================= +void CDeadHEV :: Spawn( void ) +{ + PRECACHE_MODEL("models/player.mdl"); + SET_MODEL(ENT(pev), "models/player.mdl"); + + pev->effects = 0; + pev->yaw_speed = 8; + pev->sequence = 0; + pev->body = 1; + m_bloodColor = BLOOD_COLOR_RED; + + pev->sequence = LookupSequence( m_szPoses[m_iPose] ); + + if (pev->sequence == -1) + { + ALERT ( at_console, "Dead hevsuit with bad pose\n" ); + pev->sequence = 0; + pev->effects = EF_BRIGHTFIELD; + } + + // Corpses have less health + pev->health = 8; + + MonsterInitDead(); +} + + +class CStripWeapons : public CPointEntity +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + +private: +}; + +LINK_ENTITY_TO_CLASS( player_weaponstrip, CStripWeapons ); + +void CStripWeapons :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBasePlayer *pPlayer = NULL; + + if ( pActivator && pActivator->IsPlayer() ) + { + pPlayer = (CBasePlayer *)pActivator; + } + else if ( !g_pGameRules->IsDeathmatch() ) + { + pPlayer = (CBasePlayer *)CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) ); + } + + if ( pPlayer ) + pPlayer->RemoveAllItems( FALSE ); +} + + +class CRevertSaved : public CPointEntity +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void EXPORT MessageThink( void ); + void EXPORT LoadThink( void ); + void KeyValue( KeyValueData *pkvd ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + inline float Duration( void ) { return pev->dmg_take; } + inline float HoldTime( void ) { return pev->dmg_save; } + inline float MessageTime( void ) { return m_messageTime; } + inline float LoadTime( void ) { return m_loadTime; } + + inline void SetDuration( float duration ) { pev->dmg_take = duration; } + inline void SetHoldTime( float hold ) { pev->dmg_save = hold; } + inline void SetMessageTime( float time ) { m_messageTime = time; } + inline void SetLoadTime( float time ) { m_loadTime = time; } + +private: + float m_messageTime; + float m_loadTime; +}; + +LINK_ENTITY_TO_CLASS( player_loadsaved, CRevertSaved ); + +TYPEDESCRIPTION CRevertSaved::m_SaveData[] = +{ + DEFINE_FIELD( CRevertSaved, m_messageTime, FIELD_FLOAT ), // These are not actual times, but durations, so save as floats + DEFINE_FIELD( CRevertSaved, m_loadTime, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE( CRevertSaved, CPointEntity ); + +void CRevertSaved :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "duration")) + { + SetDuration( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "holdtime")) + { + SetHoldTime( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "messagetime")) + { + SetMessageTime( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "loadtime")) + { + SetLoadTime( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + +void CRevertSaved :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), pev->renderamt, FFADE_OUT ); + pev->nextthink = gpGlobals->time + MessageTime(); + SetThink( &CRevertSaved::MessageThink ); +} + + +void CRevertSaved :: MessageThink( void ) +{ + UTIL_ShowMessageAll( STRING(pev->message) ); + float nextThink = LoadTime() - MessageTime(); + if ( nextThink > 0 ) + { + pev->nextthink = gpGlobals->time + nextThink; + SetThink( &CRevertSaved::LoadThink ); + } + else + LoadThink(); +} + + +void CRevertSaved :: LoadThink( void ) +{ + if ( !gpGlobals->deathmatch ) + { + SERVER_COMMAND("reload\n"); + } +} + + +//========================================================= +// Multiplayer intermission spots. +//========================================================= +class CInfoIntermission:public CPointEntity +{ + void Spawn( void ); + void Think( void ); +}; + +void CInfoIntermission::Spawn( void ) +{ + UTIL_SetOrigin( pev, pev->origin ); + pev->solid = SOLID_NOT; + pev->effects = EF_NODRAW; + pev->v_angle = g_vecZero; + + pev->nextthink = gpGlobals->time + 2;// let targets spawn! + +} + +void CInfoIntermission::Think ( void ) +{ + edict_t *pTarget; + + // find my target + pTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) ); + + if ( !FNullEnt(pTarget) ) + { + pev->v_angle = UTIL_VecToAngles( (pTarget->v.origin - pev->origin).Normalize() ); + pev->v_angle.x = -pev->v_angle.x; + } +} + +LINK_ENTITY_TO_CLASS( info_intermission, CInfoIntermission ); + diff --git a/main/source/dlls/singleplay_gamerules.cpp b/main/source/dlls/singleplay_gamerules.cpp index 160757a..28db751 100644 --- a/main/source/dlls/singleplay_gamerules.cpp +++ b/main/source/dlls/singleplay_gamerules.cpp @@ -34,7 +34,7 @@ extern int gmsgMOTD; //========================================================= CHalfLifeRules::CHalfLifeRules( void ) { - RefreshSkillData(); + //RefreshSkillData(); } //========================================================= diff --git a/main/source/dlls/teamplay_gamerules.cpp b/main/source/dlls/teamplay_gamerules.cpp index 4228abe..5d66d42 100644 --- a/main/source/dlls/teamplay_gamerules.cpp +++ b/main/source/dlls/teamplay_gamerules.cpp @@ -95,12 +95,12 @@ void CHalfLifeTeamplay :: Think ( void ) return; } - float flTimeLimit = CVAR_GET_FLOAT("mp_timelimit") * 60; + float flTimeLimit = ns_cvar_float(&timelimit) * 60; time_remaining = (int)(flTimeLimit ? ( flTimeLimit - gpGlobals->time ) : 0); // Don't map switch in tourny mode, it signals the end of the match instead - bool theIsTournyMode = (CVAR_GET_FLOAT(kvTournamentMode) > 0); + bool theIsTournyMode = (ns_cvar_int(&avh_tournamentmode) > 0); if ( flTimeLimit != 0 && (gpGlobals->time >= flTimeLimit) && !theIsTournyMode) { GoToIntermission(); diff --git a/main/source/dlls/triggers.cpp b/main/source/dlls/triggers.cpp index e008576..d41ed2c 100644 --- a/main/source/dlls/triggers.cpp +++ b/main/source/dlls/triggers.cpp @@ -28,6 +28,7 @@ #include "trains.h" // trigger_camera has train functionality #include "gamerules.h" #include "dlls/triggers.h" +#include "mod/AvHServerVariables.h" #define SF_TRIGGER_PUSH_START_OFF 2//spawnflag that makes trigger_push spawn turned OFF #define SF_TRIGGER_HURT_TARGETONCE 1// Only fire hurt target once @@ -535,7 +536,7 @@ void CBaseTrigger::InitTrigger( ) pev->solid = SOLID_TRIGGER; pev->movetype = MOVETYPE_NONE; SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world - if ( CVAR_GET_FLOAT("showtriggers") == 0 ) + if ( ns_cvar_float(showtriggers) == 0 ) SetBits( pev->effects, EF_NODRAW ); } @@ -1738,7 +1739,7 @@ void CLadder :: Precache( void ) // Do all of this in here because we need to 'convert' old saved games pev->solid = SOLID_NOT; pev->skin = CONTENTS_LADDER; - if ( CVAR_GET_FLOAT("showtriggers") == 0 ) + if ( ns_cvar_float(showtriggers) == 0 ) { pev->rendermode = kRenderTransTexture; pev->renderamt = 0; diff --git a/main/source/dlls/util.cpp b/main/source/dlls/util.cpp index 7452bbc..0ef106c 100644 --- a/main/source/dlls/util.cpp +++ b/main/source/dlls/util.cpp @@ -32,6 +32,7 @@ #include "gamerules.h" #include "mod/AvHServerUtil.h" #include "mod/AvHNetworkMessages.h" +#include "mod/AvHServerVariables.h" //#include "mod/AvHSharedUtil.h" //#include "mod/AvHGamerules.h" @@ -1189,12 +1190,12 @@ BOOL UTIL_ShouldShowBlood( int color ) { if ( color == BLOOD_COLOR_RED ) { - if ( CVAR_GET_FLOAT("violence_hblood") != 0 ) + if ( ns_cvar_float(violence_hblood) != 0 ) return TRUE; } else { - if ( CVAR_GET_FLOAT("violence_ablood") != 0 ) + if ( ns_cvar_float(violence_ablood) != 0 ) return TRUE; } } diff --git a/main/source/dlls/util.h b/main/source/dlls/util.h index 929918e..28292b4 100644 --- a/main/source/dlls/util.h +++ b/main/source/dlls/util.h @@ -1,585 +1,585 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -// -// Misc utility code -// -#ifndef UTIL_H -#define UTIL_H - -#include - -#ifndef ACTIVITY_H -#include "activity.h" -#endif - -#ifndef ENGINECALLBACK_H -#include "enginecallback.h" -#endif - -#include "game_shared/directorconst.h" - -//inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ); // implementation later in this file - -extern globalvars_t *gpGlobals; - -// Use this instead of ALLOC_STRING on constant strings -#define STRING(offset) (const char *)(gpGlobals->pStringBase + (int)offset) -// #define MAKE_STRING(str) ((int)str - (int)STRING(0)) - -#pragma warning(push) -#pragma warning(disable: 311) -inline string_t MAKE_STRING(const char* offset) -{ - return ((string_t)offset - (string_t)STRING(0)); -} -#pragma warning(pop) - -inline edict_t *FIND_ENTITY_BY_CLASSNAME(edict_t *entStart, const char *pszName) -{ - return FIND_ENTITY_BY_STRING(entStart, "classname", pszName); -} - -inline edict_t *FIND_ENTITY_BY_TARGETNAME(edict_t *entStart, const char *pszName) -{ - return FIND_ENTITY_BY_STRING(entStart, "targetname", pszName); -} - -// for doing a reverse lookup. Say you have a door, and want to find its button. -inline edict_t *FIND_ENTITY_BY_TARGET(edict_t *entStart, const char *pszName) -{ - return FIND_ENTITY_BY_STRING(entStart, "target", pszName); -} - -// Keeps clutter down a bit, when writing key-value pairs -#define WRITEKEY_INT(pf, szKeyName, iKeyValue) ENGINE_FPRINTF(pf, "\"%s\" \"%d\"\n", szKeyName, iKeyValue) -#define WRITEKEY_FLOAT(pf, szKeyName, flKeyValue) \ - ENGINE_FPRINTF(pf, "\"%s\" \"%f\"\n", szKeyName, flKeyValue) -#define WRITEKEY_STRING(pf, szKeyName, szKeyValue) \ - ENGINE_FPRINTF(pf, "\"%s\" \"%s\"\n", szKeyName, szKeyValue) -#define WRITEKEY_VECTOR(pf, szKeyName, flX, flY, flZ) \ - ENGINE_FPRINTF(pf, "\"%s\" \"%f %f %f\"\n", szKeyName, flX, flY, flZ) - -// Keeps clutter down a bit, when using a float as a bit-vector -#define SetBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) | (bits)) -#define ClearBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) & ~(bits)) -#define FBitSet(flBitVector, bit) ((int)(flBitVector) & (bit)) - -// Makes these more explicit, and easier to find -#define FILE_GLOBAL static -#define DLL_GLOBAL - -// Until we figure out why "const" gives the compiler problems, we'll just have to use -// this bogus "empty" define to mark things as constant. -#define CONSTANT - -// More explicit than "int" -typedef int EOFFSET; - -// In case it's not alread defined -typedef int BOOL; - -// In case this ever changes -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -// Keeps clutter down a bit, when declaring external entity/global method prototypes -#define DECLARE_GLOBAL_METHOD(MethodName) extern void DLLEXPORT MethodName( void ) -#define GLOBAL_METHOD(funcname) void DLLEXPORT funcname(void) - -// This is the glue that hooks .MAP entity class names to our CPP classes -// The _declspec forces them to be exported by name so we can do a lookup with GetProcAddress() -// The function is used to intialize / allocate the object for the entity -#ifdef _WIN32 -#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) \ - extern "C" _declspec( dllexport ) void mapClassName( entvars_t *pev ); \ - void mapClassName( entvars_t *pev ) { GetClassPtr( (DLLClassName *)pev ); } -#else -#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) extern "C" void mapClassName( entvars_t *pev ); void mapClassName( entvars_t *pev ) { GetClassPtr( (DLLClassName *)pev ); } -#endif - - -// -// Conversion among the three types of "entity", including identity-conversions. -// -#ifdef DEBUG - extern edict_t *DBG_EntOfVars(const entvars_t *pev); - inline edict_t *ENT(const entvars_t *pev) { return DBG_EntOfVars(pev); } -#else - inline edict_t *ENT(const entvars_t *pev) { return pev->pContainingEntity; } -#endif -inline edict_t *ENT(edict_t *pent) { return pent; } -inline edict_t *ENT(EOFFSET eoffset) { return (*g_engfuncs.pfnPEntityOfEntOffset)(eoffset); } -inline EOFFSET OFFSET(EOFFSET eoffset) { return eoffset; } -inline EOFFSET OFFSET(const edict_t *pent) -{ -#if _DEBUG - if ( !pent ) - ALERT( at_error, "Bad ent in OFFSET()\n" ); -#endif - return (*g_engfuncs.pfnEntOffsetOfPEntity)(pent); -} -inline EOFFSET OFFSET(entvars_t *pev) -{ -#if _DEBUG - if ( !pev ) - ALERT( at_error, "Bad pev in OFFSET()\n" ); -#endif - return OFFSET(ENT(pev)); -} -inline entvars_t *VARS(entvars_t *pev) { return pev; } - -inline entvars_t *VARS(edict_t *pent) -{ - if ( !pent ) - return NULL; - - return &pent->v; -} - -inline entvars_t* VARS(EOFFSET eoffset) { return VARS(ENT(eoffset)); } -inline int ENTINDEX(const edict_t *pEdict) { return (*g_engfuncs.pfnIndexOfEdict)(pEdict); } -inline edict_t* INDEXENT( int iEdictNum ) { return (*g_engfuncs.pfnPEntityOfEntIndex)(iEdictNum); } - -extern void NetworkMeterMessageBegin(int msg_dest, int msg_type, const float* pOrigin, edict_t* ed); - -inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ) -{ - NetworkMeterMessageBegin(msg_dest, msg_type, pOrigin, ENT(ent)); -} -inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin = NULL, edict_t* ed = NULL) -{ - NetworkMeterMessageBegin(msg_dest, msg_type, pOrigin, ed); -} - -// Testing the three types of "entity" for nullity -#define eoNullEntity 0 -inline BOOL FNullEnt(EOFFSET eoffset) { return eoffset == 0; } -inline BOOL FNullEnt(const edict_t* pent) { return pent == NULL || FNullEnt(OFFSET(pent)); } -inline BOOL FNullEnt(entvars_t* pev) { return pev == NULL || FNullEnt(OFFSET(pev)); } - -// Testing strings for nullity -#define iStringNull 0 -inline BOOL FStringNull(int iString) { return iString == iStringNull; } - -#define cchMapNameMost 32 - -// Dot products for view cone checking -#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees -#define VIEW_FIELD_WIDE (float)-0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks -#define VIEW_FIELD_NARROW (float)0.7 // +-45 degrees, more narrow check used to set up ranged attacks -#define VIEW_FIELD_ULTRA_NARROW (float)0.9 // +-25 degrees, more narrow check used to set up ranged attacks - -// All monsters need this data -#define DONT_BLEED -1 -#define BLOOD_COLOR_RED (BYTE)247 -#define BLOOD_COLOR_YELLOW (BYTE)195 -#define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW - -typedef enum -{ - - MONSTERSTATE_NONE = 0, - MONSTERSTATE_IDLE, - MONSTERSTATE_COMBAT, - MONSTERSTATE_ALERT, - MONSTERSTATE_HUNT, - MONSTERSTATE_PRONE, - MONSTERSTATE_SCRIPT, - MONSTERSTATE_PLAYDEAD, - MONSTERSTATE_DEAD - -} MONSTERSTATE; - - - -// Things that toggle (buttons/triggers/doors) need this -typedef enum - { - TS_AT_TOP, - TS_AT_BOTTOM, - TS_GOING_UP, - TS_GOING_DOWN - } TOGGLE_STATE; - -// Misc useful -inline BOOL FStrEq(const char*sz1, const char*sz2) - { return (strcmp(sz1, sz2) == 0); } -inline BOOL FClassnameIs(edict_t* pent, const char* szClassname) - { return FStrEq(STRING(VARS(pent)->classname), szClassname); } -inline BOOL FClassnameIs(entvars_t* pev, const char* szClassname) - { return FStrEq(STRING(pev->classname), szClassname); } - -class CBaseEntity; - -// Misc. Prototypes -extern void UTIL_SetSize (entvars_t* pev, const Vector &vecMin, const Vector &vecMax); -extern float UTIL_VecToYaw (const Vector &vec); -extern Vector UTIL_VecToAngles (const Vector &vec); -extern float UTIL_AngleMod (float a); -extern float UTIL_AngleDiff ( float destAngle, float srcAngle ); - -extern CBaseEntity *UTIL_FindEntityInSphere(CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius); -extern CBaseEntity *UTIL_FindEntityByString(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue ); -extern CBaseEntity *UTIL_FindEntityByClassname(CBaseEntity *pStartEntity, const char *szName ); -extern CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName ); -extern CBaseEntity *UTIL_FindEntityGeneric(const char *szName, Vector &vecSrc, float flRadius ); -int UTIL_CountEntitiesInSphere(const Vector& inVecCenter, float inRadius, const char* inClassName); - -// returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected -// otherwise returns NULL -// Index is 1 based -extern CBaseEntity *UTIL_PlayerByIndex( int playerIndex ); - -#define UTIL_EntitiesInPVS(pent) (*g_engfuncs.pfnEntitiesInPVS)(pent) -extern void UTIL_MakeVectors (const Vector &vecAngles); - -// Pass in an array of pointers and an array size, it fills the array and returns the number inserted -extern int UTIL_MonstersInSphere( CBaseEntity **pList, int listMax, const Vector ¢er, float radius ); -extern int UTIL_EntitiesInBox( CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ); - -inline void UTIL_MakeVectorsPrivate( const Vector &vecAngles, float *p_vForward, float *p_vRight, float *p_vUp ) -{ - g_engfuncs.pfnAngleVectors( vecAngles, p_vForward, p_vRight, p_vUp ); -} - -extern void UTIL_MakeAimVectors ( const Vector &vecAngles ); // like MakeVectors, but assumes pitch isn't inverted -extern void UTIL_MakeInvVectors ( const Vector &vec, globalvars_t *pgv ); - -extern void UTIL_Error ( CBaseEntity *pEntity, const char *pMessage ); -extern void UTIL_SetOrigin ( entvars_t* pev, const Vector &vecOrigin ); -extern void UTIL_EmitAmbientSound ( edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch ); -extern void UTIL_ParticleEffect ( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ); -extern void UTIL_ScreenShake ( const Vector ¢er, float amplitude, float frequency, float duration, float radius ); -extern void UTIL_ScreenShakeAll ( const Vector ¢er, float amplitude, float frequency, float duration ); -extern void UTIL_ShowMessage ( const char *pString, CBaseEntity *pPlayer); -typedef enum { NORMAL = 0, TOOLTIP = 1, CENTER = 2} SHOWMESSAGE_TYPE; -extern void UTIL_ShowMessage2 ( const char *pString, CBaseEntity *pPlayer, SHOWMESSAGE_TYPE type = NORMAL); -extern void UTIL_ShowMessageAll ( const char *pString ); -extern void UTIL_ScreenFadeAll ( const Vector &color, float fadeTime, float holdTime, int alpha, int flags ); -extern void UTIL_ScreenFade ( CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags ); - -typedef enum { ignore_monsters=1, dont_ignore_monsters=0, missile=2 } IGNORE_MONSTERS; -typedef enum { ignore_glass=1, dont_ignore_glass=0 } IGNORE_GLASS; -extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr); -extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr); -typedef enum { point_hull=0, human_hull=1, large_hull=2, head_hull=3 }; -extern void UTIL_TraceHull (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr); -extern TraceResult UTIL_GetGlobalTrace (void); -extern void UTIL_TraceModel (const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr); -extern Vector UTIL_GetAimVector (edict_t* pent, float flSpeed); -extern int UTIL_PointContents (const Vector &vec); - -extern int UTIL_IsMasterTriggered (string_t sMaster, CBaseEntity *pActivator); -extern void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount ); -extern void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, int amount ); -extern Vector UTIL_RandomBloodVector( void ); -extern BOOL UTIL_ShouldShowBlood( int bloodColor ); -extern void UTIL_BloodDecalTrace( TraceResult *pTrace, int bloodColor ); -extern void UTIL_DecalTrace( TraceResult *pTrace, int decalNumber ); -extern void UTIL_PlayerDecalTrace( TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom ); -extern void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ); -extern void UTIL_Sparks( const Vector &position ); -extern void UTIL_Ricochet( const Vector &position, float scale ); -extern void UTIL_StringToVector( float *pVector, const char *pString ); -extern void UTIL_StringToIntArray( int *pVector, int count, const char *pString ); -extern Vector UTIL_ClampVectorToBox( const Vector &input, const Vector &clampSize ); -extern float UTIL_Approach( float target, float value, float speed ); -extern float UTIL_ApproachAngle( float target, float value, float speed ); -extern float UTIL_AngleDistance( float next, float cur ); - -extern char *UTIL_VarArgs( char *format, ... ); -extern void UTIL_Remove( CBaseEntity *pEntity ); -extern BOOL UTIL_IsValidEntity( edict_t *pent ); -extern BOOL UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 ); - -// Use for ease-in, ease-out style interpolation (accel/decel) -extern float UTIL_SplineFraction( float value, float scale ); - -// Search for water transition along a vertical line -extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); -extern void UTIL_Bubbles( Vector mins, Vector maxs, int count ); -extern void UTIL_BubbleTrail( Vector from, Vector to, int count ); - -// allows precacheing of other entities -extern void UTIL_PrecacheOther( const char *szClassname ); - -// prints a message to each client -extern void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); -inline void UTIL_CenterPrintAll( const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ) -{ - UTIL_ClientPrintAll( HUD_PRINTCENTER, msg_name, param1, param2, param3, param4 ); -} - -class CBasePlayerItem; -class CBasePlayer; -extern BOOL UTIL_GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon ); - -// prints messages through the HUD -extern void ClientPrint( entvars_t *client, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); - -typedef struct hudtextparms_s -{ - float x; - float y; - int effect; - byte r1, g1, b1, a1; - byte r2, g2, b2, a2; - float fadeinTime; - float fadeoutTime; - float holdTime; - float fxTime; - int channel; -} hudtextparms_t; - -// prints as transparent 'title' to the HUD -extern void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ); -extern void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage ); - -// for handy use with ClientPrint params -extern char *UTIL_dtos1( int d ); -extern char *UTIL_dtos2( int d ); -extern char *UTIL_dtos3( int d ); -extern char *UTIL_dtos4( int d ); - -// Writes message to console with timestamp and FragLog header. -extern void UTIL_LogPrintf( char *fmt, ... ); - -// Sorta like FInViewCone, but for nonmonsters. -extern float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ); - -extern void UTIL_StripToken( const char *pKey, char *pDest );// for redundant keynames - -// Misc functions -extern void SetMovedir(entvars_t* pev); -extern Vector VecBModelOrigin( entvars_t* pevBModel ); -extern int BuildChangeList( LEVELLIST *pLevelList, int maxList ); - -// -// How did I ever live without ASSERT? -// -#include "localassert.h" -//#ifdef DEBUG -//void DBG_AssertFunction(BOOL fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage); -//#define ASSERT(f) DBG_AssertFunction(f, #f, __FILE__, __LINE__, NULL) -//#define ASSERTSZ(f, sz) DBG_AssertFunction(f, #f, __FILE__, __LINE__, sz) -//#else // !DEBUG -//#define ASSERT(f) -//#define ASSERTSZ(f, sz) -//#endif // !DEBUG - -extern DLL_GLOBAL const Vector g_vecZero; - -// -// Constants that were used only by QC (maybe not used at all now) -// -// Un-comment only as needed -// -#define LANGUAGE_ENGLISH 0 -#define LANGUAGE_GERMAN 1 -#define LANGUAGE_FRENCH 2 -#define LANGUAGE_BRITISH 3 - -extern DLL_GLOBAL int g_Language; - -#define AMBIENT_SOUND_STATIC 0 // medium radius attenuation -#define AMBIENT_SOUND_EVERYWHERE 1 -#define AMBIENT_SOUND_SMALLRADIUS 2 -#define AMBIENT_SOUND_MEDIUMRADIUS 4 -#define AMBIENT_SOUND_LARGERADIUS 8 -#define AMBIENT_SOUND_START_SILENT 16 -#define AMBIENT_SOUND_NOT_LOOPING 32 - -#define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements - -#define SND_SPAWNING (1<<8) // duplicated in protocol.h we're spawing, used in some cases for ambients -#define SND_STOP (1<<5) // duplicated in protocol.h stop sound -#define SND_CHANGE_VOL (1<<6) // duplicated in protocol.h change sound vol -#define SND_CHANGE_PITCH (1<<7) // duplicated in protocol.h change sound pitch - -#define LFO_SQUARE 1 -#define LFO_TRIANGLE 2 -#define LFO_RANDOM 3 - -// func_rotating -#define SF_BRUSH_ROTATE_Y_AXIS 0 -#define SF_BRUSH_ROTATE_INSTANT 1 -#define SF_BRUSH_ROTATE_BACKWARDS 2 -#define SF_BRUSH_ROTATE_Z_AXIS 4 -#define SF_BRUSH_ROTATE_X_AXIS 8 -#define SF_PENDULUM_AUTO_RETURN 16 -#define SF_PENDULUM_PASSABLE 32 - - -#define SF_BRUSH_ROTATE_SMALLRADIUS 128 -#define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 -#define SF_BRUSH_ROTATE_LARGERADIUS 512 - -#define PUSH_BLOCK_ONLY_X 1 -#define PUSH_BLOCK_ONLY_Y 2 - -#define VEC_HULL_MIN Vector(-16, -16, -36) -#define VEC_HULL_MAX Vector( 16, 16, 36) -#define VEC_HUMAN_HULL_MIN Vector( -16, -16, 0 ) -#define VEC_HUMAN_HULL_MAX Vector( 16, 16, 72 ) -#define VEC_HUMAN_HULL_DUCK Vector( 16, 16, 36 ) - -#define VEC_VIEW Vector( 0, 0, 28 ) - -#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18 ) -#define VEC_DUCK_HULL_MAX Vector( 16, 16, 18) -//#define VEC_DUCK_VIEW Vector( 0, 0, 12 ) - -#define SVC_TEMPENTITY 23 -#define SVC_INTERMISSION 30 -#define SVC_CDTRACK 32 -#define SVC_WEAPONANIM 35 -#define SVC_ROOMTYPE 37 - -// triggers -#define SF_TRIGGER_ALLOWMONSTERS 1// monsters allowed to fire this trigger -#define SF_TRIGGER_NOCLIENTS 2// players not allowed to fire this trigger -#define SF_TRIGGER_PUSHABLES 4// only pushables can fire this trigger - -// func breakable -#define SF_BREAK_TRIGGER_ONLY 1// may only be broken by trigger -#define SF_BREAK_TOUCH 2// can be 'crashed through' by running player (plate glass) -#define SF_BREAK_PRESSURE 4// can be broken by a player standing on it -#define SF_BREAK_CROWBAR 256// instant break if hit with crowbar - -// func_pushable (it's also func_breakable, so don't collide with those flags) -#define SF_PUSH_BREAKABLE 128 - -#define SF_LIGHT_START_OFF 1 - -#define SPAWNFLAG_NOMESSAGE 1 -#define SPAWNFLAG_NOTOUCH 1 -#define SPAWNFLAG_DROIDONLY 4 - -#define SPAWNFLAG_USEONLY 1 // can't be touched, must be used (buttons) - -#define TELE_PLAYER_ONLY 1 -#define TELE_SILENT 2 - -#define SF_TRIG_PUSH_ONCE 1 - - -// Sound Utilities - -// sentence groups -#define CBSENTENCENAME_MAX 16 -#define CVOXFILESENTENCEMAX 1536 // max number of sentences in game. NOTE: this must match - // CVOXFILESENTENCEMAX in engine\sound.h!!! - -extern char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; -extern int gcallsentences; - -int USENTENCEG_Pick(int isentenceg, char *szfound); -int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int freset); -void USENTENCEG_InitLRU(unsigned char *plru, int count); - -void SENTENCEG_Init(); -void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick); -int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, float volume, float attenuation, int flags, int pitch); -int SENTENCEG_PlayRndSz(edict_t *entity, const char *szrootname, float volume, float attenuation, int flags, int pitch); -int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szrootname, float volume, float attenuation, int flags, int pitch, int ipick, int freset); -int SENTENCEG_GetIndex(const char *szrootname); -int SENTENCEG_Lookup(const char *sample, char *sentencenum); - -void TEXTURETYPE_Init(); -char TEXTURETYPE_Find(char *name); -float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int iBulletType); - -// NOTE: use EMIT_SOUND_DYN to set the pitch of a sound. Pitch of 100 -// is no pitch shift. Pitch > 100 up to 255 is a higher pitch, pitch < 100 -// down to 1 is a lower pitch. 150 to 70 is the realistic range. -// EMIT_SOUND_DYN with pitch != 100 should be used sparingly, as it's not quite as -// fast as EMIT_SOUND (the pitchshift mixer is not native coded). - -void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, - int flags, int pitch); - - -inline void EMIT_SOUND(edict_t *entity, int channel, const char *sample, float volume, float attenuation) -{ - EMIT_SOUND_DYN(entity, channel, sample, volume, attenuation, 0, PITCH_NORM); -} - -inline void STOP_SOUND(edict_t *entity, int channel, const char *sample) -{ - EMIT_SOUND_DYN(entity, channel, sample, 0, 0, SND_STOP, PITCH_NORM); -} - -void EMIT_SOUND_SUIT(edict_t *entity, const char *sample); -void EMIT_GROUPID_SUIT(edict_t *entity, int isentenceg); -void EMIT_GROUPNAME_SUIT(edict_t *entity, const char *groupname); - -#define PRECACHE_SOUND_ARRAY( a ) \ - { for (int i = 0; i < ARRAYSIZE( a ); i++ ) PRECACHE_SOUND((char *) a [i]); } - -#define EMIT_SOUND_ARRAY_DYN( chan, array ) \ - EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, ATTN_NORM, 0, RANDOM_LONG(95,105) ); - -#define RANDOM_SOUND_ARRAY( array ) (array) [ RANDOM_LONG(0,ARRAYSIZE( (array) )-1) ] - -#define PLAYBACK_EVENT( flags, who, index ) PLAYBACK_EVENT_FULL( flags, who, index, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); -#define PLAYBACK_EVENT_DELAY( flags, who, index, delay ) PLAYBACK_EVENT_FULL( flags, who, index, delay, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); - -#define GROUP_OP_AND 0 -#define GROUP_OP_NAND 1 - -extern int g_groupmask; -extern int g_groupop; - -class UTIL_GroupTrace -{ -public: - UTIL_GroupTrace( int groupmask, int op ); - ~UTIL_GroupTrace( void ); - -private: - int m_oldgroupmask, m_oldgroupop; -}; - -void UTIL_SetGroupTrace( int groupmask, int op ); -void UTIL_UnsetGroupTrace( void ); - -int UTIL_SharedRandomLong( unsigned int seed, int low, int high ); -float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ); - -float UTIL_WeaponTimeBase( void ); - -// Leave last parameter to make the same as pEntity -void UTIL_SayText( const char *pText, CBaseEntity *pEntity, int inEntIndex = -1); -void UTIL_SayTextAll( const char *pText, CBaseEntity *pEntity, int inEntIndex = -1); - -//voogru: hope you dont mind me adding this, I use this in NSAdmin -// Statements like: -// #pragma message(Reminder "Fix this problem!") -// Which will cause messages like: -// C:\Source\Project\main.cpp(47): Reminder: Fix this problem! -// to show up during compiles. Note that you can NOT use the -// words "error" or "warning" in your reminders, since it will -// make the IDE think it should abort execution. You can double -// click on these messages and jump to the line in question. -#define Stringize( L ) #L -#define MakeString( M, L ) M(L) -#define $Line \ - MakeString( Stringize, __LINE__ ) -#define Reminder \ - __FILE__ "(" $Line ") : Reminder: " - - +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ +// +// Misc utility code +// +#ifndef UTIL_H +#define UTIL_H + +#include + +#ifndef ACTIVITY_H +#include "activity.h" +#endif + +#ifndef ENGINECALLBACK_H +#include "enginecallback.h" +#endif + +#include "game_shared/directorconst.h" + +//inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ); // implementation later in this file + +extern globalvars_t *gpGlobals; + +// Use this instead of ALLOC_STRING on constant strings +#define STRING(offset) (const char *)(gpGlobals->pStringBase + (int)offset) +// #define MAKE_STRING(str) ((int)str - (int)STRING(0)) + +#pragma warning(push) +#pragma warning(disable: 311) +inline string_t MAKE_STRING(const char* offset) +{ + return ((string_t)offset - (string_t)STRING(0)); +} +#pragma warning(pop) + +inline edict_t *FIND_ENTITY_BY_CLASSNAME(edict_t *entStart, const char *pszName) +{ + return FIND_ENTITY_BY_STRING(entStart, "classname", pszName); +} + +inline edict_t *FIND_ENTITY_BY_TARGETNAME(edict_t *entStart, const char *pszName) +{ + return FIND_ENTITY_BY_STRING(entStart, "targetname", pszName); +} + +// for doing a reverse lookup. Say you have a door, and want to find its button. +inline edict_t *FIND_ENTITY_BY_TARGET(edict_t *entStart, const char *pszName) +{ + return FIND_ENTITY_BY_STRING(entStart, "target", pszName); +} + +// Keeps clutter down a bit, when writing key-value pairs +#define WRITEKEY_INT(pf, szKeyName, iKeyValue) ENGINE_FPRINTF(pf, "\"%s\" \"%d\"\n", szKeyName, iKeyValue) +#define WRITEKEY_FLOAT(pf, szKeyName, flKeyValue) \ + ENGINE_FPRINTF(pf, "\"%s\" \"%f\"\n", szKeyName, flKeyValue) +#define WRITEKEY_STRING(pf, szKeyName, szKeyValue) \ + ENGINE_FPRINTF(pf, "\"%s\" \"%s\"\n", szKeyName, szKeyValue) +#define WRITEKEY_VECTOR(pf, szKeyName, flX, flY, flZ) \ + ENGINE_FPRINTF(pf, "\"%s\" \"%f %f %f\"\n", szKeyName, flX, flY, flZ) + +// Keeps clutter down a bit, when using a float as a bit-vector +#define SetBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) | (bits)) +#define ClearBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) & ~(bits)) +#define FBitSet(flBitVector, bit) ((int)(flBitVector) & (bit)) + +// Makes these more explicit, and easier to find +#define FILE_GLOBAL static +#define DLL_GLOBAL + +// Until we figure out why "const" gives the compiler problems, we'll just have to use +// this bogus "empty" define to mark things as constant. +#define CONSTANT + +// More explicit than "int" +typedef int EOFFSET; + +// In case it's not alread defined +typedef int BOOL; + +// In case this ever changes +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +// Keeps clutter down a bit, when declaring external entity/global method prototypes +#define DECLARE_GLOBAL_METHOD(MethodName) extern void DLLEXPORT MethodName( void ) +#define GLOBAL_METHOD(funcname) void DLLEXPORT funcname(void) + +// This is the glue that hooks .MAP entity class names to our CPP classes +// The _declspec forces them to be exported by name so we can do a lookup with GetProcAddress() +// The function is used to intialize / allocate the object for the entity +#ifdef _WIN32 +#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) \ + extern "C" _declspec( dllexport ) void mapClassName( entvars_t *pev ); \ + void mapClassName( entvars_t *pev ) { GetClassPtr( (DLLClassName *)pev ); } +#else +#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) extern "C" void mapClassName( entvars_t *pev ); void mapClassName( entvars_t *pev ) { GetClassPtr( (DLLClassName *)pev ); } +#endif + + +// +// Conversion among the three types of "entity", including identity-conversions. +// +#ifdef DEBUG + extern edict_t *DBG_EntOfVars(const entvars_t *pev); + inline edict_t *ENT(const entvars_t *pev) { return DBG_EntOfVars(pev); } +#else + inline edict_t *ENT(const entvars_t *pev) { return pev->pContainingEntity; } +#endif +inline edict_t *ENT(edict_t *pent) { return pent; } +inline edict_t *ENT(EOFFSET eoffset) { return (*g_engfuncs.pfnPEntityOfEntOffset)(eoffset); } +inline EOFFSET OFFSET(EOFFSET eoffset) { return eoffset; } +inline EOFFSET OFFSET(const edict_t *pent) +{ +#if _DEBUG + if ( !pent ) + ALERT( at_error, "Bad ent in OFFSET()\n" ); +#endif + return (*g_engfuncs.pfnEntOffsetOfPEntity)(pent); +} +inline EOFFSET OFFSET(entvars_t *pev) +{ +#if _DEBUG + if ( !pev ) + ALERT( at_error, "Bad pev in OFFSET()\n" ); +#endif + return OFFSET(ENT(pev)); +} +inline entvars_t *VARS(entvars_t *pev) { return pev; } + +inline entvars_t *VARS(edict_t *pent) +{ + if ( !pent ) + return NULL; + + return &pent->v; +} + +inline entvars_t* VARS(EOFFSET eoffset) { return VARS(ENT(eoffset)); } +inline int ENTINDEX(const edict_t *pEdict) { return (*g_engfuncs.pfnIndexOfEdict)(pEdict); } +inline edict_t* INDEXENT( int iEdictNum ) { return (*g_engfuncs.pfnPEntityOfEntIndex)(iEdictNum); } + +extern void NetworkMeterMessageBegin(int msg_dest, int msg_type, const float* pOrigin, edict_t* ed); + +inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ) +{ + NetworkMeterMessageBegin(msg_dest, msg_type, pOrigin, ENT(ent)); +} +inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin = NULL, edict_t* ed = NULL) +{ + NetworkMeterMessageBegin(msg_dest, msg_type, pOrigin, ed); +} + +// Testing the three types of "entity" for nullity +#define eoNullEntity 0 +inline BOOL FNullEnt(EOFFSET eoffset) { return eoffset == 0; } +inline BOOL FNullEnt(const edict_t* pent) { return pent == NULL || FNullEnt(OFFSET(pent)); } +inline BOOL FNullEnt(entvars_t* pev) { return pev == NULL || FNullEnt(OFFSET(pev)); } + +// Testing strings for nullity +#define iStringNull 0 +inline BOOL FStringNull(int iString) { return iString == iStringNull; } + +#define cchMapNameMost 32 + +// Dot products for view cone checking +#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees +#define VIEW_FIELD_WIDE (float)-0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks +#define VIEW_FIELD_NARROW (float)0.7 // +-45 degrees, more narrow check used to set up ranged attacks +#define VIEW_FIELD_ULTRA_NARROW (float)0.9 // +-25 degrees, more narrow check used to set up ranged attacks + +// All monsters need this data +#define DONT_BLEED -1 +#define BLOOD_COLOR_RED (BYTE)247 +#define BLOOD_COLOR_YELLOW (BYTE)195 +#define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW + +typedef enum +{ + + MONSTERSTATE_NONE = 0, + MONSTERSTATE_IDLE, + MONSTERSTATE_COMBAT, + MONSTERSTATE_ALERT, + MONSTERSTATE_HUNT, + MONSTERSTATE_PRONE, + MONSTERSTATE_SCRIPT, + MONSTERSTATE_PLAYDEAD, + MONSTERSTATE_DEAD + +} MONSTERSTATE; + + + +// Things that toggle (buttons/triggers/doors) need this +typedef enum + { + TS_AT_TOP, + TS_AT_BOTTOM, + TS_GOING_UP, + TS_GOING_DOWN + } TOGGLE_STATE; + +// Misc useful +inline BOOL FStrEq(const char*sz1, const char*sz2) + { return (strcmp(sz1, sz2) == 0); } +inline BOOL FClassnameIs(edict_t* pent, const char* szClassname) + { return FStrEq(STRING(VARS(pent)->classname), szClassname); } +inline BOOL FClassnameIs(entvars_t* pev, const char* szClassname) + { return FStrEq(STRING(pev->classname), szClassname); } + +class CBaseEntity; + +// Misc. Prototypes +extern void UTIL_SetSize (entvars_t* pev, const Vector &vecMin, const Vector &vecMax); +extern float UTIL_VecToYaw (const Vector &vec); +extern Vector UTIL_VecToAngles (const Vector &vec); +extern float UTIL_AngleMod (float a); +extern float UTIL_AngleDiff ( float destAngle, float srcAngle ); + +extern CBaseEntity *UTIL_FindEntityInSphere(CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius); +extern CBaseEntity *UTIL_FindEntityByString(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue ); +extern CBaseEntity *UTIL_FindEntityByClassname(CBaseEntity *pStartEntity, const char *szName ); +extern CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName ); +extern CBaseEntity *UTIL_FindEntityGeneric(const char *szName, Vector &vecSrc, float flRadius ); +int UTIL_CountEntitiesInSphere(const Vector& inVecCenter, float inRadius, const char* inClassName); + +// returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected +// otherwise returns NULL +// Index is 1 based +extern CBaseEntity *UTIL_PlayerByIndex( int playerIndex ); + +#define UTIL_EntitiesInPVS(pent) (*g_engfuncs.pfnEntitiesInPVS)(pent) +extern void UTIL_MakeVectors (const Vector &vecAngles); + +// Pass in an array of pointers and an array size, it fills the array and returns the number inserted +extern int UTIL_MonstersInSphere( CBaseEntity **pList, int listMax, const Vector ¢er, float radius ); +extern int UTIL_EntitiesInBox( CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ); + +inline void UTIL_MakeVectorsPrivate( const Vector &vecAngles, float *p_vForward, float *p_vRight, float *p_vUp ) +{ + g_engfuncs.pfnAngleVectors( vecAngles, p_vForward, p_vRight, p_vUp ); +} + +extern void UTIL_MakeAimVectors ( const Vector &vecAngles ); // like MakeVectors, but assumes pitch isn't inverted +extern void UTIL_MakeInvVectors ( const Vector &vec, globalvars_t *pgv ); + +extern void UTIL_Error ( CBaseEntity *pEntity, const char *pMessage ); +extern void UTIL_SetOrigin ( entvars_t* pev, const Vector &vecOrigin ); +extern void UTIL_EmitAmbientSound ( edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch ); +extern void UTIL_ParticleEffect ( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ); +extern void UTIL_ScreenShake ( const Vector ¢er, float amplitude, float frequency, float duration, float radius ); +extern void UTIL_ScreenShakeAll ( const Vector ¢er, float amplitude, float frequency, float duration ); +extern void UTIL_ShowMessage ( const char *pString, CBaseEntity *pPlayer); +typedef enum { NORMAL = 0, TOOLTIP = 1, CENTER = 2} SHOWMESSAGE_TYPE; +extern void UTIL_ShowMessage2 ( const char *pString, CBaseEntity *pPlayer, SHOWMESSAGE_TYPE type = NORMAL); +extern void UTIL_ShowMessageAll ( const char *pString ); +extern void UTIL_ScreenFadeAll ( const Vector &color, float fadeTime, float holdTime, int alpha, int flags ); +extern void UTIL_ScreenFade ( CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags ); + +typedef enum { ignore_monsters=1, dont_ignore_monsters=0, missile=2 } IGNORE_MONSTERS; +typedef enum { ignore_glass=1, dont_ignore_glass=0 } IGNORE_GLASS; +extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr); +extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr); +typedef enum { point_hull=0, human_hull=1, large_hull=2, head_hull=3 }; +extern void UTIL_TraceHull (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr); +extern TraceResult UTIL_GetGlobalTrace (void); +extern void UTIL_TraceModel (const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr); +extern Vector UTIL_GetAimVector (edict_t* pent, float flSpeed); +extern int UTIL_PointContents (const Vector &vec); + +extern int UTIL_IsMasterTriggered (string_t sMaster, CBaseEntity *pActivator); +extern void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount ); +extern void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, int amount ); +extern Vector UTIL_RandomBloodVector( void ); +extern BOOL UTIL_ShouldShowBlood( int bloodColor ); +extern void UTIL_BloodDecalTrace( TraceResult *pTrace, int bloodColor ); +extern void UTIL_DecalTrace( TraceResult *pTrace, int decalNumber ); +extern void UTIL_PlayerDecalTrace( TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom ); +extern void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ); +extern void UTIL_Sparks( const Vector &position ); +extern void UTIL_Ricochet( const Vector &position, float scale ); +extern void UTIL_StringToVector( float *pVector, const char *pString ); +extern void UTIL_StringToIntArray( int *pVector, int count, const char *pString ); +extern Vector UTIL_ClampVectorToBox( const Vector &input, const Vector &clampSize ); +extern float UTIL_Approach( float target, float value, float speed ); +extern float UTIL_ApproachAngle( float target, float value, float speed ); +extern float UTIL_AngleDistance( float next, float cur ); + +extern char *UTIL_VarArgs( char *format, ... ); +extern void UTIL_Remove( CBaseEntity *pEntity ); +extern BOOL UTIL_IsValidEntity( edict_t *pent ); +extern BOOL UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 ); + +// Use for ease-in, ease-out style interpolation (accel/decel) +extern float UTIL_SplineFraction( float value, float scale ); + +// Search for water transition along a vertical line +extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); +extern void UTIL_Bubbles( Vector mins, Vector maxs, int count ); +extern void UTIL_BubbleTrail( Vector from, Vector to, int count ); + +// allows precacheing of other entities +extern void UTIL_PrecacheOther( const char *szClassname ); + +// prints a message to each client +extern void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); +inline void UTIL_CenterPrintAll( const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ) +{ + UTIL_ClientPrintAll( HUD_PRINTCENTER, msg_name, param1, param2, param3, param4 ); +} + +class CBasePlayerItem; +class CBasePlayer; +extern BOOL UTIL_GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon ); + +// prints messages through the HUD +extern void ClientPrint( entvars_t *client, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); + +typedef struct hudtextparms_s +{ + float x; + float y; + int effect; + byte r1, g1, b1, a1; + byte r2, g2, b2, a2; + float fadeinTime; + float fadeoutTime; + float holdTime; + float fxTime; + int channel; +} hudtextparms_t; + +// prints as transparent 'title' to the HUD +extern void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ); +extern void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage ); + +// for handy use with ClientPrint params +extern char *UTIL_dtos1( int d ); +extern char *UTIL_dtos2( int d ); +extern char *UTIL_dtos3( int d ); +extern char *UTIL_dtos4( int d ); + +// Writes message to console with timestamp and FragLog header. +extern void UTIL_LogPrintf( char *fmt, ... ); + +// Sorta like FInViewCone, but for nonmonsters. +extern float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ); + +extern void UTIL_StripToken( const char *pKey, char *pDest );// for redundant keynames + +// Misc functions +extern void SetMovedir(entvars_t* pev); +extern Vector VecBModelOrigin( entvars_t* pevBModel ); +extern int BuildChangeList( LEVELLIST *pLevelList, int maxList ); + +// +// How did I ever live without ASSERT? +// +#include "localassert.h" +//#ifdef DEBUG +//void DBG_AssertFunction(BOOL fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage); +//#define ASSERT(f) DBG_AssertFunction(f, #f, __FILE__, __LINE__, NULL) +//#define ASSERTSZ(f, sz) DBG_AssertFunction(f, #f, __FILE__, __LINE__, sz) +//#else // !DEBUG +//#define ASSERT(f) +//#define ASSERTSZ(f, sz) +//#endif // !DEBUG + +extern DLL_GLOBAL const Vector g_vecZero; + +// +// Constants that were used only by QC (maybe not used at all now) +// +// Un-comment only as needed +// +#define LANGUAGE_ENGLISH 0 +#define LANGUAGE_GERMAN 1 +#define LANGUAGE_FRENCH 2 +#define LANGUAGE_BRITISH 3 + +extern DLL_GLOBAL int g_Language; + +#define AMBIENT_SOUND_STATIC 0 // medium radius attenuation +#define AMBIENT_SOUND_EVERYWHERE 1 +#define AMBIENT_SOUND_SMALLRADIUS 2 +#define AMBIENT_SOUND_MEDIUMRADIUS 4 +#define AMBIENT_SOUND_LARGERADIUS 8 +#define AMBIENT_SOUND_START_SILENT 16 +#define AMBIENT_SOUND_NOT_LOOPING 32 + +#define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements + +#define SND_SPAWNING (1<<8) // duplicated in protocol.h we're spawing, used in some cases for ambients +#define SND_STOP (1<<5) // duplicated in protocol.h stop sound +#define SND_CHANGE_VOL (1<<6) // duplicated in protocol.h change sound vol +#define SND_CHANGE_PITCH (1<<7) // duplicated in protocol.h change sound pitch + +#define LFO_SQUARE 1 +#define LFO_TRIANGLE 2 +#define LFO_RANDOM 3 + +// func_rotating +#define SF_BRUSH_ROTATE_Y_AXIS 0 +#define SF_BRUSH_ROTATE_INSTANT 1 +#define SF_BRUSH_ROTATE_BACKWARDS 2 +#define SF_BRUSH_ROTATE_Z_AXIS 4 +#define SF_BRUSH_ROTATE_X_AXIS 8 +#define SF_PENDULUM_AUTO_RETURN 16 +#define SF_PENDULUM_PASSABLE 32 + + +#define SF_BRUSH_ROTATE_SMALLRADIUS 128 +#define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 +#define SF_BRUSH_ROTATE_LARGERADIUS 512 + +#define PUSH_BLOCK_ONLY_X 1 +#define PUSH_BLOCK_ONLY_Y 2 + +#define VEC_HULL_MIN Vector(-16, -16, -36) +#define VEC_HULL_MAX Vector( 16, 16, 36) +#define VEC_HUMAN_HULL_MIN Vector( -16, -16, 0 ) +#define VEC_HUMAN_HULL_MAX Vector( 16, 16, 72 ) +#define VEC_HUMAN_HULL_DUCK Vector( 16, 16, 36 ) + +#define VEC_VIEW Vector( 0, 0, 28 ) + +#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18 ) +#define VEC_DUCK_HULL_MAX Vector( 16, 16, 18) +//#define VEC_DUCK_VIEW Vector( 0, 0, 12 ) + +#define SVC_TEMPENTITY 23 +#define SVC_INTERMISSION 30 +#define SVC_CDTRACK 32 +#define SVC_WEAPONANIM 35 +#define SVC_ROOMTYPE 37 + +// triggers +#define SF_TRIGGER_ALLOWMONSTERS 1// monsters allowed to fire this trigger +#define SF_TRIGGER_NOCLIENTS 2// players not allowed to fire this trigger +#define SF_TRIGGER_PUSHABLES 4// only pushables can fire this trigger + +// func breakable +#define SF_BREAK_TRIGGER_ONLY 1// may only be broken by trigger +#define SF_BREAK_TOUCH 2// can be 'crashed through' by running player (plate glass) +#define SF_BREAK_PRESSURE 4// can be broken by a player standing on it +#define SF_BREAK_CROWBAR 256// instant break if hit with crowbar + +// func_pushable (it's also func_breakable, so don't collide with those flags) +#define SF_PUSH_BREAKABLE 128 + +#define SF_LIGHT_START_OFF 1 + +#define SPAWNFLAG_NOMESSAGE 1 +#define SPAWNFLAG_NOTOUCH 1 +#define SPAWNFLAG_DROIDONLY 4 + +#define SPAWNFLAG_USEONLY 1 // can't be touched, must be used (buttons) + +#define TELE_PLAYER_ONLY 1 +#define TELE_SILENT 2 + +#define SF_TRIG_PUSH_ONCE 1 + + +// Sound Utilities + +// sentence groups +#define CBSENTENCENAME_MAX 16 +#define CVOXFILESENTENCEMAX 1536 // max number of sentences in game. NOTE: this must match + // CVOXFILESENTENCEMAX in engine\sound.h!!! + +extern char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; +extern int gcallsentences; + +int USENTENCEG_Pick(int isentenceg, char *szfound); +int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int freset); +void USENTENCEG_InitLRU(unsigned char *plru, int count); + +void SENTENCEG_Init(); +void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick); +int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, float volume, float attenuation, int flags, int pitch); +int SENTENCEG_PlayRndSz(edict_t *entity, const char *szrootname, float volume, float attenuation, int flags, int pitch); +int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szrootname, float volume, float attenuation, int flags, int pitch, int ipick, int freset); +int SENTENCEG_GetIndex(const char *szrootname); +int SENTENCEG_Lookup(const char *sample, char *sentencenum); + +void TEXTURETYPE_Init(); +char TEXTURETYPE_Find(char *name); +float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int iBulletType); + +// NOTE: use EMIT_SOUND_DYN to set the pitch of a sound. Pitch of 100 +// is no pitch shift. Pitch > 100 up to 255 is a higher pitch, pitch < 100 +// down to 1 is a lower pitch. 150 to 70 is the realistic range. +// EMIT_SOUND_DYN with pitch != 100 should be used sparingly, as it's not quite as +// fast as EMIT_SOUND (the pitchshift mixer is not native coded). + +void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, + int flags, int pitch); + + +inline void EMIT_SOUND(edict_t *entity, int channel, const char *sample, float volume, float attenuation) +{ + EMIT_SOUND_DYN(entity, channel, sample, volume, attenuation, 0, PITCH_NORM); +} + +inline void STOP_SOUND(edict_t *entity, int channel, const char *sample) +{ + EMIT_SOUND_DYN(entity, channel, sample, 0, 0, SND_STOP, PITCH_NORM); +} + +void EMIT_SOUND_SUIT(edict_t *entity, const char *sample); +void EMIT_GROUPID_SUIT(edict_t *entity, int isentenceg); +void EMIT_GROUPNAME_SUIT(edict_t *entity, const char *groupname); + +#define PRECACHE_SOUND_ARRAY( a ) \ + { for (int i = 0; i < ARRAYSIZE( a ); i++ ) PRECACHE_SOUND((char *) a [i]); } + +#define EMIT_SOUND_ARRAY_DYN( chan, array ) \ + EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, ATTN_NORM, 0, RANDOM_LONG(95,105) ); + +#define RANDOM_SOUND_ARRAY( array ) (array) [ RANDOM_LONG(0,ARRAYSIZE( (array) )-1) ] + +#define PLAYBACK_EVENT( flags, who, index ) PLAYBACK_EVENT_FULL( flags, who, index, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); +#define PLAYBACK_EVENT_DELAY( flags, who, index, delay ) PLAYBACK_EVENT_FULL( flags, who, index, delay, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); + +#define GROUP_OP_AND 0 +#define GROUP_OP_NAND 1 + +extern int g_groupmask; +extern int g_groupop; + +class UTIL_GroupTrace +{ +public: + UTIL_GroupTrace( int groupmask, int op ); + ~UTIL_GroupTrace( void ); + +private: + int m_oldgroupmask, m_oldgroupop; +}; + +void UTIL_SetGroupTrace( int groupmask, int op ); +void UTIL_UnsetGroupTrace( void ); + +int UTIL_SharedRandomLong( unsigned int seed, int low, int high ); +float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ); + +float UTIL_WeaponTimeBase( void ); + +// Leave last parameter to make the same as pEntity +void UTIL_SayText( const char *pText, CBaseEntity *pEntity, int inEntIndex = -1); +void UTIL_SayTextAll( const char *pText, CBaseEntity *pEntity, int inEntIndex = -1); + +//: hope you dont mind me adding this, I use this in NSAdmin +// Statements like: +// #pragma message(Reminder "Fix this problem!") +// Which will cause messages like: +// C:\Source\Project\main.cpp(47): Reminder: Fix this problem! +// to show up during compiles. Note that you can NOT use the +// words "error" or "warning" in your reminders, since it will +// make the IDE think it should abort execution. You can double +// click on these messages and jump to the line in question. +#define Stringize( L ) #L +#define MakeString( M, L ) M(L) +#define $Line \ + MakeString( Stringize, __LINE__ ) +#define Reminder \ + __FILE__ "(" $Line ") : Reminder: " + + #endif \ No newline at end of file diff --git a/main/source/dlls/weapons.cpp b/main/source/dlls/weapons.cpp index 5774bac..b4bd7d0 100644 --- a/main/source/dlls/weapons.cpp +++ b/main/source/dlls/weapons.cpp @@ -1,1918 +1,1926 @@ -// -// Copyright (c) 1999, Valve LLC. All rights reserved. -// -// This product contains software technology licensed from Id -// Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -// All Rights Reserved. -// -// Use, distribution, and modification of this source code and/or resulting -// object code is restricted to non-commercial enhancements to products from -// Valve LLC. All other use, distribution, or modification is prohibited -// without written permission from Valve LLC. -// -// ===== weapons.cpp ======================================================== -// -// functions governing the selection/use of weapons for players -// -// $Workfile: weapons.cpp $ -// $Date: 2002/11/22 21:13:52 $ -// -//------------------------------------------------------------------------------- -// $Log: weapons.cpp,v $ -// Revision 1.49 2002/11/22 21:13:52 Flayra -// - mp_consistency changes -// -// Revision 1.48 2002/11/12 02:19:43 Flayra -// - Fixed problem where friendly bulletfire was doing damage -// -// Revision 1.47 2002/10/24 21:19:11 Flayra -// - Reworked jetpack effects -// - Added a few extra sounds -// -// Revision 1.46 2002/10/16 00:41:15 Flayra -// - Removed unneeds sounds and events -// - Added distress beacon event -// - Added general purpose particle event -// -// Revision 1.45 2002/09/25 20:40:09 Flayra -// - Play different sound for aliens when they get weapons -// -// Revision 1.44 2002/09/23 22:04:00 Flayra -// - Regular update -// -// Revision 1.43 2002/08/31 18:01:18 Flayra -// - Work at VALVe -// -// Revision 1.42 2002/07/26 23:01:05 Flayra -// - Precache numerical feedback event -// -// Revision 1.41 2002/07/23 16:48:37 Flayra -// - Added distress beacon -// -// Revision 1.40 2002/07/08 16:35:17 Flayra -// - Added document header, updates for cheat protection, added constants -// -//=============================================================================== -#include "util/nowarnings.h" -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "player.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "soundent.h" -#include "decals.h" -#include "gamerules.h" -#include "mod/AvHConstants.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHNetworkMessages.h" - -extern CGraph WorldGraph; -extern int gEvilImpulse101; - -int gJetpackEventID; -int gStartOverwatchEventID; -int gEndOverwatchEventID; -int gTeleportEventID; -int gBlinkEffectSuccessEventID; -int gPhaseInEventID; -int gSiegeHitEventID; -int gSiegeViewHitEventID; -int gCommanderPointsAwardedEventID; -int gAlienSightOnEventID; -int gAlienSightOffEventID; -//int gParalysisStartEventID; -int gRegenerationEventID; -int gStartCloakEventID; -int gEndCloakEventID; -int gSporeCloudEventID; -int gUmbraCloudEventID; -int gStopScreamEventID; - -int gWelderEventID; -int gWelderConstEventID; -//int gWallJumpEventID; -//int gFlightEventID; -int gEmptySoundEventID; -int gNumericalInfoEventID; -int gInvalidActionEventID; -int gParticleEventID; -int gDistressBeaconEventID; -int gWeaponAnimationEventID; -int gLevelUpEventID; -int gMetabolizeSuccessEventID; - -#define NOT_USED 255 - -DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam -DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr"; -DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot -DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball -DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud -DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion -DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model -DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood -DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood - -ItemInfo CBasePlayerItem::ItemInfoArray[MAX_WEAPONS]; -AmmoInfo CBasePlayerItem::AmmoInfoArray[MAX_AMMO_SLOTS]; - -MULTIDAMAGE gMultiDamage; - -#define TRACER_FREQ 4 // Tracers fire every fourth bullet - - -//========================================================= -// MaxAmmoCarry - pass in a name and this function will tell -// you the maximum amount of that type of ammunition that a -// player can carry. -//========================================================= -int MaxAmmoCarry( int iszName ) -{ - for ( int i = 0; i < MAX_WEAPONS; i++ ) - { - if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo1 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo1 ) ) - return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo1; - if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo2 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo2 ) ) - return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo2; - } - - ALERT( at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) ); - return -1; -} - - -/* -============================================================================== - -MULTI-DAMAGE - -Collects multiple small damages into a single damage - -============================================================================== -*/ - -// -// ClearMultiDamage - resets the global multi damage accumulator -// -void ClearMultiDamage(void) -{ - gMultiDamage.pEntity = NULL; - gMultiDamage.amount = 0; - gMultiDamage.type = 0; -} - -// -// ApplyMultiDamage - inflicts contents of global multi damage register on gMultiDamage.pEntity -// -// GLOBALS USED: -// gMultiDamage - -void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker ) -{ - Vector vecSpot1;//where blood comes from - Vector vecDir;//direction blood should go - TraceResult tr; - - if ( !gMultiDamage.pEntity ) - return; - - float theDamage = gMultiDamage.amount; - gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, theDamage, gMultiDamage.type ); -} - - -// GLOBALS USED: -// gMultiDamage - -void AddMultiDamage( entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType) -{ - if ( !pEntity ) - return; - - gMultiDamage.type |= bitsDamageType; - - if ( pEntity != gMultiDamage.pEntity ) - { - ApplyMultiDamage(pevInflictor,pevInflictor); // UNDONE: wrong attacker! - gMultiDamage.pEntity = pEntity; - gMultiDamage.amount = 0; - } - - gMultiDamage.amount += flDamage; -} - -/* -================ -SpawnBlood -================ -*/ -void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage) -{ - if(flDamage >= 0.0f) - { - if(bloodColor == DONT_BLEED) - { - UTIL_Sparks(vecSpot); - } - else - { - UTIL_BloodDrips( vecSpot, g_vecAttackDir, bloodColor, (int)flDamage ); - } - } -} - - -int DamageDecal( CBaseEntity *pEntity, int bitsDamageType ) -{ - if ( !pEntity ) - return (DECAL_GUNSHOT1 + RANDOM_LONG(0,4)); - - return pEntity->DamageDecal( bitsDamageType ); -} - -void DecalGunshot( TraceResult *pTrace, int iBulletType ) -{ - // Is the entity valid - if ( !UTIL_IsValidEntity( pTrace->pHit ) ) - return; - - if ( VARS(pTrace->pHit)->solid == SOLID_BSP || VARS(pTrace->pHit)->movetype == MOVETYPE_PUSHSTEP ) - { - CBaseEntity *pEntity = NULL; - // Decal the wall with a gunshot - if ( !FNullEnt(pTrace->pHit) ) - pEntity = CBaseEntity::Instance(pTrace->pHit); - -// switch( iBulletType ) -// { -// case BULLET_PLAYER_9MM: -// case BULLET_MONSTER_9MM: -// case BULLET_PLAYER_MP5: -// case BULLET_MONSTER_MP5: -// case BULLET_PLAYER_BUCKSHOT: -// case BULLET_PLAYER_357: -// default: - // smoke and decal - UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); -// break; -// case BULLET_MONSTER_12MM: -// // smoke and decal -// UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); -// break; -// case BULLET_PLAYER_CROWBAR: -// // wall decal -// UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) ); -// break; -// } - } -} - - - -// -// EjectBrass - tosses a brass shell from passed origin at passed velocity -// -void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype ) -{ - // FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see. - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); - WRITE_BYTE( TE_MODEL); - WRITE_COORD( vecOrigin.x); - WRITE_COORD( vecOrigin.y); - WRITE_COORD( vecOrigin.z); - WRITE_COORD( vecVelocity.x); - WRITE_COORD( vecVelocity.y); - WRITE_COORD( vecVelocity.z); - WRITE_ANGLE( rotation ); - WRITE_SHORT( model ); - WRITE_BYTE ( soundtype); - WRITE_BYTE ( 25 );// 2.5 seconds - MESSAGE_END(); -} - - -#if 0 -// UNDONE: This is no longer used? -void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count ) -{ - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); - WRITE_BYTE ( TE_EXPLODEMODEL ); - WRITE_COORD( vecOrigin.x ); - WRITE_COORD( vecOrigin.y ); - WRITE_COORD( vecOrigin.z ); - WRITE_COORD( speed ); - WRITE_SHORT( model ); - WRITE_SHORT( count ); - WRITE_BYTE ( 15 );// 1.5 seconds - MESSAGE_END(); -} -#endif - - -int giAmmoIndex = 0; - -// Precaches the ammo and queues the ammo info for sending to clients -void AddAmmoNameToAmmoRegistry( const char *szAmmoname ) -{ - // make sure it's not already in the registry - for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) - { - if ( !CBasePlayerItem::AmmoInfoArray[i].pszName) - continue; - - if ( stricmp( CBasePlayerItem::AmmoInfoArray[i].pszName, szAmmoname ) == 0 ) - return; // ammo already in registry, just quite - } - - - giAmmoIndex++; - ASSERT( giAmmoIndex < MAX_AMMO_SLOTS ); - if ( giAmmoIndex >= MAX_AMMO_SLOTS ) - giAmmoIndex = 0; - - CBasePlayerItem::AmmoInfoArray[giAmmoIndex].pszName = szAmmoname; - CBasePlayerItem::AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; // yes, this info is redundant -} - - -// Precaches the weapon and queues the weapon info for sending to clients -void UTIL_PrecacheOtherWeapon( const char *szClassname ) -{ - edict_t *pent; - - pent = CREATE_NAMED_ENTITY( MAKE_STRING( szClassname ) ); - if ( FNullEnt( pent ) ) - { - ALERT ( at_console, "NULL Ent in UTIL_PrecacheOtherWeapon\n" ); - return; - } - - CBaseEntity *pEntity = CBaseEntity::Instance (VARS( pent )); - - if (pEntity) - { - ItemInfo II; - pEntity->Precache( ); - memset( &II, 0, sizeof II ); - if ( ((CBasePlayerItem*)pEntity)->GetItemInfo( &II ) ) - { - CBasePlayerItem::ItemInfoArray[II.iId] = II; - - if ( II.pszAmmo1 && *II.pszAmmo1 ) - { - AddAmmoNameToAmmoRegistry( II.pszAmmo1 ); - } - - if ( II.pszAmmo2 && *II.pszAmmo2 ) - { - AddAmmoNameToAmmoRegistry( II.pszAmmo2 ); - } - - memset( &II, 0, sizeof II ); - } - } - - REMOVE_ENTITY(pent); -} - -// called by worldspawn -void W_Precache(void) -{ - memset( CBasePlayerItem::ItemInfoArray, 0, sizeof(CBasePlayerItem::ItemInfoArray) ); - memset( CBasePlayerItem::AmmoInfoArray, 0, sizeof(CBasePlayerItem::AmmoInfoArray) ); - giAmmoIndex = 0; - - // Marine weapons - //UTIL_PrecacheOtherWeapon("weapon_9mmhandgun"); - //UTIL_PrecacheOtherWeapon("weapon_glock"); - //UTIL_PrecacheOther("ammo_9mmclip"); - - // Player assets - PRECACHE_UNMODIFIED_MODEL(kReadyRoomModel); - PRECACHE_UNMODIFIED_MODEL(kMarineSoldierModel); - PRECACHE_UNMODIFIED_MODEL(kHeavySoldierModel); - PRECACHE_UNMODIFIED_MODEL(kAlienLevelOneModel); - PRECACHE_UNMODIFIED_SOUND(kDistressBeaconSound); - PRECACHE_UNMODIFIED_SOUND(kLevelUpMarineSound); - PRECACHE_UNMODIFIED_SOUND(kLevelUpAlienSound); - PRECACHE_UNMODIFIED_SOUND(kAlienBuildingSound1); - PRECACHE_UNMODIFIED_SOUND(kAlienBuildingSound2); - PRECACHE_SOUND(kMyHiveEasterEgg); - - //PRECACHE_UNMODIFIED_SOUND(kAlienAbilitiesGrantedSound); - //PRECACHE_UNMODIFIED_SOUND(kAlienAbilitiesLostSound); - - PRECACHE_UNMODIFIED_MODEL(kAlienLevelTwoModel); - PRECACHE_UNMODIFIED_MODEL(kAlienLevelThreeModel); - PRECACHE_UNMODIFIED_MODEL(kAlienLevelFourModel); - PRECACHE_UNMODIFIED_MODEL(kAlienLevelFiveModel); - - PRECACHE_UNMODIFIED_MODEL(kMarineCommanderModel); - PRECACHE_UNMODIFIED_MODEL(kAlienGestateModel); - // puzl: 1072 - // Added some client side consistency checks. - PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash1.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash2.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash3.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/digesting.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/membrane.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/hera_fog.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/spore.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/spore2.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/umbra.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/umbra2.spr"); - PRECACHE_UNMODIFIED_MODEL("sprites/webstrand.spr"); - - UTIL_PrecacheOtherWeapon(kwsMine); - UTIL_PrecacheOtherWeapon(kwsKnife); - UTIL_PrecacheOtherWeapon(kwsMachineGun); - UTIL_PrecacheOtherWeapon(kwsPistol); - UTIL_PrecacheOtherWeapon(kwsShotGun); - UTIL_PrecacheOtherWeapon(kwsHeavyMachineGun); - UTIL_PrecacheOtherWeapon(kwsGrenadeGun); - UTIL_PrecacheOtherWeapon(kwsGrenade); - - // Alien weapons - UTIL_PrecacheOtherWeapon(kwsSpitGun); - UTIL_PrecacheOther(kwsSpitProjectile); - UTIL_PrecacheOther(kwsWebProjectile); - UTIL_PrecacheOtherWeapon(kwsClaws); - UTIL_PrecacheOtherWeapon(kwsSwipe); - UTIL_PrecacheOtherWeapon(kwsSporeGun); - UTIL_PrecacheOther(kwsSporeProjectile); -// UTIL_PrecacheOtherWeapon(kwsParalysisGun); - UTIL_PrecacheOtherWeapon(kwsSpikeGun); - UTIL_PrecacheOtherWeapon(kwsBiteGun); - UTIL_PrecacheOtherWeapon(kwsBite2Gun); - UTIL_PrecacheOtherWeapon(kwsHealingSpray); - UTIL_PrecacheOtherWeapon(kwsWebSpinner); -// UTIL_PrecacheOtherWeapon(kwsBabblerGun); - UTIL_PrecacheOtherWeapon(kwsPrimalScream); - UTIL_PrecacheOtherWeapon(kwsParasiteGun); - UTIL_PrecacheOtherWeapon(kwsMetabolize); - UTIL_PrecacheOtherWeapon(kwsUmbraGun); - UTIL_PrecacheOtherWeapon(kwsBlinkGun); - UTIL_PrecacheOtherWeapon(kwsDivineWind); - UTIL_PrecacheOtherWeapon(kwsBileBombGun); - UTIL_PrecacheOtherWeapon(kwsAcidRocketGun); - UTIL_PrecacheOtherWeapon(kwsStomp); - UTIL_PrecacheOtherWeapon(kwsDevour); -// UTIL_PrecacheOtherWeapon(kwsAmplify); - UTIL_PrecacheOther(kwsBileBomb); - - // Alien abilities - UTIL_PrecacheOtherWeapon(kwsLeap); - UTIL_PrecacheOtherWeapon(kwsCharge); - - // Alien buildings - UTIL_PrecacheOther(kwsAlienResourceTower); - UTIL_PrecacheOther(kwsOffenseChamber); - UTIL_PrecacheOther(kwsDefenseChamber); - UTIL_PrecacheOther(kwsSensoryChamber); - UTIL_PrecacheOther(kwsMovementChamber); - UTIL_PrecacheOther(kesTeamWebStrand); - - // Equipment - //UTIL_PrecacheOtherWeapon("weapon_tripmine"); - UTIL_PrecacheOther(kwsScan); - UTIL_PrecacheOther(kwsPhaseGate); - //UTIL_PrecacheOther(kwsNuke); - - // Marine buildings - UTIL_PrecacheOther(kwsTeamCommand); - UTIL_PrecacheOther(kwsResourceTower); - UTIL_PrecacheOther(kwsInfantryPortal); - UTIL_PrecacheOther(kwsTurretFactory); - UTIL_PrecacheOther(kwsArmory); - UTIL_PrecacheOther(kwsAdvancedArmory); - UTIL_PrecacheOther(kwsArmsLab); - UTIL_PrecacheOther(kwsPrototypeLab); - UTIL_PrecacheOther(kwsObservatory); - //UTIL_PrecacheOther(kwsChemlab); - //UTIL_PrecacheOther(kwsMedlab); - //UTIL_PrecacheOther(kwsNukePlant); - UTIL_PrecacheOther(kwsDeployedTurret); - UTIL_PrecacheOther(kwsSiegeTurret); - - // container for dropped deathmatch weapons - UTIL_PrecacheOther("weaponbox"); - - UTIL_PrecacheOtherWeapon(kwsWelder); - UTIL_PrecacheOther(kwsDeployedMine); - UTIL_PrecacheOther(kwsHealth); - UTIL_PrecacheOther(kwsCatalyst); - UTIL_PrecacheOther(kwsGenericAmmo); - UTIL_PrecacheOther(kwsHeavyArmor); - UTIL_PrecacheOther(kwsJetpack); - UTIL_PrecacheOther(kwsAmmoPack); - - UTIL_PrecacheOther(kwsDebugEntity); - - // Precache other events - gJetpackEventID = PRECACHE_EVENT(1, kJetpackEvent); - //gStartOverwatchEventID = PRECACHE_EVENT(1, kStartOverwatchEvent); - //gEndOverwatchEventID = PRECACHE_EVENT(1, kEndOverwatchEvent); - - // Alien upgrade events - gRegenerationEventID = PRECACHE_EVENT(1, kRegenerationEvent); - gStartCloakEventID = PRECACHE_EVENT(1, kStartCloakEvent); - gEndCloakEventID = PRECACHE_EVENT(1, kEndCloakEvent); - - // Extra alien weapon events - //gEnsnareHitEventID = PRECACHE_EVENT(1, kEnsnareHitEventName); - gSporeCloudEventID = PRECACHE_EVENT(1, kSporeCloudEventName); - gUmbraCloudEventID = PRECACHE_EVENT(1, kUmbraCloudEventName); - gStopScreamEventID = PRECACHE_EVENT(1, kStopPrimalScreamSoundEvent); - - // Extra marine events - gTeleportEventID = PRECACHE_EVENT(1, kTeleportEvent); - gBlinkEffectSuccessEventID = PRECACHE_EVENT(1, kBlinkEffectSuccessEventName); - gPhaseInEventID = PRECACHE_EVENT(1, kPhaseInEvent); - gSiegeHitEventID = PRECACHE_EVENT(1, kSiegeHitEvent); - gSiegeViewHitEventID = PRECACHE_EVENT(1, kSiegeViewHitEvent); - gCommanderPointsAwardedEventID = PRECACHE_EVENT(1, kCommanderPointsAwardedEvent); - gAlienSightOnEventID = PRECACHE_EVENT(1, kAlienSightOnEvent); - gAlienSightOffEventID = PRECACHE_EVENT(1, kAlienSightOffEvent); -// gParalysisStartEventID = PRECACHE_EVENT(1, kParalysisStartEventName); - - //gWallJumpEventID = PRECACHE_EVENT(1, kWallJumpEvent); - //gFlightEventID = PRECACHE_EVENT(1, kFlightEvent); - PRECACHE_UNMODIFIED_SOUND(kConnectSound); - //PRECACHE_UNMODIFIED_SOUND(kDisconnectSound); - PRECACHE_UNMODIFIED_MODEL(kNullModel); - - UTIL_PrecacheOther("monster_sentry"); - - // Allow welder events in mapper build - gWelderEventID = PRECACHE_EVENT(1, kWelderEventName); - gWelderConstEventID = PRECACHE_EVENT(1, kWelderConstEventName); - PRECACHE_EVENT(1, kWelderStartEventName); - PRECACHE_EVENT(1, kWelderEndEventName); - - gEmptySoundEventID = PRECACHE_EVENT(1, kEmptySoundEvent); - gNumericalInfoEventID = PRECACHE_EVENT(1, kNumericalInfoEvent); - gInvalidActionEventID = PRECACHE_EVENT(1, kInvalidActionEvent); - gParticleEventID = PRECACHE_EVENT(1, kParticleEvent); - gDistressBeaconEventID = PRECACHE_EVENT(1, kDistressBeaconEvent); - gWeaponAnimationEventID= PRECACHE_EVENT(1, kWeaponAnimationEvent); - gLevelUpEventID = PRECACHE_EVENT(1, kLevelUpEvent); - gMetabolizeSuccessEventID = PRECACHE_EVENT(1, kMetabolizeSuccessEventName); - - PRECACHE_UNMODIFIED_SOUND(kPhaseInSound); - - // Precache reload sound that is hardcoded deep in HL - PRECACHE_UNMODIFIED_SOUND(kEmptySound); - PRECACHE_UNMODIFIED_SOUND(kInvalidSound); - - // Not sure who's using these, but I keep getting errors that they're not precached. Buttons? - PRECACHE_UNMODIFIED_SOUND("buttons/spark1.wav"); - PRECACHE_UNMODIFIED_SOUND("buttons/spark2.wav"); - PRECACHE_UNMODIFIED_SOUND("buttons/spark3.wav"); - PRECACHE_UNMODIFIED_SOUND("buttons/spark4.wav"); - PRECACHE_UNMODIFIED_SOUND("buttons/spark5.wav"); - PRECACHE_UNMODIFIED_SOUND("buttons/spark6.wav"); - - // For grunts. Careful, this uses the same weapon id that the grenade gun uses - //UTIL_PrecacheOtherWeapon("weapon_9mmAR"); - - // common world objects -// UTIL_PrecacheOther( "item_suit" ); -// UTIL_PrecacheOther( "item_battery" ); -// UTIL_PrecacheOther( "item_antidote" ); -// UTIL_PrecacheOther( "item_security" ); -// UTIL_PrecacheOther( "item_longjump" ); - - // shotgun -// UTIL_PrecacheOtherWeapon( "weapon_shotgun" ); -// UTIL_PrecacheOther( "ammo_buckshot" ); -// -// // crowbar -// UTIL_PrecacheOtherWeapon( "weapon_rowbar" ); -// -// // glock -// UTIL_PrecacheOtherWeapon( "weapon_9mmhandgun" ); -// UTIL_PrecacheOther( "ammo_9mmclip" ); -// -// // mp5 -// UTIL_PrecacheOtherWeapon( "weapon_9mmAR" ); -// UTIL_PrecacheOther( "ammo_9mmAR" ); -// UTIL_PrecacheOther( "ammo_ARgrenades" ); - -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// // python -// UTIL_PrecacheOtherWeapon( "weapon_357" ); -// UTIL_PrecacheOther( "ammo_357" ); -//#endif -// -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// // gauss -// UTIL_PrecacheOtherWeapon( "weapon_gauss" ); -// UTIL_PrecacheOther( "ammo_gaussclip" ); -//#endif -// -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// // rpg -// UTIL_PrecacheOtherWeapon( "weapon_rpg" ); -// UTIL_PrecacheOther( "ammo_rpgclip" ); -//#endif -// -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// // crossbow -// UTIL_PrecacheOtherWeapon( "weapon_crossbow" ); -// UTIL_PrecacheOther( "ammo_crossbow" ); -// UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons -//#endif -// -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// // egon -// UTIL_PrecacheOtherWeapon( "weapon_egon" ); -//#endif -// -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// // satchel charge -// UTIL_PrecacheOtherWeapon( "weapon_satchel" ); -//#endif -// -// // hand grenade -// UTIL_PrecacheOtherWeapon("weapon_handgrenade"); -// -// -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// // squeak grenade -// UTIL_PrecacheOtherWeapon( "weapon_snark" ); -//#endif -// -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// // hornetgun -// UTIL_PrecacheOtherWeapon( "weapon_hornetgun" ); -//#endif - - -//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) -// if ( g_pGameRules->IsDeathmatch() ) -// { -// UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons -// } -//#endif - - g_sModelIndexFireball = PRECACHE_UNMODIFIED_MODEL ("sprites/zerogxplode.spr");// fireball - g_sModelIndexWExplosion = PRECACHE_UNMODIFIED_MODEL ("sprites/WXplo1.spr");// underwater fireball - g_sModelIndexSmoke = PRECACHE_UNMODIFIED_MODEL ("sprites/steam1.spr");// smoke - g_sModelIndexBubbles = PRECACHE_UNMODIFIED_MODEL ("sprites/bubble2.spr");//bubbles - g_sModelIndexBloodSpray = PRECACHE_UNMODIFIED_MODEL ("sprites/bloodspray.spr"); // initial blood - g_sModelIndexBloodDrop = PRECACHE_UNMODIFIED_MODEL ("sprites/blood.spr"); // splattered blood - - g_sModelIndexLaser = PRECACHE_UNMODIFIED_MODEL( (char *)g_pModelNameLaser ); - g_sModelIndexLaserDot = PRECACHE_UNMODIFIED_MODEL("sprites/laserdot.spr"); - - - // used by explosions - PRECACHE_UNMODIFIED_MODEL ("models/grenade.mdl"); - PRECACHE_UNMODIFIED_MODEL ("sprites/explode1.spr"); - - PRECACHE_SOUND ("weapons/debris1.wav");// explosion aftermaths - PRECACHE_SOUND ("weapons/debris2.wav");// explosion aftermaths - PRECACHE_SOUND ("weapons/debris3.wav");// explosion aftermaths - - PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound1); - PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound2); - PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound3); - PRECACHE_UNMODIFIED_SOUND (kGRHitSound); - - PRECACHE_UNMODIFIED_SOUND ("weapons/bullet_hit1.wav"); // hit by bullet - PRECACHE_UNMODIFIED_SOUND ("weapons/bullet_hit2.wav"); // hit by bullet - - PRECACHE_UNMODIFIED_SOUND ("items/weapondrop1.wav");// weapon falls to the ground - -} - - - - -TYPEDESCRIPTION CBasePlayerItem::m_SaveData[] = -{ - DEFINE_FIELD( CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR ), - DEFINE_FIELD( CBasePlayerItem, m_pNext, FIELD_CLASSPTR ), - DEFINE_FIELD( CBasePlayerItem, m_iId, FIELD_INTEGER ), -}; -IMPLEMENT_SAVERESTORE( CBasePlayerItem, CBaseAnimating ); - - -TYPEDESCRIPTION CBasePlayerWeapon::m_SaveData[] = -{ - DEFINE_FIELD( CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_TIME ), - DEFINE_FIELD( CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_TIME ), - DEFINE_FIELD( CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_TIME ), - DEFINE_FIELD( CBasePlayerWeapon, m_iPrimaryAmmoType, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayerWeapon, m_iSecondaryAmmoType, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayerWeapon, m_iClip, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayerWeapon, m_iDefaultAmmo, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CBasePlayerWeapon, CBasePlayerItem ); - - -void CBasePlayerItem :: SetObjectCollisionBox( void ) -{ - pev->absmin = pev->origin + Vector(-24, -24, 0); - pev->absmax = pev->origin + Vector(24, 24, 16); -} - -BOOL -CBasePlayerItem::CanDeploy( void ) -{ - return TRUE; -} - -// can this weapon be put away right now? -BOOL CBasePlayerItem::CanHolster( void ) -{ - return TRUE; -}; - -// returns is deploy was successful -BOOL CBasePlayerItem::Deploy( ) -{ - return TRUE; -} - -BOOL -CBasePlayerItem::IsUseable( void ) -{ - return TRUE; -} - - -//========================================================= -// Sets up movetype, size, solidtype for a new weapon. -//========================================================= -void CBasePlayerItem :: FallInit( void ) -{ - pev->movetype = MOVETYPE_TOSS; - pev->solid = SOLID_BBOX; - - UTIL_SetOrigin( pev, pev->origin ); - UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0) );//pointsize until it lands on the ground. - - SetTouch(&CBasePlayerItem::DefaultTouch ); - SetThink(&CBasePlayerItem::FallThink ); - - pev->nextthink = gpGlobals->time + 0.1; -} - -//========================================================= -// FallThink - Items that have just spawned run this think -// to catch them when they hit the ground. Once we're sure -// that the object is grounded, we change its solid type -// to trigger and set it in a large box that helps the -// player get it. -//========================================================= -void CBasePlayerItem::FallThink ( void ) -{ - pev->nextthink = gpGlobals->time + 0.1; - - if ( pev->flags & FL_ONGROUND ) - { - // clatter if we have an owner (i.e., dropped by someone) - // don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!) - if ( !FNullEnt( pev->owner ) ) - { - int pitch = 95 + RANDOM_LONG(0,29); - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "items/weapondrop1.wav", 1, ATTN_NORM, 0, pitch); - } - - // lie flat - pev->angles.x = 0; - pev->angles.z = 0; - - Materialize(); - } -} - -//========================================================= -// Materialize - make a CBasePlayerItem visible and tangible -//========================================================= -void CBasePlayerItem::Materialize( void ) -{ - if ( pev->effects & EF_NODRAW ) - { - // changing from invisible state to visible. - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 ); - pev->effects &= ~EF_NODRAW; - pev->effects |= EF_MUZZLEFLASH; - } - - pev->solid = SOLID_TRIGGER; - - UTIL_SetOrigin( pev, pev->origin );// link into world. - SetTouch (&CBasePlayerItem::DefaultTouch); - SetThink (NULL); - - this->VirtualMaterialize(); -} - -//========================================================= -// AttemptToMaterialize - the item is trying to rematerialize, -// should it do so now or wait longer? -//========================================================= -void CBasePlayerItem::AttemptToMaterialize( void ) -{ - float time = g_pGameRules->FlWeaponTryRespawn( this ); - - if ( time == 0 ) - { - Materialize(); - return; - } - - pev->nextthink = gpGlobals->time + time; -} - -//========================================================= -// CheckRespawn - a player is taking this weapon, should -// it respawn? -//========================================================= -void CBasePlayerItem :: CheckRespawn ( void ) -{ - switch ( g_pGameRules->WeaponShouldRespawn( this ) ) - { - case GR_WEAPON_RESPAWN_YES: - Respawn(); - break; - case GR_WEAPON_RESPAWN_NO: - return; - break; - } -} - -//========================================================= -// Respawn- this item is already in the world, but it is -// invisible and intangible. Make it visible and tangible. -//========================================================= -CBaseEntity* CBasePlayerItem::Respawn( void ) -{ - // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code - // will decide when to make the weapon visible and touchable. - CBaseEntity *pNewWeapon = CBaseEntity::Create( (char *)STRING( pev->classname ), g_pGameRules->VecWeaponRespawnSpot( this ), pev->angles, pev->owner ); - - if ( pNewWeapon ) - { - pNewWeapon->pev->effects |= EF_NODRAW;// invisible for now - pNewWeapon->SetTouch( NULL );// no touch - pNewWeapon->SetThink(&CBasePlayerItem::AttemptToMaterialize ); - - DROP_TO_FLOOR ( ENT(pev) ); - - // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, - // but when it should respawn is based on conditions belonging to the weapon that was taken. - pNewWeapon->pev->nextthink = g_pGameRules->FlWeaponRespawnTime( this ); - } - else - { - ALERT ( at_console, "Respawn failed to create %s!\n", STRING( pev->classname ) ); - } - - return pNewWeapon; -} - -void CBasePlayerItem::DefaultTouch( CBaseEntity *pOther ) -{ - // if it's not a player, ignore - if ( !pOther->IsPlayer() ) - return; - - CBasePlayer *pPlayer = (CBasePlayer *)pOther; - - // can I have this? - if ( !g_pGameRules->CanHavePlayerItem( pPlayer, this ) ) - { - if ( gEvilImpulse101 ) - { - UTIL_Remove( this ); - } - return; - } - - if (pOther->AddPlayerItem( this )) - { - AttachToPlayer( pPlayer ); - - AvHPlayer* thePlayer = dynamic_cast(pPlayer); - if(thePlayer && thePlayer->GetIsAlien()) - { - EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2-a.wav", 1, ATTN_NORM); - } - else - { - EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM); - } - } - - SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // UNDONE: when should this happen? -} - -BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted ) -{ - if ( !isPredicted ) - { - return ( attack_time <= curtime ) ? TRUE : FALSE; - } - else - { - return ( attack_time <= 0.0 ) ? TRUE : FALSE; - } -} - - -void CBasePlayerWeapon::ItemPostFrame( void ) -{ - bool theAttackPressed = (m_pPlayer->pev->button & IN_ATTACK); - - bool theWeaponPrimes = (this->GetWeaponPrimeTime() > 0.0f); - bool theWeaponIsPriming = this->GetIsWeaponPriming(); - bool theWeaponIsPrimed = this->GetIsWeaponPrimed(); - - if ((m_fInReload) && ( m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase() ) ) - { - // complete the reload. - int j = min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); - - // Add them to the clip - m_iClip += j; - m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; - - m_pPlayer->TabulateAmmo(); - - m_fInReload = FALSE; - } - - if ((m_pPlayer->pev->button & IN_ATTACK2) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, UseDecrement() ) ) - { - if (m_pPlayer->GetCanUseWeapon()) - { - - if ( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] ) - { - m_fFireOnEmpty = TRUE; - } - - m_pPlayer->TabulateAmmo(); - SecondaryAttack(); - m_pPlayer->pev->button &= ~IN_ATTACK2; - } - } - else if ( theAttackPressed && CanAttack( m_flNextPrimaryAttack, gpGlobals->time, UseDecrement() ) ) - { - if (m_pPlayer->GetCanUseWeapon()) - { - if ( (m_iClip == 0 && pszAmmo1()) || (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) ) - { - m_fFireOnEmpty = TRUE; - } - - m_pPlayer->TabulateAmmo(); - PrimaryAttack(); - } - } - else if ( m_pPlayer->pev->button & IN_RELOAD && iMaxClip() != WEAPON_NOCLIP && !m_fInReload ) - { - if (m_pPlayer->GetCanUseWeapon()) - { - // reload when reload is pressed, or if no buttons are down and weapon is empty. - Reload(); - } - } - else if ( !(m_pPlayer->pev->button & (IN_ATTACK|IN_ATTACK2) ) ) - { - if (m_pPlayer->GetCanUseWeapon()) - { - - // no fire buttons down - - m_fFireOnEmpty = FALSE; - - if ( !IsUseable() && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) - { - // weapon isn't useable, switch. - if ( !(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && g_pGameRules->GetNextBestWeapon( m_pPlayer, this ) ) - { - m_flNextPrimaryAttack = ( UseDecrement() ? 0.0 : gpGlobals->time ) + 0.3; - return; - } - } - else - { - // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing - if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) - { - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0) - { - Reload(); - return; - } - } - } - - WeaponIdle( ); - } - return; - } - - // catch all - if ( ShouldWeaponIdle() ) - { - WeaponIdle(); - } -} - -void CBasePlayerItem::DestroyItem( void ) -{ - //KGP: this is a virtual function call for a reason... - // it had been replaced with the contents of - // CBasePlayerItem::VirtualDestroyItem, ignoring - // AvHBasePlayerWeapon::VirtualDestroyItem in 3.01 - this->VirtualDestroyItem(); -} - -void CBasePlayerItem::VirtualDestroyItem( void ) -{ - if ( m_pPlayer ) - { - // if attached to a player, remove. - m_pPlayer->RemovePlayerItem( this ); - } - - Kill( ); -} - -int CBasePlayerItem::AddToPlayer( CBasePlayer *pPlayer ) -{ - m_pPlayer = pPlayer; - pPlayer->m_iHideHUD &= ~HIDEHUD_WEAPONS; - return TRUE; -} - -void CBasePlayerItem::Drop( void ) -{ - SetTouch( NULL ); - SetThink(&CBasePlayerItem::SUB_Remove); - pev->nextthink = gpGlobals->time + .1; -} - -void CBasePlayerItem::Kill( void ) -{ - SetTouch( NULL ); - SetThink(&CBasePlayerItem::SUB_Remove); - pev->nextthink = gpGlobals->time + .1; -} - -void CBasePlayerItem::Holster( int skiplocal /* = 0 */ ) -{ - m_pPlayer->pev->viewmodel = 0; - m_pPlayer->pev->weaponmodel = 0; -} - -void CBasePlayerItem::AttachToPlayer ( CBasePlayer *pPlayer ) -{ - pev->movetype = MOVETYPE_FOLLOW; - pev->solid = SOLID_NOT; - pev->aiment = pPlayer->edict(); - pev->effects = EF_NODRAW; // ?? - pev->modelindex = 0;// server won't send down to clients if modelindex == 0 - pev->model = iStringNull; - pev->owner = pPlayer->edict(); - pev->nextthink = gpGlobals->time + .1; - SetTouch( NULL ); -} - -void CBasePlayerItem::Spawn() -{ - CBaseAnimating::Spawn(); -} - -// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal -int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal ) -{ - if ( m_iDefaultAmmo ) - { - return ExtractAmmo( (CBasePlayerWeapon *)pOriginal ); - } - else - { - // a dead player dropped this. - return ExtractClipAmmo( (CBasePlayerWeapon *)pOriginal ); - } -} - - -int CBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer ) -{ - int bResult = CBasePlayerItem::AddToPlayer( pPlayer ); - - pPlayer->pev->weapons |= (1<GetAmmoIndex( pszAmmo1() ); - m_iSecondaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo2() ); - } - - - if (bResult) - return AddWeapon( ); - return FALSE; -} - -int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) -{ - BOOL bSend = FALSE; - int state = 0; - - // KGP: folded m_iEnabled into the state value and converted it to a proper bitfield. - if( pPlayer->m_pActiveItem == this ) - { state |= WEAPON_IS_CURRENT; } - if( pPlayer->m_fOnTarget ) - { state |= WEAPON_ON_TARGET; } - if( m_iEnabled ) - { state |= WEAPON_IS_ENABLED; } - - // Forcing send of all data! - if ( !pPlayer->m_fWeapon ) - { - bSend = TRUE; - } - - // This is the current or last weapon, so the state will need to be updated - if ( this == pPlayer->m_pActiveItem || - this == pPlayer->m_pClientActiveItem ) - { - if ( pPlayer->m_pActiveItem != pPlayer->m_pClientActiveItem ) - { - bSend = TRUE; - } - } - - // If the ammo, state, or fov has changed, update the weapon - if ( m_iClip != m_iClientClip || - state != m_iClientWeaponState || - pPlayer->m_iFOV != pPlayer->m_iClientFOV ) - { - bSend = TRUE; - } - - if ( bSend ) - { - NetMsg_CurWeapon( pPlayer->pev, state, m_iId, m_iClip ); - m_iClientClip = m_iClip; - m_iClientWeaponState = state; - pPlayer->m_fWeapon = TRUE; - } - - if ( m_pNext ) - m_pNext->UpdateClientData( pPlayer ); - - return 1; -} - -void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body ) -{ - if(iAnim >= 0) - { - if ( UseDecrement() ) - skiplocal = 1; - else - skiplocal = 0; - - m_pPlayer->pev->weaponanim = iAnim; - - if ( skiplocal && ENGINE_CANSKIP( m_pPlayer->edict() ) ) - return; - - MESSAGE_BEGIN( MSG_ONE, SVC_WEAPONANIM, NULL, m_pPlayer->pev ); - WRITE_BYTE( iAnim ); // sequence number - WRITE_BYTE( pev->body ); // weaponmodel bodygroup. - MESSAGE_END(); - } -} - -BOOL CBasePlayerWeapon :: AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry ) -{ - int iIdAmmo; - - if (iMaxClip < 1) - { - m_iClip = -1; - iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry ); - } - else if (m_iClip == 0) - { - int i; - i = min( m_iClip + iCount, iMaxClip ) - m_iClip; - m_iClip += i; - iIdAmmo = m_pPlayer->GiveAmmo( iCount - i, szName, iMaxCarry ); - } - else - { - iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry ); - } - - // m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = iMaxCarry; // hack for testing - - if (iIdAmmo > 0) - { - m_iPrimaryAmmoType = iIdAmmo; - if (m_pPlayer->HasPlayerItem( this ) ) - { - // play the "got ammo" sound only if we gave some ammo to a player that already had this gun. - // if the player is just getting this gun for the first time, DefaultTouch will play the "picked up gun" sound for us. - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - } - } - - return iIdAmmo > 0 ? TRUE : FALSE; -} - - -BOOL CBasePlayerWeapon :: AddSecondaryAmmo( int iCount, char *szName, int iMax ) -{ - int iIdAmmo; - - iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMax ); - - //m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] = iMax; // hack for testing - - if (iIdAmmo > 0) - { - m_iSecondaryAmmoType = iIdAmmo; - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - } - return iIdAmmo > 0 ? TRUE : FALSE; -} - -//========================================================= -// IsUseable - this function determines whether or not a -// weapon is useable by the player in its current state. -// (does it have ammo loaded? do I have any ammo for the -// weapon?, etc) -//========================================================= -BOOL CBasePlayerWeapon :: IsUseable( void ) -{ - if ( m_iClip <= 0 ) - { - // This looks like a nasty bug Valve didn't notice - ASSERT(PrimaryAmmoIndex() >= 0); - - if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] <= 0 && iMaxAmmo1() != -1 ) - { - // clip is empty (or nonexistant) and the player has no more ammo of this type. - return FALSE; - } - } - - return TRUE; -} - -BOOL CBasePlayerWeapon :: CanDeploy( void ) -{ - BOOL bHasAmmo = 0; - - // All weapons can always deploy. Once fire is hit, it checks if it's out of ammo. If so, the weapon switches. - // This is needed so ammo packs function correctly, and it also feels more responsive. - -// if ( !pszAmmo1() ) -// { -// // this weapon doesn't use ammo, can always deploy. -// return TRUE; -// } -// -// if ( pszAmmo1() ) -// { -// bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] != 0); -// } -// if ( pszAmmo2() ) -// { -// bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] != 0); -// } -// if (m_iClip > 0) -// { -// bHasAmmo |= 1; -// } -// if (!bHasAmmo) -// { -// return FALSE; -// } - - return TRUE; -} - -BOOL CBasePlayerWeapon :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal, int body) -{ - if (!CanDeploy( )) - return FALSE; - - m_pPlayer->TabulateAmmo(); - m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); - m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); - strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); - SendWeaponAnim( iAnim, skiplocal, body ); - - // Set the player animation as well - //this->m_pPlayer->SetAnimation(PLAYER_ANIM(iAnim)); - - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + this->GetDeployTime(); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + this->GetDeployTime() + kDeployIdleInterval; - - return TRUE; -} - - -BOOL CBasePlayerWeapon :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body) -{ - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) - return FALSE; - - int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); - - if (j == 0) - return FALSE; - - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay; - - //!!UNDONE -- reload sound goes here !!! - //SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 ); - - m_fInReload = TRUE; - - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + kDeployIdleInterval; - - return TRUE; -} - -BOOL CBasePlayerWeapon :: PlayEmptySound( void ) -{ - if (m_iPlayEmptySound) - { -// EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM); - - int flags = FEV_NOTHOST; - - PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), gEmptySoundEventID, 0.0, (float *)&(m_pPlayer->pev->origin), (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); - - m_iPlayEmptySound = 0; - return 0; - } - return 0; -} - -void CBasePlayerWeapon :: ResetEmptySound( void ) -{ - m_iPlayEmptySound = 1; -} - -//========================================================= -//========================================================= -int CBasePlayerWeapon::PrimaryAmmoIndex( void ) -{ - return m_iPrimaryAmmoType; -} - -//========================================================= -//========================================================= -int CBasePlayerWeapon::SecondaryAmmoIndex( void ) -{ - return -1; -} - -void CBasePlayerWeapon::Holster( int skiplocal /* = 0 */ ) -{ - m_fInReload = FALSE; // cancel any reload in progress. - m_pPlayer->pev->viewmodel = 0; - m_pPlayer->pev->weaponmodel = 0; -} - -void CBasePlayerAmmo::Spawn( void ) -{ - pev->movetype = MOVETYPE_TOSS; - pev->solid = SOLID_TRIGGER; - UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16)); - UTIL_SetOrigin( pev, pev->origin ); - - SetTouch(&CBasePlayerAmmo::DefaultTouch ); -} - -CBaseEntity* CBasePlayerAmmo::Respawn( void ) -{ - pev->effects |= EF_NODRAW; - SetTouch( NULL ); - - UTIL_SetOrigin( pev, g_pGameRules->VecAmmoRespawnSpot( this ) );// move to wherever I'm supposed to repawn. - - SetThink(&CBasePlayerAmmo::Materialize ); - pev->nextthink = g_pGameRules->FlAmmoRespawnTime( this ); - - return this; -} - -void CBasePlayerAmmo::Materialize( void ) -{ - if ( pev->effects & EF_NODRAW ) - { - // changing from invisible state to visible. - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 ); - pev->effects &= ~EF_NODRAW; - pev->effects |= EF_MUZZLEFLASH; - } - - SetTouch(&CBasePlayerAmmo::DefaultTouch ); -} - -void CBasePlayerAmmo :: DefaultTouch( CBaseEntity *pOther ) -{ - if ( !pOther->IsPlayer() ) - { - return; - } - - if (AddAmmo( pOther )) - { - if ( g_pGameRules->AmmoShouldRespawn( this ) == GR_AMMO_RESPAWN_YES ) - { - Respawn(); - } - else - { - SetTouch( NULL ); - SetThink(&CBasePlayerAmmo::SUB_Remove); - pev->nextthink = gpGlobals->time + .1; - } - } - else if (gEvilImpulse101) - { - // evil impulse 101 hack, kill always - SetTouch( NULL ); - SetThink(&CBasePlayerAmmo::SUB_Remove); - pev->nextthink = gpGlobals->time + .1; - } -} - -//========================================================= -// called by the new item with the existing item as parameter -// -// if we call ExtractAmmo(), it's because the player is picking up this type of weapon for -// the first time. If it is spawned by the world, m_iDefaultAmmo will have a default ammo amount in it. -// if this is a weapon dropped by a dying player, has 0 m_iDefaultAmmo, which means only the ammo in -// the weapon clip comes along. -//========================================================= -int CBasePlayerWeapon::ExtractAmmo( CBasePlayerWeapon *pWeapon ) -{ - int iReturn; - - if ( pszAmmo1() != NULL ) - { - // blindly call with m_iDefaultAmmo. It's either going to be a value or zero. If it is zero, - // we only get the ammo in the weapon's clip, which is what we want. - iReturn = pWeapon->AddPrimaryAmmo( m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1() ); - m_iDefaultAmmo = 0; - } - - if ( pszAmmo2() != NULL ) - { - iReturn = pWeapon->AddSecondaryAmmo( 0, (char *)pszAmmo2(), iMaxAmmo2() ); - } - - return iReturn; -} - -//========================================================= -// called by the new item's class with the existing item as parameter -//========================================================= -int CBasePlayerWeapon::ExtractClipAmmo( CBasePlayerWeapon *pWeapon ) -{ - int iAmmo; - - if ( m_iClip == WEAPON_NOCLIP ) - { - iAmmo = 0;// guns with no clips always come empty if they are second-hand - } - else - { - iAmmo = m_iClip; - } - - return pWeapon->m_pPlayer->GiveAmmo( iAmmo, (char *)pszAmmo1(), iMaxAmmo1() ); // , &m_iPrimaryAmmoType -} - -//========================================================= -// RetireWeapon - no more ammo for this gun, put it away. -//========================================================= -void CBasePlayerWeapon::RetireWeapon( void ) -{ - // first, no viewmodel at all. - m_pPlayer->pev->viewmodel = iStringNull; - m_pPlayer->pev->weaponmodel = iStringNull; - //m_pPlayer->pev->viewmodelindex = NULL; - - g_pGameRules->GetNextBestWeapon( m_pPlayer, this ); -} - -//********************************************************* -// weaponbox code: -//********************************************************* - -LINK_ENTITY_TO_CLASS( weaponbox, CWeaponBox ); - -TYPEDESCRIPTION CWeaponBox::m_SaveData[] = -{ - DEFINE_ARRAY( CWeaponBox, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ), - DEFINE_ARRAY( CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS ), - DEFINE_ARRAY( CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ), - DEFINE_FIELD( CWeaponBox, m_cAmmoTypes, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CWeaponBox, CBaseEntity ); - -//========================================================= -// -//========================================================= -void CWeaponBox::Precache( void ) -{ - PRECACHE_UNMODIFIED_MODEL("models/w_weaponbox.mdl"); -} - -//========================================================= -//========================================================= -void CWeaponBox :: KeyValue( KeyValueData *pkvd ) -{ - if ( m_cAmmoTypes < MAX_AMMO_SLOTS ) - { - PackAmmo( ALLOC_STRING(pkvd->szKeyName), atoi(pkvd->szValue) ); - m_cAmmoTypes++;// count this new ammo type. - - pkvd->fHandled = TRUE; - } - else - { - ALERT ( at_console, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS ); - } -} - -//========================================================= -// CWeaponBox - Spawn -//========================================================= -void CWeaponBox::Spawn( void ) -{ - Precache( ); - - pev->movetype = MOVETYPE_TOSS; - pev->solid = SOLID_TRIGGER; - - UTIL_SetSize( pev, g_vecZero, g_vecZero ); - - SET_MODEL( ENT(pev), "models/w_weaponbox.mdl"); -} - -//========================================================= -// CWeaponBox - Kill - the think function that removes the -// box from the world. -//========================================================= -void CWeaponBox::Kill( void ) -{ - CBasePlayerItem *pWeapon; - int i; - - // destroy the weapons - for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - pWeapon = m_rgpPlayerItems[ i ]; - - while ( pWeapon ) - { - pWeapon->SetThink(&CWeaponBox::SUB_Remove); - pWeapon->pev->nextthink = gpGlobals->time + 0.1; - pWeapon = pWeapon->m_pNext; - } - } - - // remove the box - UTIL_Remove( this ); -} - -//========================================================= -// CWeaponBox - Touch: try to add my contents to the toucher -// if the toucher is a player. -//========================================================= -void CWeaponBox::Touch( CBaseEntity *pOther ) -{ - if ( !(pev->flags & FL_ONGROUND ) ) - { - return; - } - - if ( !pOther->IsPlayer() ) - { - // only players may touch a weaponbox. - return; - } - - if ( !pOther->IsAlive() ) - { - // no dead guys. - return; - } - - CBasePlayer *pPlayer = (CBasePlayer *)pOther; - int i; - -// dole out ammo - for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) - { - if ( !FStringNull( m_rgiszAmmo[ i ] ) ) - { - // there's some ammo of this type. - pPlayer->GiveAmmo( m_rgAmmo[ i ], (char *)STRING( m_rgiszAmmo[ i ] ), MaxAmmoCarry( m_rgiszAmmo[ i ] ) ); - - //ALERT ( at_console, "Gave %d rounds of %s\n", m_rgAmmo[i], STRING(m_rgiszAmmo[i]) ); - - // now empty the ammo from the weaponbox since we just gave it to the player - m_rgiszAmmo[ i ] = iStringNull; - m_rgAmmo[ i ] = 0; - } - } - -// go through my weapons and try to give the usable ones to the player. -// it's important the the player be given ammo first, so the weapons code doesn't refuse -// to deploy a better weapon that the player may pick up because he has no ammo for it. - for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - if ( m_rgpPlayerItems[ i ] ) - { - CBasePlayerItem *pItem; - - // have at least one weapon in this slot - while ( m_rgpPlayerItems[ i ] ) - { - //ALERT ( at_console, "trying to give %s\n", STRING( m_rgpPlayerItems[ i ]->pev->classname ) ); - - pItem = m_rgpPlayerItems[ i ]; - m_rgpPlayerItems[ i ] = m_rgpPlayerItems[ i ]->m_pNext;// unlink this weapon from the box - - if ( pPlayer->AddPlayerItem( pItem ) ) - { - pItem->AttachToPlayer( pPlayer ); - } - } - } - } - - EMIT_SOUND( pOther->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM ); - SetTouch(NULL); - UTIL_Remove(this); -} - -//========================================================= -// CWeaponBox - PackWeapon: Add this weapon to the box -//========================================================= -BOOL CWeaponBox::PackWeapon( CBasePlayerItem *pWeapon ) -{ - // is one of these weapons already packed in this box? - if ( HasWeapon( pWeapon ) ) - { - return FALSE;// box can only hold one of each weapon type - } - - if ( pWeapon->m_pPlayer ) - { - if ( !pWeapon->m_pPlayer->RemovePlayerItem( pWeapon ) ) - { - // failed to unhook the weapon from the player! - return FALSE; - } - } - - int iWeaponSlot = pWeapon->iItemSlot(); - - if ( m_rgpPlayerItems[ iWeaponSlot ] ) - { - // there's already one weapon in this slot, so link this into the slot's column - pWeapon->m_pNext = m_rgpPlayerItems[ iWeaponSlot ]; - m_rgpPlayerItems[ iWeaponSlot ] = pWeapon; - } - else - { - // first weapon we have for this slot - m_rgpPlayerItems[ iWeaponSlot ] = pWeapon; - pWeapon->m_pNext = NULL; - } - - pWeapon->pev->spawnflags |= SF_NORESPAWN;// never respawn - pWeapon->pev->movetype = MOVETYPE_NONE; - pWeapon->pev->solid = SOLID_NOT; - pWeapon->pev->effects = EF_NODRAW; - pWeapon->pev->modelindex = 0; - pWeapon->pev->model = iStringNull; - pWeapon->pev->owner = edict(); - pWeapon->SetThink( NULL );// crowbar may be trying to swing again, etc. - pWeapon->SetTouch( NULL ); - pWeapon->m_pPlayer = NULL; - - //ALERT ( at_console, "packed %s\n", STRING(pWeapon->pev->classname) ); - - return TRUE; -} - -//========================================================= -// CWeaponBox - PackAmmo -//========================================================= -BOOL CWeaponBox::PackAmmo( int iszName, int iCount ) -{ - int iMaxCarry; - - if ( FStringNull( iszName ) ) - { - // error here - ALERT ( at_console, "NULL String in PackAmmo!\n" ); - return FALSE; - } - - iMaxCarry = MaxAmmoCarry( iszName ); - - if ( iMaxCarry != -1 && iCount > 0 ) - { - //ALERT ( at_console, "Packed %d rounds of %s\n", iCount, STRING(iszName) ); - GiveAmmo( iCount, (char *)STRING( iszName ), iMaxCarry ); - return TRUE; - } - - return FALSE; -} - -//========================================================= -// CWeaponBox - GiveAmmo -//========================================================= -int CWeaponBox::GiveAmmo( int iCount, char *szName, int iMax, int *pIndex/* = NULL*/ ) -{ - int i; - - for (i = 1; i < MAX_AMMO_SLOTS && !FStringNull( m_rgiszAmmo[i] ); i++) - { - if (stricmp( szName, STRING( m_rgiszAmmo[i])) == 0) - { - if (pIndex) - *pIndex = i; - - int iAdd = min( iCount, iMax - m_rgAmmo[i]); - if (iCount == 0 || iAdd > 0) - { - m_rgAmmo[i] += iAdd; - - return i; - } - return -1; - } - } - if (i < MAX_AMMO_SLOTS) - { - if (pIndex) - *pIndex = i; - - m_rgiszAmmo[i] = MAKE_STRING( szName ); - m_rgAmmo[i] = iCount; - - return i; - } - ALERT( at_console, "out of named ammo slots\n"); - return i; -} - -//========================================================= -// CWeaponBox::HasWeapon - is a weapon of this type already -// packed in this box? -//========================================================= -BOOL CWeaponBox::HasWeapon( CBasePlayerItem *pCheckItem ) -{ - CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()]; - - while (pItem) - { - if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) )) - { - return TRUE; - } - pItem = pItem->m_pNext; - } - - return FALSE; -} - -//========================================================= -// CWeaponBox::IsEmpty - is there anything in this box? -//========================================================= -BOOL CWeaponBox::IsEmpty( void ) -{ - int i; - - for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - if ( m_rgpPlayerItems[ i ] ) - { - return FALSE; - } - } - - for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) - { - if ( !FStringNull( m_rgiszAmmo[ i ] ) ) - { - // still have a bit of this type of ammo - return FALSE; - } - } - - return TRUE; -} - -//========================================================= -//========================================================= -void CWeaponBox::SetObjectCollisionBox( void ) -{ - pev->absmin = pev->origin + Vector(-16, -16, 0); - pev->absmax = pev->origin + Vector(16, 16, 16); -} - - -void CBasePlayerWeapon::PrintState( void ) -{ - ALERT( at_console, "primary: %f\n", m_flNextPrimaryAttack ); - ALERT( at_console, "idle : %f\n", m_flTimeWeaponIdle ); - -// ALERT( at_console, "nextrl : %f\n", m_flNextReload ); -// ALERT( at_console, "nextpum: %f\n", m_flPumpTime ); - -// ALERT( at_console, "m_frt : %f\n", m_fReloadTime ); - ALERT( at_console, "m_finre: %i\n", m_fInReload ); -// ALERT( at_console, "m_finsr: %i\n", m_fInSpecialReload ); - - ALERT( at_console, "m_iclip: %i\n", m_iClip ); -} - - -TYPEDESCRIPTION CRpg::m_SaveData[] = -{ - DEFINE_FIELD( CRpg, m_fSpotActive, FIELD_INTEGER ), - DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ), -}; -IMPLEMENT_SAVERESTORE( CRpg, CBasePlayerWeapon ); - -TYPEDESCRIPTION CRpgRocket::m_SaveData[] = -{ - DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ), - DEFINE_FIELD( CRpgRocket, m_pLauncher, FIELD_CLASSPTR ), -}; -IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade ); - -TYPEDESCRIPTION CShotgun::m_SaveData[] = -{ - DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ), - DEFINE_FIELD( CShotgun, m_fInSpecialReload, FIELD_INTEGER ), - DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ), - // DEFINE_FIELD( CShotgun, m_iShell, FIELD_INTEGER ), - DEFINE_FIELD( CShotgun, m_flPumpTime, FIELD_TIME ), -}; -IMPLEMENT_SAVERESTORE( CShotgun, CBasePlayerWeapon ); - -TYPEDESCRIPTION CGauss::m_SaveData[] = -{ - DEFINE_FIELD( CGauss, m_fInAttack, FIELD_INTEGER ), -// DEFINE_FIELD( CGauss, m_flStartCharge, FIELD_TIME ), -// DEFINE_FIELD( CGauss, m_flPlayAftershock, FIELD_TIME ), -// DEFINE_FIELD( CGauss, m_flNextAmmoBurn, FIELD_TIME ), - DEFINE_FIELD( CGauss, m_fPrimaryFire, FIELD_BOOLEAN ), -}; -IMPLEMENT_SAVERESTORE( CGauss, CBasePlayerWeapon ); - -TYPEDESCRIPTION CEgon::m_SaveData[] = -{ -// DEFINE_FIELD( CEgon, m_pBeam, FIELD_CLASSPTR ), -// DEFINE_FIELD( CEgon, m_pNoise, FIELD_CLASSPTR ), -// DEFINE_FIELD( CEgon, m_pSprite, FIELD_CLASSPTR ), - DEFINE_FIELD( CEgon, m_shootTime, FIELD_TIME ), - DEFINE_FIELD( CEgon, m_fireState, FIELD_INTEGER ), - DEFINE_FIELD( CEgon, m_fireMode, FIELD_INTEGER ), - DEFINE_FIELD( CEgon, m_shakeTime, FIELD_TIME ), - DEFINE_FIELD( CEgon, m_flAmmoUseTime, FIELD_TIME ), -}; -IMPLEMENT_SAVERESTORE( CEgon, CBasePlayerWeapon ); - -TYPEDESCRIPTION CSatchel::m_SaveData[] = -{ - DEFINE_FIELD( CSatchel, m_chargeReady, FIELD_INTEGER ), -}; -IMPLEMENT_SAVERESTORE( CSatchel, CBasePlayerWeapon ); - +// +// Copyright (c) 1999, Valve LLC. All rights reserved. +// +// This product contains software technology licensed from Id +// Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +// All Rights Reserved. +// +// Use, distribution, and modification of this source code and/or resulting +// object code is restricted to non-commercial enhancements to products from +// Valve LLC. All other use, distribution, or modification is prohibited +// without written permission from Valve LLC. +// +// ===== weapons.cpp ======================================================== +// +// functions governing the selection/use of weapons for players +// +// $Workfile: weapons.cpp $ +// $Date: 2002/11/22 21:13:52 $ +// +//------------------------------------------------------------------------------- +// $Log: weapons.cpp,v $ +// Revision 1.49 2002/11/22 21:13:52 Flayra +// - mp_consistency changes +// +// Revision 1.48 2002/11/12 02:19:43 Flayra +// - Fixed problem where friendly bulletfire was doing damage +// +// Revision 1.47 2002/10/24 21:19:11 Flayra +// - Reworked jetpack effects +// - Added a few extra sounds +// +// Revision 1.46 2002/10/16 00:41:15 Flayra +// - Removed unneeds sounds and events +// - Added distress beacon event +// - Added general purpose particle event +// +// Revision 1.45 2002/09/25 20:40:09 Flayra +// - Play different sound for aliens when they get weapons +// +// Revision 1.44 2002/09/23 22:04:00 Flayra +// - Regular update +// +// Revision 1.43 2002/08/31 18:01:18 Flayra +// - Work at VALVe +// +// Revision 1.42 2002/07/26 23:01:05 Flayra +// - Precache numerical feedback event +// +// Revision 1.41 2002/07/23 16:48:37 Flayra +// - Added distress beacon +// +// Revision 1.40 2002/07/08 16:35:17 Flayra +// - Added document header, updates for cheat protection, added constants +// +//=============================================================================== +#include "util/nowarnings.h" +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "player.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "soundent.h" +#include "decals.h" +#include "gamerules.h" +#include "mod/AvHConstants.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHNetworkMessages.h" + +extern CGraph WorldGraph; +extern int gEvilImpulse101; + +int gJetpackEventID; +int gStartOverwatchEventID; +int gEndOverwatchEventID; +int gTeleportEventID; +int gBlinkEffectSuccessEventID; +int gPhaseInEventID; +int gSiegeHitEventID; +int gSiegeViewHitEventID; +int gCommanderPointsAwardedEventID; +int gAlienSightOnEventID; +int gAlienSightOffEventID; +//int gParalysisStartEventID; +int gRegenerationEventID; +int gStartCloakEventID; +int gEndCloakEventID; +int gSporeCloudEventID; +int gUmbraCloudEventID; +int gStopScreamEventID; + +int gWelderEventID; +int gWelderConstEventID; +//int gWallJumpEventID; +//int gFlightEventID; +int gEmptySoundEventID; +int gNumericalInfoEventID; +int gInvalidActionEventID; +int gParticleEventID; +int gDistressBeaconEventID; +int gWeaponAnimationEventID; +int gLevelUpEventID; +int gMetabolizeSuccessEventID; + +#define NOT_USED 255 + +DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam +DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr"; +DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot +DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball +DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud +DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion +DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model +DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood +DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood + +ItemInfo CBasePlayerItem::ItemInfoArray[MAX_WEAPONS]; +AmmoInfo CBasePlayerItem::AmmoInfoArray[MAX_AMMO_SLOTS]; + +MULTIDAMAGE gMultiDamage; + +#define TRACER_FREQ 4 // Tracers fire every fourth bullet + +extern bool gCanMove[]; + +//========================================================= +// MaxAmmoCarry - pass in a name and this function will tell +// you the maximum amount of that type of ammunition that a +// player can carry. +//========================================================= +int MaxAmmoCarry( int iszName ) +{ + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo1 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo1 ) ) + return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo1; + if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo2 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo2 ) ) + return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo2; + } + + ALERT( at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) ); + return -1; +} + + +/* +============================================================================== + +MULTI-DAMAGE + +Collects multiple small damages into a single damage + +============================================================================== +*/ + +// +// ClearMultiDamage - resets the global multi damage accumulator +// +void ClearMultiDamage(void) +{ + gMultiDamage.pEntity = NULL; + gMultiDamage.amount = 0; + gMultiDamage.type = 0; +} + +// +// ApplyMultiDamage - inflicts contents of global multi damage register on gMultiDamage.pEntity +// +// GLOBALS USED: +// gMultiDamage + +void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker ) +{ + Vector vecSpot1;//where blood comes from + Vector vecDir;//direction blood should go + TraceResult tr; + + if ( !gMultiDamage.pEntity ) + return; + + float theDamage = gMultiDamage.amount; + gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, theDamage, gMultiDamage.type ); +} + + +// GLOBALS USED: +// gMultiDamage + +void AddMultiDamage( entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType) +{ + if ( !pEntity ) + return; + + gMultiDamage.type |= bitsDamageType; + + if ( pEntity != gMultiDamage.pEntity ) + { + ApplyMultiDamage(pevInflictor,pevInflictor); // UNDONE: wrong attacker! + gMultiDamage.pEntity = pEntity; + gMultiDamage.amount = 0; + } + + gMultiDamage.amount += flDamage; +} + +/* +================ +SpawnBlood +================ +*/ +void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage) +{ + if(flDamage >= 0.0f) + { + if(bloodColor == DONT_BLEED) + { + UTIL_Sparks(vecSpot); + } + else + { + UTIL_BloodDrips( vecSpot, g_vecAttackDir, bloodColor, (int)flDamage ); + } + } +} + + +int DamageDecal( CBaseEntity *pEntity, int bitsDamageType ) +{ + if ( !pEntity ) + return (DECAL_GUNSHOT1 + RANDOM_LONG(0,4)); + + return pEntity->DamageDecal( bitsDamageType ); +} + +void DecalGunshot( TraceResult *pTrace, int iBulletType ) +{ + // Is the entity valid + if ( !UTIL_IsValidEntity( pTrace->pHit ) ) + return; + + if ( VARS(pTrace->pHit)->solid == SOLID_BSP || VARS(pTrace->pHit)->movetype == MOVETYPE_PUSHSTEP ) + { + CBaseEntity *pEntity = NULL; + // Decal the wall with a gunshot + if ( !FNullEnt(pTrace->pHit) ) + pEntity = CBaseEntity::Instance(pTrace->pHit); + +// switch( iBulletType ) +// { +// case BULLET_PLAYER_9MM: +// case BULLET_MONSTER_9MM: +// case BULLET_PLAYER_MP5: +// case BULLET_MONSTER_MP5: +// case BULLET_PLAYER_BUCKSHOT: +// case BULLET_PLAYER_357: +// default: + // smoke and decal + UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); +// break; +// case BULLET_MONSTER_12MM: +// // smoke and decal +// UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); +// break; +// case BULLET_PLAYER_CROWBAR: +// // wall decal +// UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) ); +// break; +// } + } +} + + + +// +// EjectBrass - tosses a brass shell from passed origin at passed velocity +// +void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype ) +{ + // FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see. + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); + WRITE_BYTE( TE_MODEL); + WRITE_COORD( vecOrigin.x); + WRITE_COORD( vecOrigin.y); + WRITE_COORD( vecOrigin.z); + WRITE_COORD( vecVelocity.x); + WRITE_COORD( vecVelocity.y); + WRITE_COORD( vecVelocity.z); + WRITE_ANGLE( rotation ); + WRITE_SHORT( model ); + WRITE_BYTE ( soundtype); + WRITE_BYTE ( 25 );// 2.5 seconds + MESSAGE_END(); +} + + +#if 0 +// UNDONE: This is no longer used? +void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count ) +{ + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); + WRITE_BYTE ( TE_EXPLODEMODEL ); + WRITE_COORD( vecOrigin.x ); + WRITE_COORD( vecOrigin.y ); + WRITE_COORD( vecOrigin.z ); + WRITE_COORD( speed ); + WRITE_SHORT( model ); + WRITE_SHORT( count ); + WRITE_BYTE ( 15 );// 1.5 seconds + MESSAGE_END(); +} +#endif + + +int giAmmoIndex = 0; + +// Precaches the ammo and queues the ammo info for sending to clients +void AddAmmoNameToAmmoRegistry( const char *szAmmoname ) +{ + // make sure it's not already in the registry + for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) + { + if ( !CBasePlayerItem::AmmoInfoArray[i].pszName) + continue; + + if ( stricmp( CBasePlayerItem::AmmoInfoArray[i].pszName, szAmmoname ) == 0 ) + return; // ammo already in registry, just quite + } + + + giAmmoIndex++; + ASSERT( giAmmoIndex < MAX_AMMO_SLOTS ); + if ( giAmmoIndex >= MAX_AMMO_SLOTS ) + giAmmoIndex = 0; + + CBasePlayerItem::AmmoInfoArray[giAmmoIndex].pszName = szAmmoname; + CBasePlayerItem::AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; // yes, this info is redundant +} + + +// Precaches the weapon and queues the weapon info for sending to clients +void UTIL_PrecacheOtherWeapon( const char *szClassname ) +{ + edict_t *pent; + + pent = CREATE_NAMED_ENTITY( MAKE_STRING( szClassname ) ); + if ( FNullEnt( pent ) ) + { + ALERT ( at_console, "NULL Ent in UTIL_PrecacheOtherWeapon\n" ); + return; + } + + CBaseEntity *pEntity = CBaseEntity::Instance (VARS( pent )); + + if (pEntity) + { + ItemInfo II; + pEntity->Precache( ); + memset( &II, 0, sizeof II ); + if ( ((CBasePlayerItem*)pEntity)->GetItemInfo( &II ) ) + { + CBasePlayerItem::ItemInfoArray[II.iId] = II; + + if ( II.pszAmmo1 && *II.pszAmmo1 ) + { + AddAmmoNameToAmmoRegistry( II.pszAmmo1 ); + } + + if ( II.pszAmmo2 && *II.pszAmmo2 ) + { + AddAmmoNameToAmmoRegistry( II.pszAmmo2 ); + } + + memset( &II, 0, sizeof II ); + } + } + + REMOVE_ENTITY(pent); +} + +// called by worldspawn +void W_Precache(void) +{ + memset( CBasePlayerItem::ItemInfoArray, 0, sizeof(CBasePlayerItem::ItemInfoArray) ); + memset( CBasePlayerItem::AmmoInfoArray, 0, sizeof(CBasePlayerItem::AmmoInfoArray) ); + giAmmoIndex = 0; + + // Marine weapons + //UTIL_PrecacheOtherWeapon("weapon_9mmhandgun"); + //UTIL_PrecacheOtherWeapon("weapon_glock"); + //UTIL_PrecacheOther("ammo_9mmclip"); + + // Player assets + PRECACHE_UNMODIFIED_MODEL(kReadyRoomModel); + PRECACHE_UNMODIFIED_MODEL(kMarineSoldierModel); + PRECACHE_UNMODIFIED_MODEL(kHeavySoldierModel); + PRECACHE_UNMODIFIED_MODEL(kAlienLevelOneModel); + PRECACHE_UNMODIFIED_SOUND(kDistressBeaconSound); + PRECACHE_UNMODIFIED_SOUND(kLevelUpMarineSound); + PRECACHE_UNMODIFIED_SOUND(kLevelUpAlienSound); + PRECACHE_UNMODIFIED_SOUND(kAlienBuildingSound1); + PRECACHE_UNMODIFIED_SOUND(kAlienBuildingSound2); + PRECACHE_SOUND(kMyHiveEasterEgg); + + //PRECACHE_UNMODIFIED_SOUND(kAlienAbilitiesGrantedSound); + //PRECACHE_UNMODIFIED_SOUND(kAlienAbilitiesLostSound); + + PRECACHE_UNMODIFIED_MODEL(kAlienLevelTwoModel); + PRECACHE_UNMODIFIED_MODEL(kAlienLevelThreeModel); + PRECACHE_UNMODIFIED_MODEL(kAlienLevelFourModel); + PRECACHE_UNMODIFIED_MODEL(kAlienLevelFiveModel); + + PRECACHE_UNMODIFIED_MODEL(kMarineCommanderModel); + PRECACHE_UNMODIFIED_MODEL(kAlienGestateModel); + // : 1072 + // Added some client side consistency checks. + PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash1.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash2.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash3.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/digesting.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/membrane.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/hera_fog.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/spore.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/spore2.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/umbra.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/umbra2.spr"); + PRECACHE_UNMODIFIED_MODEL("sprites/webstrand.spr"); + + UTIL_PrecacheOtherWeapon(kwsMine); + UTIL_PrecacheOtherWeapon(kwsKnife); + UTIL_PrecacheOtherWeapon(kwsMachineGun); + UTIL_PrecacheOtherWeapon(kwsPistol); + UTIL_PrecacheOtherWeapon(kwsShotGun); + UTIL_PrecacheOtherWeapon(kwsHeavyMachineGun); + UTIL_PrecacheOtherWeapon(kwsGrenadeGun); + UTIL_PrecacheOtherWeapon(kwsGrenade); + + // Alien weapons + UTIL_PrecacheOtherWeapon(kwsSpitGun); + UTIL_PrecacheOther(kwsSpitProjectile); + UTIL_PrecacheOther(kwsWebProjectile); + UTIL_PrecacheOtherWeapon(kwsClaws); + UTIL_PrecacheOtherWeapon(kwsSwipe); + UTIL_PrecacheOtherWeapon(kwsSporeGun); + UTIL_PrecacheOther(kwsSporeProjectile); +// UTIL_PrecacheOtherWeapon(kwsParalysisGun); + UTIL_PrecacheOtherWeapon(kwsSpikeGun); + UTIL_PrecacheOtherWeapon(kwsBiteGun); + UTIL_PrecacheOtherWeapon(kwsBite2Gun); + UTIL_PrecacheOtherWeapon(kwsHealingSpray); + UTIL_PrecacheOtherWeapon(kwsWebSpinner); +// UTIL_PrecacheOtherWeapon(kwsBabblerGun); + UTIL_PrecacheOtherWeapon(kwsPrimalScream); + UTIL_PrecacheOtherWeapon(kwsParasiteGun); + UTIL_PrecacheOtherWeapon(kwsMetabolize); + UTIL_PrecacheOtherWeapon(kwsUmbraGun); + UTIL_PrecacheOtherWeapon(kwsBlinkGun); + UTIL_PrecacheOtherWeapon(kwsDivineWind); + UTIL_PrecacheOtherWeapon(kwsBileBombGun); + UTIL_PrecacheOtherWeapon(kwsAcidRocketGun); + UTIL_PrecacheOtherWeapon(kwsStomp); + UTIL_PrecacheOtherWeapon(kwsDevour); +// UTIL_PrecacheOtherWeapon(kwsAmplify); + UTIL_PrecacheOther(kwsBileBomb); + + // Alien abilities + UTIL_PrecacheOtherWeapon(kwsLeap); + UTIL_PrecacheOtherWeapon(kwsCharge); + + // Alien buildings + UTIL_PrecacheOther(kwsAlienResourceTower); + UTIL_PrecacheOther(kwsOffenseChamber); + UTIL_PrecacheOther(kwsDefenseChamber); + UTIL_PrecacheOther(kwsSensoryChamber); + UTIL_PrecacheOther(kwsMovementChamber); + UTIL_PrecacheOther(kesTeamWebStrand); + + // Equipment + //UTIL_PrecacheOtherWeapon("weapon_tripmine"); + UTIL_PrecacheOther(kwsScan); + UTIL_PrecacheOther(kwsPhaseGate); + //UTIL_PrecacheOther(kwsNuke); + + // Marine buildings + UTIL_PrecacheOther(kwsTeamCommand); + UTIL_PrecacheOther(kwsResourceTower); + UTIL_PrecacheOther(kwsInfantryPortal); + UTIL_PrecacheOther(kwsTurretFactory); + UTIL_PrecacheOther(kwsArmory); + UTIL_PrecacheOther(kwsAdvancedArmory); + UTIL_PrecacheOther(kwsArmsLab); + UTIL_PrecacheOther(kwsPrototypeLab); + UTIL_PrecacheOther(kwsObservatory); + //UTIL_PrecacheOther(kwsChemlab); + //UTIL_PrecacheOther(kwsMedlab); + //UTIL_PrecacheOther(kwsNukePlant); + UTIL_PrecacheOther(kwsDeployedTurret); + UTIL_PrecacheOther(kwsSiegeTurret); + + // container for dropped deathmatch weapons + UTIL_PrecacheOther("weaponbox"); + + UTIL_PrecacheOtherWeapon(kwsWelder); + UTIL_PrecacheOther(kwsDeployedMine); + UTIL_PrecacheOther(kwsHealth); + UTIL_PrecacheOther(kwsCatalyst); + UTIL_PrecacheOther(kwsGenericAmmo); + UTIL_PrecacheOther(kwsHeavyArmor); + UTIL_PrecacheOther(kwsJetpack); + UTIL_PrecacheOther(kwsAmmoPack); + + UTIL_PrecacheOther(kwsDebugEntity); + + // Precache other events + gJetpackEventID = PRECACHE_EVENT(1, kJetpackEvent); + //gStartOverwatchEventID = PRECACHE_EVENT(1, kStartOverwatchEvent); + //gEndOverwatchEventID = PRECACHE_EVENT(1, kEndOverwatchEvent); + + // Alien upgrade events + gRegenerationEventID = PRECACHE_EVENT(1, kRegenerationEvent); + gStartCloakEventID = PRECACHE_EVENT(1, kStartCloakEvent); + gEndCloakEventID = PRECACHE_EVENT(1, kEndCloakEvent); + + // Extra alien weapon events + //gEnsnareHitEventID = PRECACHE_EVENT(1, kEnsnareHitEventName); + gSporeCloudEventID = PRECACHE_EVENT(1, kSporeCloudEventName); + gUmbraCloudEventID = PRECACHE_EVENT(1, kUmbraCloudEventName); + gStopScreamEventID = PRECACHE_EVENT(1, kStopPrimalScreamSoundEvent); + + // Extra marine events + gTeleportEventID = PRECACHE_EVENT(1, kTeleportEvent); + gBlinkEffectSuccessEventID = PRECACHE_EVENT(1, kBlinkEffectSuccessEventName); + gPhaseInEventID = PRECACHE_EVENT(1, kPhaseInEvent); + gSiegeHitEventID = PRECACHE_EVENT(1, kSiegeHitEvent); + gSiegeViewHitEventID = PRECACHE_EVENT(1, kSiegeViewHitEvent); + gCommanderPointsAwardedEventID = PRECACHE_EVENT(1, kCommanderPointsAwardedEvent); + gAlienSightOnEventID = PRECACHE_EVENT(1, kAlienSightOnEvent); + gAlienSightOffEventID = PRECACHE_EVENT(1, kAlienSightOffEvent); +// gParalysisStartEventID = PRECACHE_EVENT(1, kParalysisStartEventName); + + //gWallJumpEventID = PRECACHE_EVENT(1, kWallJumpEvent); + //gFlightEventID = PRECACHE_EVENT(1, kFlightEvent); + PRECACHE_UNMODIFIED_SOUND(kConnectSound); + //PRECACHE_UNMODIFIED_SOUND(kDisconnectSound); + PRECACHE_UNMODIFIED_MODEL(kNullModel); + + UTIL_PrecacheOther("monster_sentry"); + + // Allow welder events in mapper build + gWelderEventID = PRECACHE_EVENT(1, kWelderEventName); + gWelderConstEventID = PRECACHE_EVENT(1, kWelderConstEventName); + PRECACHE_EVENT(1, kWelderStartEventName); + PRECACHE_EVENT(1, kWelderEndEventName); + + gEmptySoundEventID = PRECACHE_EVENT(1, kEmptySoundEvent); + gNumericalInfoEventID = PRECACHE_EVENT(1, kNumericalInfoEvent); + gInvalidActionEventID = PRECACHE_EVENT(1, kInvalidActionEvent); + gParticleEventID = PRECACHE_EVENT(1, kParticleEvent); + gDistressBeaconEventID = PRECACHE_EVENT(1, kDistressBeaconEvent); + gWeaponAnimationEventID= PRECACHE_EVENT(1, kWeaponAnimationEvent); + gLevelUpEventID = PRECACHE_EVENT(1, kLevelUpEvent); + gMetabolizeSuccessEventID = PRECACHE_EVENT(1, kMetabolizeSuccessEventName); + + PRECACHE_UNMODIFIED_SOUND(kPhaseInSound); + + // Precache reload sound that is hardcoded deep in HL + PRECACHE_UNMODIFIED_SOUND(kEmptySound); + PRECACHE_UNMODIFIED_SOUND(kInvalidSound); + + // Not sure who's using these, but I keep getting errors that they're not precached. Buttons? + PRECACHE_UNMODIFIED_SOUND("buttons/spark1.wav"); + PRECACHE_UNMODIFIED_SOUND("buttons/spark2.wav"); + PRECACHE_UNMODIFIED_SOUND("buttons/spark3.wav"); + PRECACHE_UNMODIFIED_SOUND("buttons/spark4.wav"); + PRECACHE_UNMODIFIED_SOUND("buttons/spark5.wav"); + PRECACHE_UNMODIFIED_SOUND("buttons/spark6.wav"); + + // For grunts. Careful, this uses the same weapon id that the grenade gun uses + //UTIL_PrecacheOtherWeapon("weapon_9mmAR"); + + // common world objects +// UTIL_PrecacheOther( "item_suit" ); +// UTIL_PrecacheOther( "item_battery" ); +// UTIL_PrecacheOther( "item_antidote" ); +// UTIL_PrecacheOther( "item_security" ); +// UTIL_PrecacheOther( "item_longjump" ); + + // shotgun +// UTIL_PrecacheOtherWeapon( "weapon_shotgun" ); +// UTIL_PrecacheOther( "ammo_buckshot" ); +// +// // crowbar +// UTIL_PrecacheOtherWeapon( "weapon_rowbar" ); +// +// // glock +// UTIL_PrecacheOtherWeapon( "weapon_9mmhandgun" ); +// UTIL_PrecacheOther( "ammo_9mmclip" ); +// +// // mp5 +// UTIL_PrecacheOtherWeapon( "weapon_9mmAR" ); +// UTIL_PrecacheOther( "ammo_9mmAR" ); +// UTIL_PrecacheOther( "ammo_ARgrenades" ); + +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// // python +// UTIL_PrecacheOtherWeapon( "weapon_357" ); +// UTIL_PrecacheOther( "ammo_357" ); +//#endif +// +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// // gauss +// UTIL_PrecacheOtherWeapon( "weapon_gauss" ); +// UTIL_PrecacheOther( "ammo_gaussclip" ); +//#endif +// +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// // rpg +// UTIL_PrecacheOtherWeapon( "weapon_rpg" ); +// UTIL_PrecacheOther( "ammo_rpgclip" ); +//#endif +// +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// // crossbow +// UTIL_PrecacheOtherWeapon( "weapon_crossbow" ); +// UTIL_PrecacheOther( "ammo_crossbow" ); +// UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons +//#endif +// +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// // egon +// UTIL_PrecacheOtherWeapon( "weapon_egon" ); +//#endif +// +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// // satchel charge +// UTIL_PrecacheOtherWeapon( "weapon_satchel" ); +//#endif +// +// // hand grenade +// UTIL_PrecacheOtherWeapon("weapon_handgrenade"); +// +// +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// // squeak grenade +// UTIL_PrecacheOtherWeapon( "weapon_snark" ); +//#endif +// +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// // hornetgun +// UTIL_PrecacheOtherWeapon( "weapon_hornetgun" ); +//#endif + + +//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) +// if ( g_pGameRules->IsDeathmatch() ) +// { +// UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons +// } +//#endif + + g_sModelIndexFireball = PRECACHE_UNMODIFIED_MODEL ("sprites/zerogxplode.spr");// fireball + g_sModelIndexWExplosion = PRECACHE_UNMODIFIED_MODEL ("sprites/WXplo1.spr");// underwater fireball + g_sModelIndexSmoke = PRECACHE_UNMODIFIED_MODEL ("sprites/steam1.spr");// smoke + g_sModelIndexBubbles = PRECACHE_UNMODIFIED_MODEL ("sprites/bubble2.spr");//bubbles + g_sModelIndexBloodSpray = PRECACHE_UNMODIFIED_MODEL ("sprites/bloodspray.spr"); // initial blood + g_sModelIndexBloodDrop = PRECACHE_UNMODIFIED_MODEL ("sprites/blood.spr"); // splattered blood + + g_sModelIndexLaser = PRECACHE_UNMODIFIED_MODEL( (char *)g_pModelNameLaser ); + g_sModelIndexLaserDot = PRECACHE_UNMODIFIED_MODEL("sprites/laserdot.spr"); + + + // used by explosions + PRECACHE_UNMODIFIED_MODEL ("models/grenade.mdl"); + PRECACHE_UNMODIFIED_MODEL ("sprites/explode1.spr"); + + PRECACHE_SOUND ("weapons/debris1.wav");// explosion aftermaths + PRECACHE_SOUND ("weapons/debris2.wav");// explosion aftermaths + PRECACHE_SOUND ("weapons/debris3.wav");// explosion aftermaths + + PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound1); + PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound2); + PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound3); + PRECACHE_UNMODIFIED_SOUND (kGRHitSound); + + PRECACHE_UNMODIFIED_SOUND ("weapons/bullet_hit1.wav"); // hit by bullet + PRECACHE_UNMODIFIED_SOUND ("weapons/bullet_hit2.wav"); // hit by bullet + + PRECACHE_UNMODIFIED_SOUND ("items/weapondrop1.wav");// weapon falls to the ground + +} + + + + +TYPEDESCRIPTION CBasePlayerItem::m_SaveData[] = +{ + DEFINE_FIELD( CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR ), + DEFINE_FIELD( CBasePlayerItem, m_pNext, FIELD_CLASSPTR ), + DEFINE_FIELD( CBasePlayerItem, m_iId, FIELD_INTEGER ), +}; +IMPLEMENT_SAVERESTORE( CBasePlayerItem, CBaseAnimating ); + + +TYPEDESCRIPTION CBasePlayerWeapon::m_SaveData[] = +{ + DEFINE_FIELD( CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_TIME ), + DEFINE_FIELD( CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_TIME ), + DEFINE_FIELD( CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_TIME ), + DEFINE_FIELD( CBasePlayerWeapon, m_iPrimaryAmmoType, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayerWeapon, m_iSecondaryAmmoType, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayerWeapon, m_iClip, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayerWeapon, m_iDefaultAmmo, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CBasePlayerWeapon, CBasePlayerItem ); + + +void CBasePlayerItem :: SetObjectCollisionBox( void ) +{ + pev->absmin = pev->origin + Vector(-24, -24, 0); + pev->absmax = pev->origin + Vector(24, 24, 16); +} + +BOOL +CBasePlayerItem::CanDeploy( void ) +{ + return TRUE; +} + +// can this weapon be put away right now? +BOOL CBasePlayerItem::CanHolster( void ) +{ + return TRUE; +}; + +// returns is deploy was successful +BOOL CBasePlayerItem::Deploy( ) +{ + return TRUE; +} + +BOOL +CBasePlayerItem::IsUseable( void ) +{ + return TRUE; +} + + +//========================================================= +// Sets up movetype, size, solidtype for a new weapon. +//========================================================= +void CBasePlayerItem :: FallInit( void ) +{ + pev->movetype = MOVETYPE_TOSS; + pev->solid = SOLID_BBOX; + + UTIL_SetOrigin( pev, pev->origin ); + UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0) );//pointsize until it lands on the ground. + + SetTouch(&CBasePlayerItem::DefaultTouch ); + SetThink(&CBasePlayerItem::FallThink ); + + pev->nextthink = gpGlobals->time + 0.1; +} + +//========================================================= +// FallThink - Items that have just spawned run this think +// to catch them when they hit the ground. Once we're sure +// that the object is grounded, we change its solid type +// to trigger and set it in a large box that helps the +// player get it. +//========================================================= +void CBasePlayerItem::FallThink ( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; + + if ( pev->flags & FL_ONGROUND ) + { + // clatter if we have an owner (i.e., dropped by someone) + // don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!) + if ( !FNullEnt( pev->owner ) ) + { + int pitch = 95 + RANDOM_LONG(0,29); + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "items/weapondrop1.wav", 1, ATTN_NORM, 0, pitch); + } + + // lie flat + pev->angles.x = 0; + pev->angles.z = 0; + + Materialize(); + } +} + +//========================================================= +// Materialize - make a CBasePlayerItem visible and tangible +//========================================================= +void CBasePlayerItem::Materialize( void ) +{ + if ( pev->effects & EF_NODRAW ) + { + // changing from invisible state to visible. + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 ); + pev->effects &= ~EF_NODRAW; + pev->effects |= EF_MUZZLEFLASH; + } + + pev->solid = SOLID_TRIGGER; + + UTIL_SetOrigin( pev, pev->origin );// link into world. + SetTouch (&CBasePlayerItem::DefaultTouch); + SetThink (NULL); + + this->VirtualMaterialize(); +} + +//========================================================= +// AttemptToMaterialize - the item is trying to rematerialize, +// should it do so now or wait longer? +//========================================================= +void CBasePlayerItem::AttemptToMaterialize( void ) +{ + float time = g_pGameRules->FlWeaponTryRespawn( this ); + + if ( time == 0 ) + { + Materialize(); + return; + } + + pev->nextthink = gpGlobals->time + time; +} + +//========================================================= +// CheckRespawn - a player is taking this weapon, should +// it respawn? +//========================================================= +void CBasePlayerItem :: CheckRespawn ( void ) +{ + switch ( g_pGameRules->WeaponShouldRespawn( this ) ) + { + case GR_WEAPON_RESPAWN_YES: + Respawn(); + break; + case GR_WEAPON_RESPAWN_NO: + return; + break; + } +} + +//========================================================= +// Respawn- this item is already in the world, but it is +// invisible and intangible. Make it visible and tangible. +//========================================================= +CBaseEntity* CBasePlayerItem::Respawn( void ) +{ + // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code + // will decide when to make the weapon visible and touchable. + CBaseEntity *pNewWeapon = CBaseEntity::Create( (char *)STRING( pev->classname ), g_pGameRules->VecWeaponRespawnSpot( this ), pev->angles, pev->owner ); + + if ( pNewWeapon ) + { + pNewWeapon->pev->effects |= EF_NODRAW;// invisible for now + pNewWeapon->SetTouch( NULL );// no touch + pNewWeapon->SetThink(&CBasePlayerItem::AttemptToMaterialize ); + + DROP_TO_FLOOR ( ENT(pev) ); + + // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, + // but when it should respawn is based on conditions belonging to the weapon that was taken. + pNewWeapon->pev->nextthink = g_pGameRules->FlWeaponRespawnTime( this ); + } + else + { + ALERT ( at_console, "Respawn failed to create %s!\n", STRING( pev->classname ) ); + } + + return pNewWeapon; +} + +void CBasePlayerItem::DefaultTouch( CBaseEntity *pOther ) +{ + // if it's not a player, ignore + if ( !pOther->IsPlayer() ) + return; + + CBasePlayer *pPlayer = (CBasePlayer *)pOther; + + // can I have this? + if ( !g_pGameRules->CanHavePlayerItem( pPlayer, this ) ) + { + if ( gEvilImpulse101 ) + { + UTIL_Remove( this ); + } + return; + } + + if (pOther->AddPlayerItem( this )) + { + AttachToPlayer( pPlayer ); + + AvHPlayer* thePlayer = dynamic_cast(pPlayer); + if(thePlayer && thePlayer->GetIsAlien()) + { + EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2-a.wav", 1, ATTN_NORM); + } + else + { + EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM); + } + } + + SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // UNDONE: when should this happen? +} + +BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted ) +{ + if ( !isPredicted ) + { + return ( attack_time <= curtime ) ? TRUE : FALSE; + } + else + { + return ( attack_time <= 0.0 ) ? TRUE : FALSE; + } +} + + +void CBasePlayerWeapon::ItemPostFrame( void ) +{ + bool theAttackPressed = (m_pPlayer->pev->button & IN_ATTACK) && !(m_pPlayer->pev->button & IN_ATTACK2); + + bool theWeaponPrimes = (this->GetWeaponPrimeTime() > 0.0f); + bool theWeaponIsPriming = this->GetIsWeaponPriming(); + bool theWeaponIsPrimed = this->GetIsWeaponPrimed(); + + if ((m_fInReload) && ( m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase() ) ) + { + // complete the reload. + int j = min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); + + // Add them to the clip + m_iClip += j; + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; + + m_pPlayer->TabulateAmmo(); + + m_fInReload = FALSE; + } + +/* // +movement: Removed case for +attack2 since it's used for movement abilities + if ((m_pPlayer->pev->button & IN_ATTACK2) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, UseDecrement() ) ) + { + if (m_pPlayer->GetCanUseWeapon()) + { + + if ( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] ) + { + m_fFireOnEmpty = TRUE; + } + + m_pPlayer->TabulateAmmo(); + SecondaryAttack(); + m_pPlayer->pev->button &= ~IN_ATTACK2; + } + } + else +*/ + if ( theAttackPressed && CanAttack( m_flNextPrimaryAttack, gpGlobals->time, UseDecrement() ) ) + { + if (m_pPlayer->GetCanUseWeapon()) + { + if ( (m_iClip == 0 && pszAmmo1()) || (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) ) + { + m_fFireOnEmpty = TRUE; + } + + m_pPlayer->TabulateAmmo(); + PrimaryAttack(); + } + } + else if ( m_pPlayer->pev->button & IN_RELOAD && iMaxClip() != WEAPON_NOCLIP && !m_fInReload ) + { + if (m_pPlayer->GetCanUseWeapon()) + { + // reload when reload is pressed, or if no buttons are down and weapon is empty. + Reload(); + } + } + // +movement: Removed case for +attack2 + else if ( !(m_pPlayer->pev->button & (IN_ATTACK /* |IN_ATTACK2 */) ) ) + { + if (m_pPlayer->GetCanUseWeapon()) + { + + // no fire buttons down + + m_fFireOnEmpty = FALSE; + + if ( !IsUseable() && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) + { + // weapon isn't useable, switch. + if ( !(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && g_pGameRules->GetNextBestWeapon( m_pPlayer, this ) ) + { + m_flNextPrimaryAttack = ( UseDecrement() ? 0.0 : gpGlobals->time ) + 0.3; + return; + } + } + else + { + // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) + { + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0) + { + Reload(); + return; + } + } + } + + WeaponIdle( ); + } + return; + } + + // catch all + if ( ShouldWeaponIdle() ) + { + WeaponIdle(); + } +} + +void CBasePlayerItem::DestroyItem( void ) +{ + //KGP: this is a virtual function call for a reason... + // it had been replaced with the contents of + // CBasePlayerItem::VirtualDestroyItem, ignoring + // AvHBasePlayerWeapon::VirtualDestroyItem in 3.01 + this->VirtualDestroyItem(); +} + +void CBasePlayerItem::VirtualDestroyItem( void ) +{ + if ( m_pPlayer ) + { + // if attached to a player, remove. + m_pPlayer->RemovePlayerItem( this ); + } + + Kill( ); +} + +int CBasePlayerItem::AddToPlayer( CBasePlayer *pPlayer ) +{ + m_pPlayer = pPlayer; + pPlayer->m_iHideHUD &= ~HIDEHUD_WEAPONS; + return TRUE; +} + +void CBasePlayerItem::Drop( void ) +{ + SetTouch( NULL ); + SetThink(&CBasePlayerItem::SUB_Remove); + pev->nextthink = gpGlobals->time + .1; +} + +void CBasePlayerItem::Kill( void ) +{ + SetTouch( NULL ); + SetThink(&CBasePlayerItem::SUB_Remove); + pev->nextthink = gpGlobals->time + .1; +} + +void CBasePlayerItem::Holster( int skiplocal /* = 0 */ ) +{ + m_pPlayer->pev->viewmodel = 0; + m_pPlayer->pev->weaponmodel = 0; +} + +void CBasePlayerItem::AttachToPlayer ( CBasePlayer *pPlayer ) +{ + pev->movetype = MOVETYPE_FOLLOW; + pev->solid = SOLID_NOT; + pev->aiment = pPlayer->edict(); + pev->effects = EF_NODRAW; // ?? + pev->modelindex = 0;// server won't send down to clients if modelindex == 0 + pev->model = iStringNull; + pev->owner = pPlayer->edict(); + pev->nextthink = gpGlobals->time + .1; + SetTouch( NULL ); +} + +void CBasePlayerItem::Spawn() +{ + CBaseAnimating::Spawn(); +} + +// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal +int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal ) +{ + if ( m_iDefaultAmmo ) + { + return ExtractAmmo( (CBasePlayerWeapon *)pOriginal ); + } + else + { + // a dead player dropped this. + return ExtractClipAmmo( (CBasePlayerWeapon *)pOriginal ); + } +} + + +int CBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer ) +{ + int bResult = CBasePlayerItem::AddToPlayer( pPlayer ); + + pPlayer->pev->weapons |= (1<GetAmmoIndex( pszAmmo1() ); + m_iSecondaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo2() ); + } + + + if (bResult) + return AddWeapon( ); + return FALSE; +} + +int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) +{ + BOOL bSend = FALSE; + int state = 0; + + // KGP: folded m_iEnabled into the state value and converted it to a proper bitfield. + if( pPlayer->m_pActiveItem == this ) + { state |= WEAPON_IS_CURRENT; } + if( pPlayer->m_fOnTarget ) + { state |= WEAPON_ON_TARGET; } + if( m_iEnabled ) + { state |= WEAPON_IS_ENABLED; } + + // Forcing send of all data! + if ( !pPlayer->m_fWeapon ) + { + bSend = TRUE; + } + + // This is the current or last weapon, so the state will need to be updated + if ( this == pPlayer->m_pActiveItem || + this == pPlayer->m_pClientActiveItem ) + { + if ( pPlayer->m_pActiveItem != pPlayer->m_pClientActiveItem ) + { + bSend = TRUE; + } + } + + // If the ammo, state, or fov has changed, update the weapon + if ( m_iClip != m_iClientClip || + state != m_iClientWeaponState || + pPlayer->m_iFOV != pPlayer->m_iClientFOV ) + { + bSend = TRUE; + } + + if (m_iId == 22 || m_iId == 11 || m_iId == 21) + gCanMove[pPlayer->entindex() - 1] = m_iEnabled; + + if ( bSend ) + { + NetMsg_CurWeapon( pPlayer->pev, state, m_iId, m_iClip ); + m_iClientClip = m_iClip; + m_iClientWeaponState = state; + pPlayer->m_fWeapon = TRUE; + } + + if ( m_pNext ) + m_pNext->UpdateClientData( pPlayer ); + + return 1; +} + +void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body ) +{ + if(iAnim >= 0) + { + if ( UseDecrement() ) + skiplocal = 1; + else + skiplocal = 0; + + m_pPlayer->pev->weaponanim = iAnim; + + if ( skiplocal && ENGINE_CANSKIP( m_pPlayer->edict() ) ) + return; + + MESSAGE_BEGIN( MSG_ONE, SVC_WEAPONANIM, NULL, m_pPlayer->pev ); + WRITE_BYTE( iAnim ); // sequence number + WRITE_BYTE( pev->body ); // weaponmodel bodygroup. + MESSAGE_END(); + } +} + +BOOL CBasePlayerWeapon :: AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry ) +{ + int iIdAmmo; + + if (iMaxClip < 1) + { + m_iClip = -1; + iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry ); + } + else if (m_iClip == 0) + { + int i; + i = min( m_iClip + iCount, iMaxClip ) - m_iClip; + m_iClip += i; + iIdAmmo = m_pPlayer->GiveAmmo( iCount - i, szName, iMaxCarry ); + } + else + { + iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry ); + } + + // m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = iMaxCarry; // hack for testing + + if (iIdAmmo > 0) + { + m_iPrimaryAmmoType = iIdAmmo; + if (m_pPlayer->HasPlayerItem( this ) ) + { + // play the "got ammo" sound only if we gave some ammo to a player that already had this gun. + // if the player is just getting this gun for the first time, DefaultTouch will play the "picked up gun" sound for us. + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + } + } + + return iIdAmmo > 0 ? TRUE : FALSE; +} + + +BOOL CBasePlayerWeapon :: AddSecondaryAmmo( int iCount, char *szName, int iMax ) +{ + int iIdAmmo; + + iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMax ); + + //m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] = iMax; // hack for testing + + if (iIdAmmo > 0) + { + m_iSecondaryAmmoType = iIdAmmo; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + } + return iIdAmmo > 0 ? TRUE : FALSE; +} + +//========================================================= +// IsUseable - this function determines whether or not a +// weapon is useable by the player in its current state. +// (does it have ammo loaded? do I have any ammo for the +// weapon?, etc) +//========================================================= +BOOL CBasePlayerWeapon :: IsUseable( void ) +{ + if ( m_iClip <= 0 ) + { + // This looks like a nasty bug Valve didn't notice + ASSERT(PrimaryAmmoIndex() >= 0); + + if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] <= 0 && iMaxAmmo1() != -1 ) + { + // clip is empty (or nonexistant) and the player has no more ammo of this type. + return FALSE; + } + } + + return TRUE; +} + +BOOL CBasePlayerWeapon :: CanDeploy( void ) +{ + BOOL bHasAmmo = 0; + + // All weapons can always deploy. Once fire is hit, it checks if it's out of ammo. If so, the weapon switches. + // This is needed so ammo packs function correctly, and it also feels more responsive. + +// if ( !pszAmmo1() ) +// { +// // this weapon doesn't use ammo, can always deploy. +// return TRUE; +// } +// +// if ( pszAmmo1() ) +// { +// bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] != 0); +// } +// if ( pszAmmo2() ) +// { +// bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] != 0); +// } +// if (m_iClip > 0) +// { +// bHasAmmo |= 1; +// } +// if (!bHasAmmo) +// { +// return FALSE; +// } + + return TRUE; +} + +BOOL CBasePlayerWeapon :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal, int body) +{ + if (!CanDeploy( )) + return FALSE; + + m_pPlayer->TabulateAmmo(); + m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); + m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); + strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); + SendWeaponAnim( iAnim, skiplocal, body ); + + // Set the player animation as well + //this->m_pPlayer->SetAnimation(PLAYER_ANIM(iAnim)); + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + this->GetDeployTime(); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + this->GetDeployTime() + kDeployIdleInterval; + + return TRUE; +} + + +BOOL CBasePlayerWeapon :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body) +{ + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + return FALSE; + + int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); + + if (j == 0) + return FALSE; + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay; + + //!!UNDONE -- reload sound goes here !!! + //SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 ); + + m_fInReload = TRUE; + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + kDeployIdleInterval; + + return TRUE; +} + +BOOL CBasePlayerWeapon :: PlayEmptySound( void ) +{ + if (m_iPlayEmptySound) + { +// EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM); + + int flags = FEV_NOTHOST; + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), gEmptySoundEventID, 0.0, (float *)&(m_pPlayer->pev->origin), (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); + + m_iPlayEmptySound = 0; + return 0; + } + return 0; +} + +void CBasePlayerWeapon :: ResetEmptySound( void ) +{ + m_iPlayEmptySound = 1; +} + +//========================================================= +//========================================================= +int CBasePlayerWeapon::PrimaryAmmoIndex( void ) +{ + return m_iPrimaryAmmoType; +} + +//========================================================= +//========================================================= +int CBasePlayerWeapon::SecondaryAmmoIndex( void ) +{ + return -1; +} + +void CBasePlayerWeapon::Holster( int skiplocal /* = 0 */ ) +{ + m_fInReload = FALSE; // cancel any reload in progress. + m_pPlayer->pev->viewmodel = 0; + m_pPlayer->pev->weaponmodel = 0; +} + +void CBasePlayerAmmo::Spawn( void ) +{ + pev->movetype = MOVETYPE_TOSS; + pev->solid = SOLID_TRIGGER; + UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16)); + UTIL_SetOrigin( pev, pev->origin ); + + SetTouch(&CBasePlayerAmmo::DefaultTouch ); +} + +CBaseEntity* CBasePlayerAmmo::Respawn( void ) +{ + pev->effects |= EF_NODRAW; + SetTouch( NULL ); + + UTIL_SetOrigin( pev, g_pGameRules->VecAmmoRespawnSpot( this ) );// move to wherever I'm supposed to repawn. + + SetThink(&CBasePlayerAmmo::Materialize ); + pev->nextthink = g_pGameRules->FlAmmoRespawnTime( this ); + + return this; +} + +void CBasePlayerAmmo::Materialize( void ) +{ + if ( pev->effects & EF_NODRAW ) + { + // changing from invisible state to visible. + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 ); + pev->effects &= ~EF_NODRAW; + pev->effects |= EF_MUZZLEFLASH; + } + + SetTouch(&CBasePlayerAmmo::DefaultTouch ); +} + +void CBasePlayerAmmo :: DefaultTouch( CBaseEntity *pOther ) +{ + if ( !pOther->IsPlayer() ) + { + return; + } + + if (AddAmmo( pOther )) + { + if ( g_pGameRules->AmmoShouldRespawn( this ) == GR_AMMO_RESPAWN_YES ) + { + Respawn(); + } + else + { + SetTouch( NULL ); + SetThink(&CBasePlayerAmmo::SUB_Remove); + pev->nextthink = gpGlobals->time + .1; + } + } + else if (gEvilImpulse101) + { + // evil impulse 101 hack, kill always + SetTouch( NULL ); + SetThink(&CBasePlayerAmmo::SUB_Remove); + pev->nextthink = gpGlobals->time + .1; + } +} + +//========================================================= +// called by the new item with the existing item as parameter +// +// if we call ExtractAmmo(), it's because the player is picking up this type of weapon for +// the first time. If it is spawned by the world, m_iDefaultAmmo will have a default ammo amount in it. +// if this is a weapon dropped by a dying player, has 0 m_iDefaultAmmo, which means only the ammo in +// the weapon clip comes along. +//========================================================= +int CBasePlayerWeapon::ExtractAmmo( CBasePlayerWeapon *pWeapon ) +{ + int iReturn; + + if ( pszAmmo1() != NULL ) + { + // blindly call with m_iDefaultAmmo. It's either going to be a value or zero. If it is zero, + // we only get the ammo in the weapon's clip, which is what we want. + iReturn = pWeapon->AddPrimaryAmmo( m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1() ); + m_iDefaultAmmo = 0; + } + + if ( pszAmmo2() != NULL ) + { + iReturn = pWeapon->AddSecondaryAmmo( 0, (char *)pszAmmo2(), iMaxAmmo2() ); + } + + return iReturn; +} + +//========================================================= +// called by the new item's class with the existing item as parameter +//========================================================= +int CBasePlayerWeapon::ExtractClipAmmo( CBasePlayerWeapon *pWeapon ) +{ + int iAmmo; + + if ( m_iClip == WEAPON_NOCLIP ) + { + iAmmo = 0;// guns with no clips always come empty if they are second-hand + } + else + { + iAmmo = m_iClip; + } + + return pWeapon->m_pPlayer->GiveAmmo( iAmmo, (char *)pszAmmo1(), iMaxAmmo1() ); // , &m_iPrimaryAmmoType +} + +//========================================================= +// RetireWeapon - no more ammo for this gun, put it away. +//========================================================= +void CBasePlayerWeapon::RetireWeapon( void ) +{ + // first, no viewmodel at all. + m_pPlayer->pev->viewmodel = iStringNull; + m_pPlayer->pev->weaponmodel = iStringNull; + //m_pPlayer->pev->viewmodelindex = NULL; + + g_pGameRules->GetNextBestWeapon( m_pPlayer, this ); +} + +//********************************************************* +// weaponbox code: +//********************************************************* + +LINK_ENTITY_TO_CLASS( weaponbox, CWeaponBox ); + +TYPEDESCRIPTION CWeaponBox::m_SaveData[] = +{ + DEFINE_ARRAY( CWeaponBox, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ), + DEFINE_ARRAY( CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS ), + DEFINE_ARRAY( CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ), + DEFINE_FIELD( CWeaponBox, m_cAmmoTypes, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CWeaponBox, CBaseEntity ); + +//========================================================= +// +//========================================================= +void CWeaponBox::Precache( void ) +{ + PRECACHE_UNMODIFIED_MODEL("models/w_weaponbox.mdl"); +} + +//========================================================= +//========================================================= +void CWeaponBox :: KeyValue( KeyValueData *pkvd ) +{ + if ( m_cAmmoTypes < MAX_AMMO_SLOTS ) + { + PackAmmo( ALLOC_STRING(pkvd->szKeyName), atoi(pkvd->szValue) ); + m_cAmmoTypes++;// count this new ammo type. + + pkvd->fHandled = TRUE; + } + else + { + ALERT ( at_console, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS ); + } +} + +//========================================================= +// CWeaponBox - Spawn +//========================================================= +void CWeaponBox::Spawn( void ) +{ + Precache( ); + + pev->movetype = MOVETYPE_TOSS; + pev->solid = SOLID_TRIGGER; + + UTIL_SetSize( pev, g_vecZero, g_vecZero ); + + SET_MODEL( ENT(pev), "models/w_weaponbox.mdl"); +} + +//========================================================= +// CWeaponBox - Kill - the think function that removes the +// box from the world. +//========================================================= +void CWeaponBox::Kill( void ) +{ + CBasePlayerItem *pWeapon; + int i; + + // destroy the weapons + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + pWeapon = m_rgpPlayerItems[ i ]; + + while ( pWeapon ) + { + pWeapon->SetThink(&CWeaponBox::SUB_Remove); + pWeapon->pev->nextthink = gpGlobals->time + 0.1; + pWeapon = pWeapon->m_pNext; + } + } + + // remove the box + UTIL_Remove( this ); +} + +//========================================================= +// CWeaponBox - Touch: try to add my contents to the toucher +// if the toucher is a player. +//========================================================= +void CWeaponBox::Touch( CBaseEntity *pOther ) +{ + if ( !(pev->flags & FL_ONGROUND ) ) + { + return; + } + + if ( !pOther->IsPlayer() ) + { + // only players may touch a weaponbox. + return; + } + + if ( !pOther->IsAlive() ) + { + // no dead guys. + return; + } + + CBasePlayer *pPlayer = (CBasePlayer *)pOther; + int i; + +// dole out ammo + for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) + { + if ( !FStringNull( m_rgiszAmmo[ i ] ) ) + { + // there's some ammo of this type. + pPlayer->GiveAmmo( m_rgAmmo[ i ], (char *)STRING( m_rgiszAmmo[ i ] ), MaxAmmoCarry( m_rgiszAmmo[ i ] ) ); + + //ALERT ( at_console, "Gave %d rounds of %s\n", m_rgAmmo[i], STRING(m_rgiszAmmo[i]) ); + + // now empty the ammo from the weaponbox since we just gave it to the player + m_rgiszAmmo[ i ] = iStringNull; + m_rgAmmo[ i ] = 0; + } + } + +// go through my weapons and try to give the usable ones to the player. +// it's important the the player be given ammo first, so the weapons code doesn't refuse +// to deploy a better weapon that the player may pick up because he has no ammo for it. + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + if ( m_rgpPlayerItems[ i ] ) + { + CBasePlayerItem *pItem; + + // have at least one weapon in this slot + while ( m_rgpPlayerItems[ i ] ) + { + //ALERT ( at_console, "trying to give %s\n", STRING( m_rgpPlayerItems[ i ]->pev->classname ) ); + + pItem = m_rgpPlayerItems[ i ]; + m_rgpPlayerItems[ i ] = m_rgpPlayerItems[ i ]->m_pNext;// unlink this weapon from the box + + if ( pPlayer->AddPlayerItem( pItem ) ) + { + pItem->AttachToPlayer( pPlayer ); + } + } + } + } + + EMIT_SOUND( pOther->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM ); + SetTouch(NULL); + UTIL_Remove(this); +} + +//========================================================= +// CWeaponBox - PackWeapon: Add this weapon to the box +//========================================================= +BOOL CWeaponBox::PackWeapon( CBasePlayerItem *pWeapon ) +{ + // is one of these weapons already packed in this box? + if ( HasWeapon( pWeapon ) ) + { + return FALSE;// box can only hold one of each weapon type + } + + if ( pWeapon->m_pPlayer ) + { + if ( !pWeapon->m_pPlayer->RemovePlayerItem( pWeapon ) ) + { + // failed to unhook the weapon from the player! + return FALSE; + } + } + + int iWeaponSlot = pWeapon->iItemSlot(); + + if ( m_rgpPlayerItems[ iWeaponSlot ] ) + { + // there's already one weapon in this slot, so link this into the slot's column + pWeapon->m_pNext = m_rgpPlayerItems[ iWeaponSlot ]; + m_rgpPlayerItems[ iWeaponSlot ] = pWeapon; + } + else + { + // first weapon we have for this slot + m_rgpPlayerItems[ iWeaponSlot ] = pWeapon; + pWeapon->m_pNext = NULL; + } + + pWeapon->pev->spawnflags |= SF_NORESPAWN;// never respawn + pWeapon->pev->movetype = MOVETYPE_NONE; + pWeapon->pev->solid = SOLID_NOT; + pWeapon->pev->effects = EF_NODRAW; + pWeapon->pev->modelindex = 0; + pWeapon->pev->model = iStringNull; + pWeapon->pev->owner = edict(); + pWeapon->SetThink( NULL );// crowbar may be trying to swing again, etc. + pWeapon->SetTouch( NULL ); + pWeapon->m_pPlayer = NULL; + + //ALERT ( at_console, "packed %s\n", STRING(pWeapon->pev->classname) ); + + return TRUE; +} + +//========================================================= +// CWeaponBox - PackAmmo +//========================================================= +BOOL CWeaponBox::PackAmmo( int iszName, int iCount ) +{ + int iMaxCarry; + + if ( FStringNull( iszName ) ) + { + // error here + ALERT ( at_console, "NULL String in PackAmmo!\n" ); + return FALSE; + } + + iMaxCarry = MaxAmmoCarry( iszName ); + + if ( iMaxCarry != -1 && iCount > 0 ) + { + //ALERT ( at_console, "Packed %d rounds of %s\n", iCount, STRING(iszName) ); + GiveAmmo( iCount, (char *)STRING( iszName ), iMaxCarry ); + return TRUE; + } + + return FALSE; +} + +//========================================================= +// CWeaponBox - GiveAmmo +//========================================================= +int CWeaponBox::GiveAmmo( int iCount, char *szName, int iMax, int *pIndex/* = NULL*/ ) +{ + int i; + + for (i = 1; i < MAX_AMMO_SLOTS && !FStringNull( m_rgiszAmmo[i] ); i++) + { + if (stricmp( szName, STRING( m_rgiszAmmo[i])) == 0) + { + if (pIndex) + *pIndex = i; + + int iAdd = min( iCount, iMax - m_rgAmmo[i]); + if (iCount == 0 || iAdd > 0) + { + m_rgAmmo[i] += iAdd; + + return i; + } + return -1; + } + } + if (i < MAX_AMMO_SLOTS) + { + if (pIndex) + *pIndex = i; + + m_rgiszAmmo[i] = MAKE_STRING( szName ); + m_rgAmmo[i] = iCount; + + return i; + } + ALERT( at_console, "out of named ammo slots\n"); + return i; +} + +//========================================================= +// CWeaponBox::HasWeapon - is a weapon of this type already +// packed in this box? +//========================================================= +BOOL CWeaponBox::HasWeapon( CBasePlayerItem *pCheckItem ) +{ + CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()]; + + while (pItem) + { + if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) )) + { + return TRUE; + } + pItem = pItem->m_pNext; + } + + return FALSE; +} + +//========================================================= +// CWeaponBox::IsEmpty - is there anything in this box? +//========================================================= +BOOL CWeaponBox::IsEmpty( void ) +{ + int i; + + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + if ( m_rgpPlayerItems[ i ] ) + { + return FALSE; + } + } + + for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) + { + if ( !FStringNull( m_rgiszAmmo[ i ] ) ) + { + // still have a bit of this type of ammo + return FALSE; + } + } + + return TRUE; +} + +//========================================================= +//========================================================= +void CWeaponBox::SetObjectCollisionBox( void ) +{ + pev->absmin = pev->origin + Vector(-16, -16, 0); + pev->absmax = pev->origin + Vector(16, 16, 16); +} + + +void CBasePlayerWeapon::PrintState( void ) +{ + ALERT( at_console, "primary: %f\n", m_flNextPrimaryAttack ); + ALERT( at_console, "idle : %f\n", m_flTimeWeaponIdle ); + +// ALERT( at_console, "nextrl : %f\n", m_flNextReload ); +// ALERT( at_console, "nextpum: %f\n", m_flPumpTime ); + +// ALERT( at_console, "m_frt : %f\n", m_fReloadTime ); + ALERT( at_console, "m_finre: %i\n", m_fInReload ); +// ALERT( at_console, "m_finsr: %i\n", m_fInSpecialReload ); + + ALERT( at_console, "m_iclip: %i\n", m_iClip ); +} + + +TYPEDESCRIPTION CRpg::m_SaveData[] = +{ + DEFINE_FIELD( CRpg, m_fSpotActive, FIELD_INTEGER ), + DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ), +}; +IMPLEMENT_SAVERESTORE( CRpg, CBasePlayerWeapon ); + +TYPEDESCRIPTION CRpgRocket::m_SaveData[] = +{ + DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ), + DEFINE_FIELD( CRpgRocket, m_pLauncher, FIELD_CLASSPTR ), +}; +IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade ); + +TYPEDESCRIPTION CShotgun::m_SaveData[] = +{ + DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ), + DEFINE_FIELD( CShotgun, m_fInSpecialReload, FIELD_INTEGER ), + DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ), + // DEFINE_FIELD( CShotgun, m_iShell, FIELD_INTEGER ), + DEFINE_FIELD( CShotgun, m_flPumpTime, FIELD_TIME ), +}; +IMPLEMENT_SAVERESTORE( CShotgun, CBasePlayerWeapon ); + +TYPEDESCRIPTION CGauss::m_SaveData[] = +{ + DEFINE_FIELD( CGauss, m_fInAttack, FIELD_INTEGER ), +// DEFINE_FIELD( CGauss, m_flStartCharge, FIELD_TIME ), +// DEFINE_FIELD( CGauss, m_flPlayAftershock, FIELD_TIME ), +// DEFINE_FIELD( CGauss, m_flNextAmmoBurn, FIELD_TIME ), + DEFINE_FIELD( CGauss, m_fPrimaryFire, FIELD_BOOLEAN ), +}; +IMPLEMENT_SAVERESTORE( CGauss, CBasePlayerWeapon ); + +TYPEDESCRIPTION CEgon::m_SaveData[] = +{ +// DEFINE_FIELD( CEgon, m_pBeam, FIELD_CLASSPTR ), +// DEFINE_FIELD( CEgon, m_pNoise, FIELD_CLASSPTR ), +// DEFINE_FIELD( CEgon, m_pSprite, FIELD_CLASSPTR ), + DEFINE_FIELD( CEgon, m_shootTime, FIELD_TIME ), + DEFINE_FIELD( CEgon, m_fireState, FIELD_INTEGER ), + DEFINE_FIELD( CEgon, m_fireMode, FIELD_INTEGER ), + DEFINE_FIELD( CEgon, m_shakeTime, FIELD_TIME ), + DEFINE_FIELD( CEgon, m_flAmmoUseTime, FIELD_TIME ), +}; +IMPLEMENT_SAVERESTORE( CEgon, CBasePlayerWeapon ); + +TYPEDESCRIPTION CSatchel::m_SaveData[] = +{ + DEFINE_FIELD( CSatchel, m_chargeReady, FIELD_INTEGER ), +}; +IMPLEMENT_SAVERESTORE( CSatchel, CBasePlayerWeapon ); + diff --git a/main/source/dlls/weapons.h b/main/source/dlls/weapons.h index 18f2be5..b315f6e 100644 --- a/main/source/dlls/weapons.h +++ b/main/source/dlls/weapons.h @@ -54,8 +54,8 @@ public: typedef enum { SATCHEL_DETONATE = 0, SATCHEL_RELEASE } SATCHELCODE; - static CGrenade *ShootExplosiveTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time ); - static CGrenade *ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time ); + static CGrenade *ShootExplosiveTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time, int inDamageType ); + static CGrenade *ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time); static CGrenade *ShootContact( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); static CGrenade *ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); static void UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code ); @@ -77,8 +77,11 @@ public: virtual void BounceSound( void ); virtual int BloodColor( void ) { return DONT_BLEED; } virtual void Killed( entvars_t *pevAttacker, int iGib ); - + virtual void SetDamageType(int inDamageType); + BOOL m_fRegisteredSound;// whether or not this grenade has issued its DANGER sound to the world sound list yet. +private: + int m_damageType; }; @@ -380,6 +383,8 @@ public: int m_iDefaultAmmo;// how much ammo you get when you pick up this weapon as placed by a level designer. + bool PrevAttack2Status; // HACK: For +movement weapon animations + float m_flLastAnimationPlayed; }; diff --git a/main/source/dlls/world.cpp b/main/source/dlls/world.cpp index 4783a7d..c0f4f2e 100644 --- a/main/source/dlls/world.cpp +++ b/main/source/dlls/world.cpp @@ -35,6 +35,7 @@ #include "gamerules.h" #include "teamplay_gamerules.h" #include "mod/AvHGamerules.h" +#include "mod/AvHServerVariables.h" #include "pm_shared/pm_defs.h" extern CGraph WorldGraph; @@ -480,7 +481,7 @@ void CWorld :: Spawn( void ) { g_fGameOver = FALSE; Precache( ); - g_flWeaponCheat = CVAR_GET_FLOAT( "sv_cheats" ); // Is the impulse 101 command allowed? + g_flWeaponCheat = ns_cvar_float(avh_cheats ); // Is the impulse 101 command allowed? } void CWorld :: Precache( void ) diff --git a/main/source/engine/cdll_int.h b/main/source/engine/cdll_int.h index 4312475..c5403ff 100644 --- a/main/source/engine/cdll_int.h +++ b/main/source/engine/cdll_int.h @@ -96,9 +96,17 @@ typedef struct hud_player_info_s char *model; short topcolor; short bottomcolor; - + char padding[8]; } hud_player_info_t; +#define MAX_ALIAS_NAME 32 +typedef struct cmdalias_s +{ + struct cmdalias_s *next; + char name[MAX_ALIAS_NAME]; + char *value; +} cmdalias_t; + // this is by no means complete, or even accurate typedef struct cl_enginefuncs_s @@ -251,6 +259,41 @@ typedef struct cl_enginefuncs_s void ( *pfnGetMousePos )( struct tagPOINT *ppt ); void ( *pfnSetMousePos )( int x, int y ); void ( *pfnSetMouseEnable )( qboolean fEnable ); + // missing functions from alfred reynolds. Return type and parameters unknown + void (* GetFirstCvarPtr)(void); + void (* GetFirstCmdFunctionHandle)(void); + void (* GetNextCmdFunctionHandle)(void); + void (* GetCmdFunctionName)(void); + void (* hudGetClientOldTime)(void); + void (* hudGetServerGravityValue)(void); + void (* hudGetModelByIndex)(void); + void (* pfnSetFilterMode)(void); + void (* pfnSetFilterColor)(void); + void (* pfnSetFilterBrightness)(void); + void (* pfnSequenceGet)(void); + void (* pfnSPR_DrawGeneric)(void); + void (* pfnSequencePickSentence)(void); + void (* pfnDrawString)(void); + void (* pfnDrawStringReverse)(void); + void (* LocalPlayerInfo_ValueForKey)(void); + void (* pfnVGUI2DrawCharacter)(void); + void (* pfnVGUI2DrawCharacterAdd)(void); + void (* COM_GetApproxWavePlayLength)(void); + void (* pfnGetCareerUI)(void); + void (* Cvar_Set)(void); + void (* pfnIsCareerMatch)(void); + void (* pfnPlaySoundVoiceByName)(void); + void (* pfnPrimeMusicStream)(void); + void (* GetAbsoluteTime)(void); + void (* pfnProcessTutorMessageDecayBuffer)(void); + void (* pfnConstructTutorMessageDecayBuffer)(void); + void (* pfnResetTutorMessageDecayData)(void); + void (* pfnPlaySoundByNameAtPitch)(void); + void (* pfnFillRGBABlend)(void); + void (* pfnGetAppID)(void); +// end: missing functions from alfred reynolds. Return type and parameters unknown + + cmdalias_t* (*pfnGetAliases) ( void ); } cl_enginefunc_t; #ifndef IN_BUTTONS_H diff --git a/main/source/engine/eiface.h b/main/source/engine/eiface.h index d3a54e1..4e103a6 100644 --- a/main/source/engine/eiface.h +++ b/main/source/engine/eiface.h @@ -260,6 +260,7 @@ typedef struct enginefuncs_s qboolean (*pfnVoice_SetClientListening)(int iReceiver, int iSender, qboolean bListen); const char* (*pfnGetPlayerAuthId) ( edict_t *e ); + void (*pfnQueryClientCvarValue2)( const edict_t *player, const char *cvarName, int requestID ); } enginefuncs_t; // ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 138 @@ -480,6 +481,8 @@ typedef struct void (*pfnOnFreeEntPrivateData)(edict_t *pEnt); void (*pfnGameShutdown)(void); int (*pfnShouldCollide)( edict_t *pentTouched, edict_t *pentOther ); + void (*pfnCvarValue)( const edict_t *pEnt, const char *value ); + void (*pfnCvarValue2)( const edict_t *pEnt, int requestID, const char *cvarName, const char *value ); } NEW_DLL_FUNCTIONS; typedef int (*NEW_DLL_FUNCTIONS_FN)( NEW_DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ); diff --git a/main/source/game_shared/teamconst.h b/main/source/game_shared/teamconst.h index 7214096..2ed0c3d 100644 --- a/main/source/game_shared/teamconst.h +++ b/main/source/game_shared/teamconst.h @@ -1,35 +1,35 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: teamconst.h $ -// $Date: 2002/07/23 16:49:42 $ -// -//------------------------------------------------------------------------------- -// $Log: teamconst.h,v $ -// Revision 1.2 2002/07/23 16:49:42 Flayra -// - Added document header, new player constant -// -//=============================================================================== -#ifndef TEAM_CONST_H -#define TEAM_CONST_H - -#define MAX_PLAYERS 32//voogru: Why was this 64?! - -#define MAX_PLAYERS_PER_TEAM 16 - -//#define MAX_TEAMS 64 -#define MAX_TEAMS 6 - -#define MAX_TEAM_NAME 16 - -#define MAX_TEAMNAME_LENGTH 16 - -#define TEAMPLAY_TEAMLISTLENGTH MAX_TEAMS*MAX_TEAMNAME_LENGTH - +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: teamconst.h $ +// $Date: 2002/07/23 16:49:42 $ +// +//------------------------------------------------------------------------------- +// $Log: teamconst.h,v $ +// Revision 1.2 2002/07/23 16:49:42 Flayra +// - Added document header, new player constant +// +//=============================================================================== +#ifndef TEAM_CONST_H +#define TEAM_CONST_H + +#define MAX_PLAYERS 32//: Why was this 64?! + +#define MAX_PLAYERS_PER_TEAM 16 + +//#define MAX_TEAMS 64 +#define MAX_TEAMS 6 + +#define MAX_TEAM_NAME 16 + +#define MAX_TEAMNAME_LENGTH 16 + +#define TEAMPLAY_TEAMLISTLENGTH MAX_TEAMS*MAX_TEAMNAME_LENGTH + #endif \ No newline at end of file diff --git a/main/source/game_shared/vgui_loadtga.cpp b/main/source/game_shared/vgui_loadtga.cpp index cea0050..bb3d219 100644 --- a/main/source/game_shared/vgui_loadtga.cpp +++ b/main/source/game_shared/vgui_loadtga.cpp @@ -1,100 +1,100 @@ -//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============ -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= - -#include "../cl_dll/wrect.h" -#include "../cl_dll/cl_dll.h" -#include "vgui.h" -#include "vgui_loadtga.h" -#include "vgui_inputstream.h" - - -//#include "ui/MemoryInputStream.h" - -// tankefugl: HACK -// Implemented old MemoryInputStream to work around bugs(?) in the other implementation or in -// the vgui's handling of the other one -class MemoryInputStream2 : public vgui::InputStream -{ -public: - MemoryInputStream2() - { - m_pData = NULL; - m_DataLen = m_ReadPos = 0; - } - - virtual void seekStart(bool& success) {m_ReadPos=0; success=true;} - virtual void seekRelative(int count,bool& success) {m_ReadPos+=count; success=true;} - virtual void seekEnd(bool& success) {m_ReadPos=m_DataLen; success=true;} - virtual int getAvailable(bool& success) {success=false; return 0;} // This is what vgui does for files... - - virtual uchar readUChar(bool& success) - { - if(m_ReadPos>=0 && m_ReadPos=0 && m_ReadPos #include "voice_banmgr.h" #include "dlls/extdll.h" - +#include "localassert.h" #define BANMGR_FILEVERSION 1 char const *g_pBanMgrFilename = "voice_ban.dt"; @@ -18,12 +18,12 @@ char const *g_pBanMgrFilename = "voice_ban.dt"; // Hash a player ID to a byte. unsigned char HashPlayerID(char const playerID[16]) { - unsigned char curHash = 0; + char curHash = 0; for(int i=0; i < 16; i++) - curHash += (unsigned char)(playerID[i] & 0xFF); + curHash += (char)(playerID[i]); - return curHash; + return (unsigned char)curHash; } @@ -169,7 +169,7 @@ void CVoiceBanMgr::Clear() CVoiceBanMgr::BannedPlayer* CVoiceBanMgr::InternalFindPlayerSquelch(char const playerID[16]) { - int index = HashPlayerID(playerID); + int index = (int)HashPlayerID(playerID) & 0xFF; BannedPlayer *pListHead = &m_PlayerHash[index]; for(BannedPlayer *pCur=pListHead->m_pNext; pCur != pListHead; pCur=pCur->m_pNext) { diff --git a/main/source/game_shared/voice_gamemgr.cpp b/main/source/game_shared/voice_gamemgr.cpp index d08f0a1..d96a5bf 100644 --- a/main/source/game_shared/voice_gamemgr.cpp +++ b/main/source/game_shared/voice_gamemgr.cpp @@ -12,7 +12,7 @@ #include "dlls/util.h" #include "dlls/cbase.h" #include "dlls/player.h" - +#include "mod/AvHServerVariables.h" #define UPDATE_INTERVAL 0.3 @@ -217,7 +217,7 @@ void CVoiceGameMgr::UpdateMasks() { m_UpdateInterval = 0; - bool bAllTalk = !!g_engfuncs.pfnCVarGetFloat( "sv_alltalk" ); + bool bAllTalk = !!ns_cvar_float(&sv_alltalk); for(int iClient=0; iClient < m_nMaxPlayers; iClient++) { diff --git a/main/source/game_shared/voice_status.cpp b/main/source/game_shared/voice_status.cpp index 88f7238..008f23c 100644 --- a/main/source/game_shared/voice_status.cpp +++ b/main/source/game_shared/voice_status.cpp @@ -1,880 +1,880 @@ -//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============ -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= - -// There are hud.h's coming out of the woodwork so this ensures that we get the right one. -#include "../cl_dll/hud.h" - -#include "../cl_dll/cl_util.h" -#include -#include -#include -#include "../cl_dll/hud_servers.h" -#include "../cl_dll/demo.h" -#include "common/demo_api.h" -#include "voice_status.h" -#include "common/r_efx.h" -#include "common/entity_types.h" -#include "VGUI_ActionSignal.h" -#include "VGUI_Scheme.h" -#include "VGUI_TextImage.h" -#include "vgui_loadtga.h" -#include "vgui_helpers.h" -#include "vgui_mousecode.h" -#include "dlls/extdll.h" -#include "mod/AvHClientUtil.h" -#include "mod/AvHHudConstants.h" - -using namespace vgui; - - -extern int cam_thirdperson; - - -#define VOICE_MODEL_INTERVAL 0.3 -#define SCOREBOARD_BLINK_FREQUENCY 0.3 // How often to blink the scoreboard icons. -#define SQUELCHOSCILLATE_PER_SECOND 2.0f - - -extern BitmapTGA *LoadTGA( const char* pImageName ); - -// Allow assignment within conditional -#pragma warning (disable: 4706) - -// ---------------------------------------------------------------------- // -// The voice manager for the client. -// ---------------------------------------------------------------------- // -CVoiceStatus g_VoiceStatus; - -CVoiceStatus* GetClientVoiceMgr() -{ - return &g_VoiceStatus; -} - - - -// ---------------------------------------------------------------------- // -// CVoiceStatus. -// ---------------------------------------------------------------------- // - -static CVoiceStatus *g_pInternalVoiceStatus = NULL; - -int __MsgFunc_VoiceMask(const char *pszName, int iSize, void *pbuf) -{ - if(g_pInternalVoiceStatus) - g_pInternalVoiceStatus->HandleVoiceMaskMsg(iSize, pbuf); - - return 1; -} - -int __MsgFunc_ReqState(const char *pszName, int iSize, void *pbuf) -{ - if(g_pInternalVoiceStatus) - g_pInternalVoiceStatus->HandleReqStateMsg(iSize, pbuf); - - return 1; -} - - -int g_BannedPlayerPrintCount; -void ForEachBannedPlayer(char id[16]) -{ - char str[256]; - sprintf(str, "Ban %d: %2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x\n", - g_BannedPlayerPrintCount++, - id[0], id[1], id[2], id[3], - id[4], id[5], id[6], id[7], - id[8], id[9], id[10], id[11], - id[12], id[13], id[14], id[15] - ); - strupr(str); - gEngfuncs.pfnConsolePrint(str); -} - - -void ShowBannedCallback() -{ - if(g_pInternalVoiceStatus) - { - g_BannedPlayerPrintCount = 0; - gEngfuncs.pfnConsolePrint("------- BANNED PLAYERS -------\n"); - g_pInternalVoiceStatus->m_BanMgr.ForEachBannedPlayer(ForEachBannedPlayer); - gEngfuncs.pfnConsolePrint("------------------------------\n"); - } -} - - -// ---------------------------------------------------------------------- // -// CVoiceStatus. -// ---------------------------------------------------------------------- // - -CVoiceStatus::CVoiceStatus() -{ - m_bBanMgrInitialized = false; - m_LastUpdateServerState = 0; - - m_pSpeakerLabelIcon = NULL; - m_pScoreboardNeverSpoken = NULL; - m_pScoreboardNotSpeaking = NULL; - m_pScoreboardSpeaking = NULL; - m_pScoreboardSpeaking2 = NULL; - m_pScoreboardSquelch = NULL; - m_pScoreboardBanned = NULL; - - m_pLocalBitmap = NULL; - m_pAckBitmap = NULL; - - m_bTalking = m_bServerAcked = false; - - memset(m_pBanButtons, 0, sizeof(m_pBanButtons)); - - m_bServerModEnable = -1; -} - - -CVoiceStatus::~CVoiceStatus() -{ - g_pInternalVoiceStatus = NULL; - - for(int i=0; i < MAX_VOICE_SPEAKERS; i++) - { - delete m_Labels[i].m_pLabel; - m_Labels[i].m_pLabel = NULL; - - delete m_Labels[i].m_pIcon; - m_Labels[i].m_pIcon = NULL; - - delete m_Labels[i].m_pBackground; - m_Labels[i].m_pBackground = NULL; - } - - delete m_pLocalLabel; - m_pLocalLabel = NULL; - - FreeBitmaps(); - - if(m_pchGameDir) - { - if(m_bBanMgrInitialized) - { - m_BanMgr.SaveState(m_pchGameDir); - } - - free(m_pchGameDir); - } -} - - -int CVoiceStatus::Init( - IVoiceStatusHelper *pHelper, - Panel **pParentPanel) -{ - // Setup the voice_modenable cvar. - gEngfuncs.pfnRegisterVariable("voice_modenable", "1", FCVAR_ARCHIVE); - - gEngfuncs.pfnRegisterVariable("voice_clientdebug", "0", 0); - - gEngfuncs.pfnAddCommand("voice_showbanned", ShowBannedCallback); - - if(gEngfuncs.pfnGetGameDirectory()) - { - m_BanMgr.Init(gEngfuncs.pfnGetGameDirectory()); - m_bBanMgrInitialized = true; - } - - assert(!g_pInternalVoiceStatus); - g_pInternalVoiceStatus = this; - - m_BlinkTimer = 0; - m_VoiceHeadModel = NULL; - memset(m_Labels, 0, sizeof(m_Labels)); - - for(int i=0; i < MAX_VOICE_SPEAKERS; i++) - { - CVoiceLabel *pLabel = &m_Labels[i]; - - pLabel->m_pBackground = new Label(""); - - if(pLabel->m_pLabel = new Label("")) - { - pLabel->m_pLabel->setVisible( true ); - pLabel->m_pLabel->setFont( Scheme::sf_primary2 ); - pLabel->m_pLabel->setTextAlignment( Label::a_east ); - pLabel->m_pLabel->setContentAlignment( Label::a_east ); - pLabel->m_pLabel->setParent( pLabel->m_pBackground ); - } - - if( pLabel->m_pIcon = new ImagePanel( NULL ) ) - { - pLabel->m_pIcon->setVisible( true ); - pLabel->m_pIcon->setParent( pLabel->m_pBackground ); - } - - pLabel->m_clientindex = -1; - } - - m_pLocalLabel = new ImagePanel(NULL); - - m_bInSquelchMode = false; - - m_pHelper = pHelper; - m_pParentPanel = pParentPanel; - gHUD.AddHudElem(this); - m_iFlags = HUD_ACTIVE; - HOOK_MESSAGE(VoiceMask); - HOOK_MESSAGE(ReqState); - - // Cache the game directory for use when we shut down - const char *pchGameDirT = gEngfuncs.pfnGetGameDirectory(); - m_pchGameDir = (char *)malloc(strlen(pchGameDirT) + 1); - strcpy(m_pchGameDir, pchGameDirT); - - return 1; -} - - -int CVoiceStatus::VidInit() -{ - FreeBitmaps(); - - - if( m_pLocalBitmap = vgui_LoadTGA("gfx/vgui/icntlk_pl.tga") ) - { - m_pLocalBitmap->setColor(Color(255,255,255,135)); - } - - if( m_pAckBitmap = vgui_LoadTGA("gfx/vgui/icntlk_sv.tga") ) - { - m_pAckBitmap->setColor(Color(255,255,255,135)); // Give just a tiny bit of translucency so software draws correctly. - } - - m_pLocalLabel->setImage( m_pLocalBitmap ); - m_pLocalLabel->setVisible( false ); - - - if( m_pSpeakerLabelIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/speaker4.tga" ) ) - m_pSpeakerLabelIcon->setColor( Color(255,255,255,1) ); // Give just a tiny bit of translucency so software draws correctly. - - if (m_pScoreboardNeverSpoken = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_speaker1.tga")) - m_pScoreboardNeverSpoken->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. - - if(m_pScoreboardNotSpeaking = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_speaker2.tga")) - m_pScoreboardNotSpeaking->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. - - if(m_pScoreboardSpeaking = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_speaker3.tga")) - m_pScoreboardSpeaking->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. - - if(m_pScoreboardSpeaking2 = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_speaker4.tga")) - m_pScoreboardSpeaking2->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. - - if(m_pScoreboardSquelch = vgui_LoadTGA("gfx/vgui/icntlk_squelch.tga")) - m_pScoreboardSquelch->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. - - if(m_pScoreboardBanned = vgui_LoadTGA("gfx/vgui/640_voiceblocked.tga")) - m_pScoreboardBanned->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. - - // Figure out the voice head model height. - m_VoiceHeadModelHeight = 45; - char *pFile = (char *)gEngfuncs.COM_LoadFile("scripts/voicemodel.txt", 5, NULL); - if(pFile) - { - char token[4096]; - gEngfuncs.COM_ParseFile(pFile, token); - if(token[0] >= '0' && token[0] <= '9') - { - m_VoiceHeadModelHeight = (float)atof(token); - } - - gEngfuncs.COM_FreeFile(pFile); - } - - m_VoiceHeadModel = gEngfuncs.pfnSPR_Load("sprites/voiceicon.spr"); - return TRUE; -} - - -void CVoiceStatus::Frame(double frametime) -{ - // check server banned players once per second - if(gEngfuncs.GetClientTime() - m_LastUpdateServerState > 1) - { - UpdateServerState(false); - } - - m_BlinkTimer += frametime; - - // Update speaker labels. - if( m_pHelper->CanShowSpeakerLabels() ) - { - for( int i=0; i < MAX_VOICE_SPEAKERS; i++ ) - m_Labels[i].m_pBackground->setVisible( m_Labels[i].m_clientindex != -1 ); - } - else - { - for( int i=0; i < MAX_VOICE_SPEAKERS; i++ ) - m_Labels[i].m_pBackground->setVisible( false ); - } - - for(int i=0; i < VOICE_MAX_PLAYERS; i++) - UpdateBanButton(i); -} - - -void CVoiceStatus::CreateEntities() -{ - if(!m_VoiceHeadModel) - return; - - cl_entity_t *localPlayer = gEngfuncs.GetLocalPlayer(); - - int iOutModel = 0; - for(int i=0; i < VOICE_MAX_PLAYERS; i++) - { - if(!m_VoicePlayers[i]) - continue; - - cl_entity_s *pClient = gEngfuncs.GetEntityByIndex(i+1); - - // Don't show an icon if the player is not in our PVS. - if(!pClient || pClient->curstate.messagenum < localPlayer->curstate.messagenum) - continue; - - // Don't show an icon for dead or spectating players (ie: invisible entities). - if(pClient->curstate.effects & EF_NODRAW) - continue; - - // Don't show an icon for the local player unless we're in thirdperson mode. - if(pClient == localPlayer && !cam_thirdperson) - continue; - - cl_entity_s *pEnt = &m_VoiceHeadModels[iOutModel]; - ++iOutModel; - - memset(pEnt, 0, sizeof(*pEnt)); - - pEnt->curstate.rendermode = kRenderTransAdd; - pEnt->curstate.renderamt = 255; - pEnt->baseline.renderamt = 255; - pEnt->curstate.renderfx = kRenderFxNoDissipation; - pEnt->curstate.framerate = 1; - // tankefugl: different sprite for each team - if (pClient->curstate.team <= SPR_Frames(m_VoiceHeadModel)) - pEnt->curstate.frame = pClient->curstate.team; - else - pEnt->curstate.frame = 0; - //pEnt->curstate.frame = 0; - // :tankefugl - pEnt->model = (struct model_s*)gEngfuncs.GetSpritePointer(m_VoiceHeadModel); - pEnt->angles[0] = pEnt->angles[1] = pEnt->angles[2] = 0; - pEnt->curstate.scale = 0.5f; - - pEnt->origin[0] = pEnt->origin[1] = 0; - pEnt->origin[2] = AvHCUGetIconHeightForPlayer((AvHUser3)pClient->curstate.iuser3); - - VectorAdd(pEnt->origin, pClient->origin, pEnt->origin); - - // Tell the engine. - gEngfuncs.CL_CreateVisibleEntity(ET_NORMAL, pEnt); - } -} - - -void CVoiceStatus::UpdateSpeakerStatus(int entindex, qboolean bTalking) -{ - if(!*m_pParentPanel) - return; - - if( gEngfuncs.pfnGetCvarFloat("voice_clientdebug") ) - { - char msg[256]; - _snprintf( msg, sizeof(msg), "CVoiceStatus::UpdateSpeakerStatus: ent %d talking = %d\n", entindex, bTalking ); - gEngfuncs.pfnConsolePrint( msg ); - } - - // Is it the local player talking? - if( entindex == -1 ) - { - m_bTalking = !!bTalking; - if( bTalking ) - { - // Enable voice for them automatically if they try to talk. - gEngfuncs.pfnClientCmd( "voice_modenable 1" ); - } - } - else if( entindex == -2 ) - { - m_bServerAcked = !!bTalking; - } - else if(entindex >= 0 && entindex <= VOICE_MAX_PLAYERS) - { - int iClient = entindex - 1; - if(iClient < 0) - return; - - CVoiceLabel *pLabel = FindVoiceLabel(iClient); - if(bTalking) - { - m_VoicePlayers[iClient] = true; - m_VoiceEnabledPlayers[iClient] = true; - - // If we don't have a label for this guy yet, then create one. - if(!pLabel) - { - if(pLabel = GetFreeVoiceLabel()) - { - // Get the name from the engine. - hud_player_info_t info; - memset(&info, 0, sizeof(info)); - GetPlayerInfo(entindex, &info); - - char paddedName[512]; - _snprintf(paddedName, sizeof(paddedName), "%s ", info.name); - - int color[3]; - m_pHelper->GetPlayerTextColor( entindex, color ); - - if( pLabel->m_pBackground ) - { - pLabel->m_pBackground->setBgColor( color[0], color[1], color[2], 135 ); - pLabel->m_pBackground->setParent( *m_pParentPanel ); - pLabel->m_pBackground->setVisible( m_pHelper->CanShowSpeakerLabels() ); - } - - if( pLabel->m_pLabel ) - { - pLabel->m_pLabel->setFgColor( 0, 0, 0, 0 ); - pLabel->m_pLabel->setBgColor( 0, 0, 0, 255 ); - pLabel->m_pLabel->setText( paddedName ); - } - - pLabel->m_clientindex = iClient; - } - } - } - else - { - m_VoicePlayers[iClient] = false; - - // If we have a label for this guy, kill it. - if(pLabel) - { - pLabel->m_pBackground->setVisible(false); - pLabel->m_clientindex = -1; - } - } - } - - RepositionLabels(); -} - - -void CVoiceStatus::UpdateServerState(bool bForce) -{ - // Can't do anything when we're not in a level. - char const *pLevelName = gEngfuncs.pfnGetLevelName(); - if( pLevelName[0] == 0 ) - { - if( gEngfuncs.pfnGetCvarFloat("voice_clientdebug") ) - { - gEngfuncs.pfnConsolePrint( "CVoiceStatus::UpdateServerState: pLevelName[0]==0\n" ); - } - - return; - } - - int bCVarModEnable = !!gEngfuncs.pfnGetCvarFloat("voice_modenable"); - if(bForce || m_bServerModEnable != bCVarModEnable) - { - m_bServerModEnable = bCVarModEnable; - - char str[256]; - _snprintf(str, sizeof(str), "VModEnable %d", m_bServerModEnable); - ServerCmd(str); - - if(gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) - { - char msg[256]; - sprintf(msg, "CVoiceStatus::UpdateServerState: Sending '%s'\n", str); - gEngfuncs.pfnConsolePrint(msg); - } - } - - char str[2048]; - sprintf(str, "vban"); - bool bChange = false; - - for(unsigned long dw=0; dw < VOICE_MAX_PLAYERS_DW; dw++) - { - unsigned long serverBanMask = 0; - unsigned long banMask = 0; - for(unsigned long i=0; i < 32; i++) - { - char playerID[16]; - if(!gEngfuncs.GetPlayerUniqueID(i+1, playerID)) - continue; - - if(m_BanMgr.GetPlayerBan(playerID)) - banMask |= 1 << i; - - if(m_ServerBannedPlayers[dw*32 + i]) - serverBanMask |= 1 << i; - } - - if(serverBanMask != banMask) - bChange = true; - - // Ok, the server needs to be updated. - char numStr[512]; - sprintf(numStr, " %x", banMask); - strcat(str, numStr); - } - - if(bChange || bForce) - { - if(gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) - { - char msg[256]; - sprintf(msg, "CVoiceStatus::UpdateServerState: Sending '%s'\n", str); - gEngfuncs.pfnConsolePrint(msg); - } - - gEngfuncs.pfnServerCmdUnreliable(str); // Tell the server.. - } - else - { - if (gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) - { - gEngfuncs.pfnConsolePrint( "CVoiceStatus::UpdateServerState: no change\n" ); - } - } - - m_LastUpdateServerState = gEngfuncs.GetClientTime(); -} - -void CVoiceStatus::UpdateSpeakerImage(Label *pLabel, int iPlayer) -{ - m_pBanButtons[iPlayer-1] = pLabel; - UpdateBanButton(iPlayer-1); -} - -void CVoiceStatus::UpdateBanButton(int iClient) -{ - Label *pPanel = m_pBanButtons[iClient]; - - if (!pPanel) - return; - - char playerID[16]; - extern bool HACK_GetPlayerUniqueID( int iPlayer, char playerID[16] ); - if(!HACK_GetPlayerUniqueID(iClient+1, playerID)) - return; - - // Figure out if it's blinking or not. - bool bBlink = fmod(m_BlinkTimer, SCOREBOARD_BLINK_FREQUENCY*2) < SCOREBOARD_BLINK_FREQUENCY; - bool bTalking = !!m_VoicePlayers[iClient]; - bool bBanned = m_BanMgr.GetPlayerBan(playerID); - bool bNeverSpoken = !m_VoiceEnabledPlayers[iClient]; - - // Get the appropriate image to display on the panel. - if (bBanned) - { - pPanel->setImage(m_pScoreboardBanned); - } - else if (bTalking) - { - if (bBlink) - { - pPanel->setImage(m_pScoreboardSpeaking2); - } - else - { - pPanel->setImage(m_pScoreboardSpeaking); - } - pPanel->setFgColor(255, 170, 0, 1); - } - else if (bNeverSpoken) - { - pPanel->setImage(m_pScoreboardNeverSpoken); - pPanel->setFgColor(100, 100, 100, 1); - } - else - { - pPanel->setImage(m_pScoreboardNotSpeaking); - } -} - -#include "cl_dll\parsemsg.h" -void CVoiceStatus::HandleVoiceMaskMsg(int iSize, void *pbuf) -{ - BEGIN_READ( pbuf, iSize ); - - unsigned long dw; - for(dw=0; dw < VOICE_MAX_PLAYERS_DW; dw++) - { - m_AudiblePlayers.SetDWord(dw, (unsigned long)READ_LONG()); - m_ServerBannedPlayers.SetDWord(dw, (unsigned long)READ_LONG()); - - if(gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) - { - char str[256]; - gEngfuncs.pfnConsolePrint("CVoiceStatus::HandleVoiceMaskMsg\n"); - - sprintf(str, " - m_AudiblePlayers[%d] = %lu\n", dw, m_AudiblePlayers.GetDWord(dw)); - gEngfuncs.pfnConsolePrint(str); - - sprintf(str, " - m_ServerBannedPlayers[%d] = %lu\n", dw, m_ServerBannedPlayers.GetDWord(dw)); - gEngfuncs.pfnConsolePrint(str); - } - } - - m_bServerModEnable = READ_BYTE(); -} - -void CVoiceStatus::HandleReqStateMsg(int iSize, void *pbuf) -{ - if(gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) - { - gEngfuncs.pfnConsolePrint("CVoiceStatus::HandleReqStateMsg\n"); - } - - UpdateServerState(true); -} - -void CVoiceStatus::StartSquelchMode() -{ - if(m_bInSquelchMode) - return; - - m_bInSquelchMode = true; - m_pHelper->UpdateCursorState(); -} - -void CVoiceStatus::StopSquelchMode() -{ - m_bInSquelchMode = false; - m_pHelper->UpdateCursorState(); -} - -bool CVoiceStatus::IsInSquelchMode() -{ - return m_bInSquelchMode; -} - -CVoiceLabel* CVoiceStatus::FindVoiceLabel(int clientindex) -{ - for(int i=0; i < MAX_VOICE_SPEAKERS; i++) - { - if(m_Labels[i].m_clientindex == clientindex) - return &m_Labels[i]; - } - - return NULL; -} - - -CVoiceLabel* CVoiceStatus::GetFreeVoiceLabel() -{ - return FindVoiceLabel(-1); -} - - -void CVoiceStatus::RepositionLabels() -{ - // find starting position to draw from, along right-hand side of screen - int y = ScreenHeight() / 2; - - int iconWide = 8, iconTall = 8; - if( m_pSpeakerLabelIcon ) - { - m_pSpeakerLabelIcon->getSize( iconWide, iconTall ); - } - - // Reposition active labels. - for(int i = 0; i < MAX_VOICE_SPEAKERS; i++) - { - CVoiceLabel *pLabel = &m_Labels[i]; - - if( pLabel->m_clientindex == -1 || !pLabel->m_pLabel ) - { - if( pLabel->m_pBackground ) - pLabel->m_pBackground->setVisible( false ); - - continue; - } - - int textWide, textTall; - pLabel->m_pLabel->getContentSize( textWide, textTall ); - - // Don't let it stretch too far across their screen. - if( textWide > (ScreenWidth()*2)/3 ) - textWide = (ScreenWidth()*2)/3; - - // Setup the background label to fit everything in. - int border = 2; - int bgWide = textWide + iconWide + border*3; - int bgTall = max( textTall, iconTall ) + border*2; - pLabel->m_pBackground->setBounds( ScreenWidth() - bgWide - 8, y, bgWide, bgTall ); - - // Put the text at the left. - pLabel->m_pLabel->setBounds( border, (bgTall - textTall) / 2, textWide, textTall ); - - // Put the icon at the right. - int iconLeft = border + textWide + border; - int iconTop = (bgTall - iconTall) / 2; - if( pLabel->m_pIcon ) - { - pLabel->m_pIcon->setImage( m_pSpeakerLabelIcon ); - pLabel->m_pIcon->setBounds( iconLeft, iconTop, iconWide, iconTall ); - } - - y += bgTall + 2; - } - - if( m_pLocalBitmap && m_pAckBitmap && m_pLocalLabel && (m_bTalking || m_bServerAcked) ) - { - m_pLocalLabel->setParent(*m_pParentPanel); - m_pLocalLabel->setVisible( true ); - - if( m_bServerAcked && !!gEngfuncs.pfnGetCvarFloat("voice_clientdebug") ) - m_pLocalLabel->setImage( m_pAckBitmap ); - else - m_pLocalLabel->setImage( m_pLocalBitmap ); - - int sizeX, sizeY; - m_pLocalBitmap->getSize(sizeX, sizeY); - - int local_xPos = ScreenWidth() - sizeX - 10; - int local_yPos = m_pHelper->GetAckIconHeight() - sizeY; - - // Move bar depending on role - switch(gHUD.GetHUDUser3()) - { - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: - local_xPos -= kResourceEnergyBarWidth*ScreenWidth(); - break; - - case AVH_USER3_COMMANDER_PLAYER: - local_xPos -= kVoiceStatusCommanderRightEdgeInset*ScreenWidth(); - local_yPos -= (1.0f - kVoiceStatusCommanderTopEdgeInset)*ScreenHeight(); - break; - - case AVH_USER3_MARINE_PLAYER: - local_xPos -= kVoiceStatusMarineRightEdgeInset*ScreenWidth(); - break; - } - - m_pLocalLabel->setPos( local_xPos, local_yPos ); - } - else - { - m_pLocalLabel->setVisible( false ); - } -} - - -void CVoiceStatus::FreeBitmaps() -{ - // Delete all the images we have loaded. - delete m_pLocalBitmap; - m_pLocalBitmap = NULL; - - delete m_pAckBitmap; - m_pAckBitmap = NULL; - - delete m_pSpeakerLabelIcon; - m_pSpeakerLabelIcon = NULL; - - delete m_pScoreboardNeverSpoken; - m_pScoreboardNeverSpoken = NULL; - - delete m_pScoreboardNotSpeaking; - m_pScoreboardNotSpeaking = NULL; - - delete m_pScoreboardSpeaking; - m_pScoreboardSpeaking = NULL; - - delete m_pScoreboardSpeaking2; - m_pScoreboardSpeaking2 = NULL; - - delete m_pScoreboardSquelch; - m_pScoreboardSquelch = NULL; - - delete m_pScoreboardBanned; - m_pScoreboardBanned = NULL; - - // Clear references to the images in panels. - for(int i=0; i < VOICE_MAX_PLAYERS; i++) - { - if (m_pBanButtons[i]) - { - m_pBanButtons[i]->setImage(NULL); - } - } - - if(m_pLocalLabel) - m_pLocalLabel->setImage(NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the target client has been banned -// Input : playerID - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CVoiceStatus::IsPlayerBlocked(int iPlayer) -{ - char playerID[16]; - if (!gEngfuncs.GetPlayerUniqueID(iPlayer, playerID)) - return false; - - return m_BanMgr.GetPlayerBan(playerID); -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the player can't hear the other client due to game rules (eg. the other team) -// Input : playerID - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CVoiceStatus::IsPlayerAudible(int iPlayer) -{ - return !!m_AudiblePlayers[iPlayer-1]; -} - -//----------------------------------------------------------------------------- -// Purpose: blocks/unblocks the target client from being heard -// Input : playerID - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -void CVoiceStatus::SetPlayerBlockedState(int iPlayer, bool blocked) -{ - if (gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) - { - gEngfuncs.pfnConsolePrint( "CVoiceStatus::SetPlayerBlockedState part 1\n" ); - } - - char playerID[16]; - if (!gEngfuncs.GetPlayerUniqueID(iPlayer, playerID)) - return; - - if (gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) - { - gEngfuncs.pfnConsolePrint( "CVoiceStatus::SetPlayerBlockedState part 2\n" ); - } - - // Squelch or (try to) unsquelch this player. - if (gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) - { - char str[256]; - sprintf(str, "CVoiceStatus::SetPlayerBlockedState: setting player %d ban to %d\n", iPlayer, !m_BanMgr.GetPlayerBan(playerID)); - gEngfuncs.pfnConsolePrint(str); - } - - m_BanMgr.SetPlayerBan( playerID, blocked ); - UpdateServerState(false); -} +//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +// There are hud.h's coming out of the woodwork so this ensures that we get the right one. +#include "../cl_dll/hud.h" + +#include "../cl_dll/cl_util.h" +#include +#include +#include +#include "../cl_dll/hud_servers.h" +#include "../cl_dll/demo.h" +#include "common/demo_api.h" +#include "voice_status.h" +#include "common/r_efx.h" +#include "common/entity_types.h" +#include "VGUI_ActionSignal.h" +#include "VGUI_Scheme.h" +#include "VGUI_TextImage.h" +#include "vgui_loadtga.h" +#include "vgui_helpers.h" +#include "vgui_mousecode.h" +#include "dlls/extdll.h" +#include "mod/AvHClientUtil.h" +#include "mod/AvHHudConstants.h" + +using namespace vgui; + + +extern int cam_thirdperson; + + +#define VOICE_MODEL_INTERVAL 0.3 +#define SCOREBOARD_BLINK_FREQUENCY 0.3 // How often to blink the scoreboard icons. +#define SQUELCHOSCILLATE_PER_SECOND 2.0f + + +extern BitmapTGA *LoadTGA( const char* pImageName ); + +// Allow assignment within conditional +#pragma warning (disable: 4706) + +// ---------------------------------------------------------------------- // +// The voice manager for the client. +// ---------------------------------------------------------------------- // +CVoiceStatus g_VoiceStatus; + +CVoiceStatus* GetClientVoiceMgr() +{ + return &g_VoiceStatus; +} + + + +// ---------------------------------------------------------------------- // +// CVoiceStatus. +// ---------------------------------------------------------------------- // + +static CVoiceStatus *g_pInternalVoiceStatus = NULL; + +int __MsgFunc_VoiceMask(const char *pszName, int iSize, void *pbuf) +{ + if(g_pInternalVoiceStatus) + g_pInternalVoiceStatus->HandleVoiceMaskMsg(iSize, pbuf); + + return 1; +} + +int __MsgFunc_ReqState(const char *pszName, int iSize, void *pbuf) +{ + if(g_pInternalVoiceStatus) + g_pInternalVoiceStatus->HandleReqStateMsg(iSize, pbuf); + + return 1; +} + + +int g_BannedPlayerPrintCount; +void ForEachBannedPlayer(char id[16]) +{ + char str[256]; + sprintf(str, "Ban %d: %2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x\n", + g_BannedPlayerPrintCount++, + id[0], id[1], id[2], id[3], + id[4], id[5], id[6], id[7], + id[8], id[9], id[10], id[11], + id[12], id[13], id[14], id[15] + ); + strupr(str); + gEngfuncs.pfnConsolePrint(str); +} + + +void ShowBannedCallback() +{ + if(g_pInternalVoiceStatus) + { + g_BannedPlayerPrintCount = 0; + gEngfuncs.pfnConsolePrint("------- BANNED PLAYERS -------\n"); + g_pInternalVoiceStatus->m_BanMgr.ForEachBannedPlayer(ForEachBannedPlayer); + gEngfuncs.pfnConsolePrint("------------------------------\n"); + } +} + + +// ---------------------------------------------------------------------- // +// CVoiceStatus. +// ---------------------------------------------------------------------- // + +CVoiceStatus::CVoiceStatus() +{ + m_bBanMgrInitialized = false; + m_LastUpdateServerState = 0; + + m_pSpeakerLabelIcon = NULL; + m_pScoreboardNeverSpoken = NULL; + m_pScoreboardNotSpeaking = NULL; + m_pScoreboardSpeaking = NULL; + m_pScoreboardSpeaking2 = NULL; + m_pScoreboardSquelch = NULL; + m_pScoreboardBanned = NULL; + + m_pLocalBitmap = NULL; + m_pAckBitmap = NULL; + + m_bTalking = m_bServerAcked = false; + + memset(m_pBanButtons, 0, sizeof(m_pBanButtons)); + + m_bServerModEnable = -1; +} + + +CVoiceStatus::~CVoiceStatus() +{ + g_pInternalVoiceStatus = NULL; + + for(int i=0; i < MAX_VOICE_SPEAKERS; i++) + { + delete m_Labels[i].m_pLabel; + m_Labels[i].m_pLabel = NULL; + + delete m_Labels[i].m_pIcon; + m_Labels[i].m_pIcon = NULL; + + delete m_Labels[i].m_pBackground; + m_Labels[i].m_pBackground = NULL; + } + + delete m_pLocalLabel; + m_pLocalLabel = NULL; + + FreeBitmaps(); + + if(m_pchGameDir) + { + if(m_bBanMgrInitialized) + { + m_BanMgr.SaveState(m_pchGameDir); + } + + free(m_pchGameDir); + } +} + + +int CVoiceStatus::Init( + IVoiceStatusHelper *pHelper, + Panel **pParentPanel) +{ + // Setup the voice_modenable cvar. + gEngfuncs.pfnRegisterVariable("voice_modenable", "1", FCVAR_ARCHIVE); + + gEngfuncs.pfnRegisterVariable("voice_clientdebug", "0", 0); + + gEngfuncs.pfnAddCommand("voice_showbanned", ShowBannedCallback); + + if(gEngfuncs.pfnGetGameDirectory()) + { + m_BanMgr.Init(gEngfuncs.pfnGetGameDirectory()); + m_bBanMgrInitialized = true; + } + + assert(!g_pInternalVoiceStatus); + g_pInternalVoiceStatus = this; + + m_BlinkTimer = 0; + m_VoiceHeadModel = NULL; + memset(m_Labels, 0, sizeof(m_Labels)); + + for(int i=0; i < MAX_VOICE_SPEAKERS; i++) + { + CVoiceLabel *pLabel = &m_Labels[i]; + + pLabel->m_pBackground = new Label(""); + + if(pLabel->m_pLabel = new Label("")) + { + pLabel->m_pLabel->setVisible( true ); + pLabel->m_pLabel->setFont( Scheme::sf_primary2 ); + pLabel->m_pLabel->setTextAlignment( Label::a_east ); + pLabel->m_pLabel->setContentAlignment( Label::a_east ); + pLabel->m_pLabel->setParent( pLabel->m_pBackground ); + } + + if( pLabel->m_pIcon = new ImagePanel( NULL ) ) + { + pLabel->m_pIcon->setVisible( true ); + pLabel->m_pIcon->setParent( pLabel->m_pBackground ); + } + + pLabel->m_clientindex = -1; + } + + m_pLocalLabel = new ImagePanel(NULL); + + m_bInSquelchMode = false; + + m_pHelper = pHelper; + m_pParentPanel = pParentPanel; + gHUD.AddHudElem(this); + m_iFlags = HUD_ACTIVE; + HOOK_MESSAGE(VoiceMask); + HOOK_MESSAGE(ReqState); + + // Cache the game directory for use when we shut down + const char *pchGameDirT = gEngfuncs.pfnGetGameDirectory(); + m_pchGameDir = (char *)malloc(strlen(pchGameDirT) + 1); + strcpy(m_pchGameDir, pchGameDirT); + + return 1; +} + + +int CVoiceStatus::VidInit() +{ + FreeBitmaps(); + + + if( m_pLocalBitmap = vgui_LoadTGA("gfx/vgui/icntlk_pl.tga") ) + { + m_pLocalBitmap->setColor(Color(255,255,255,135)); + } + + if( m_pAckBitmap = vgui_LoadTGA("gfx/vgui/icntlk_sv.tga") ) + { + m_pAckBitmap->setColor(Color(255,255,255,135)); // Give just a tiny bit of translucency so software draws correctly. + } + + m_pLocalLabel->setImage( m_pLocalBitmap ); + m_pLocalLabel->setVisible( false ); + + + if( m_pSpeakerLabelIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/speaker4.tga" ) ) + m_pSpeakerLabelIcon->setColor( Color(255,255,255,1) ); // Give just a tiny bit of translucency so software draws correctly. + + if (m_pScoreboardNeverSpoken = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_speaker1.tga")) + m_pScoreboardNeverSpoken->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. + + if(m_pScoreboardNotSpeaking = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_speaker2.tga")) + m_pScoreboardNotSpeaking->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. + + if(m_pScoreboardSpeaking = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_speaker3.tga")) + m_pScoreboardSpeaking->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. + + if(m_pScoreboardSpeaking2 = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_speaker4.tga")) + m_pScoreboardSpeaking2->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. + + if(m_pScoreboardSquelch = vgui_LoadTGA("gfx/vgui/icntlk_squelch.tga")) + m_pScoreboardSquelch->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. + + if(m_pScoreboardBanned = vgui_LoadTGA("gfx/vgui/640_voiceblocked.tga")) + m_pScoreboardBanned->setColor(Color(255,255,255,1)); // Give just a tiny bit of translucency so software draws correctly. + + // Figure out the voice head model height. + m_VoiceHeadModelHeight = 45; + char *pFile = (char *)gEngfuncs.COM_LoadFile("scripts/voicemodel.txt", 5, NULL); + if(pFile) + { + char token[4096]; + gEngfuncs.COM_ParseFile(pFile, token); + if(token[0] >= '0' && token[0] <= '9') + { + m_VoiceHeadModelHeight = (float)atof(token); + } + + gEngfuncs.COM_FreeFile(pFile); + } + + m_VoiceHeadModel = gEngfuncs.pfnSPR_Load("sprites/voiceicon.spr"); + return TRUE; +} + + +void CVoiceStatus::Frame(double frametime) +{ + // check server banned players once per second + if(gEngfuncs.GetClientTime() - m_LastUpdateServerState > 1) + { + UpdateServerState(false); + } + + m_BlinkTimer += frametime; + + // Update speaker labels. + if( m_pHelper->CanShowSpeakerLabels() ) + { + for( int i=0; i < MAX_VOICE_SPEAKERS; i++ ) + m_Labels[i].m_pBackground->setVisible( m_Labels[i].m_clientindex != -1 ); + } + else + { + for( int i=0; i < MAX_VOICE_SPEAKERS; i++ ) + m_Labels[i].m_pBackground->setVisible( false ); + } + + for(int i=0; i < VOICE_MAX_PLAYERS; i++) + UpdateBanButton(i); +} + + +void CVoiceStatus::CreateEntities() +{ + if(!m_VoiceHeadModel) + return; + + cl_entity_t *localPlayer = gEngfuncs.GetLocalPlayer(); + + int iOutModel = 0; + for(int i=0; i < VOICE_MAX_PLAYERS; i++) + { + if(!m_VoicePlayers[i]) + continue; + + cl_entity_s *pClient = gEngfuncs.GetEntityByIndex(i+1); + + // Don't show an icon if the player is not in our PVS. + if(!pClient || pClient->curstate.messagenum < localPlayer->curstate.messagenum) + continue; + + // Don't show an icon for dead or spectating players (ie: invisible entities). + if(pClient->curstate.effects & EF_NODRAW) + continue; + + // Don't show an icon for the local player unless we're in thirdperson mode. + if(pClient == localPlayer && !cam_thirdperson) + continue; + + cl_entity_s *pEnt = &m_VoiceHeadModels[iOutModel]; + ++iOutModel; + + memset(pEnt, 0, sizeof(*pEnt)); + + pEnt->curstate.rendermode = kRenderTransAdd; + pEnt->curstate.renderamt = 255; + pEnt->baseline.renderamt = 255; + pEnt->curstate.renderfx = kRenderFxNoDissipation; + pEnt->curstate.framerate = 1; + // : different sprite for each team + if (pClient->curstate.team <= SPR_Frames(m_VoiceHeadModel)) + pEnt->curstate.frame = pClient->curstate.team; + else + pEnt->curstate.frame = 0; + //pEnt->curstate.frame = 0; + // : + pEnt->model = (struct model_s*)gEngfuncs.GetSpritePointer(m_VoiceHeadModel); + pEnt->angles[0] = pEnt->angles[1] = pEnt->angles[2] = 0; + pEnt->curstate.scale = 0.5f; + + pEnt->origin[0] = pEnt->origin[1] = 0; + pEnt->origin[2] = AvHCUGetIconHeightForPlayer((AvHUser3)pClient->curstate.iuser3); + + VectorAdd(pEnt->origin, pClient->origin, pEnt->origin); + + // Tell the engine. + gEngfuncs.CL_CreateVisibleEntity(ET_NORMAL, pEnt); + } +} + + +void CVoiceStatus::UpdateSpeakerStatus(int entindex, qboolean bTalking) +{ + if(!*m_pParentPanel) + return; + + if( gEngfuncs.pfnGetCvarFloat("voice_clientdebug") ) + { + char msg[256]; + _snprintf( msg, sizeof(msg), "CVoiceStatus::UpdateSpeakerStatus: ent %d talking = %d\n", entindex, bTalking ); + gEngfuncs.pfnConsolePrint( msg ); + } + + // Is it the local player talking? + if( entindex == -1 ) + { + m_bTalking = !!bTalking; + if( bTalking ) + { + // Enable voice for them automatically if they try to talk. + gEngfuncs.pfnClientCmd( "voice_modenable 1" ); + } + } + else if( entindex == -2 ) + { + m_bServerAcked = !!bTalking; + } + else if(entindex >= 0 && entindex <= VOICE_MAX_PLAYERS) + { + int iClient = entindex - 1; + if(iClient < 0) + return; + + CVoiceLabel *pLabel = FindVoiceLabel(iClient); + if(bTalking) + { + m_VoicePlayers[iClient] = true; + m_VoiceEnabledPlayers[iClient] = true; + + // If we don't have a label for this guy yet, then create one. + if(!pLabel) + { + if(pLabel = GetFreeVoiceLabel()) + { + // Get the name from the engine. + hud_player_info_t info; + memset(&info, 0, sizeof(info)); + GetPlayerInfo(entindex, &info); + + char paddedName[512]; + _snprintf(paddedName, sizeof(paddedName), "%s ", info.name); + + int color[3]; + m_pHelper->GetPlayerTextColor( entindex, color ); + + if( pLabel->m_pBackground ) + { + pLabel->m_pBackground->setBgColor( color[0], color[1], color[2], 135 ); + pLabel->m_pBackground->setParent( *m_pParentPanel ); + pLabel->m_pBackground->setVisible( m_pHelper->CanShowSpeakerLabels() ); + } + + if( pLabel->m_pLabel ) + { + pLabel->m_pLabel->setFgColor( 0, 0, 0, 0 ); + pLabel->m_pLabel->setBgColor( 0, 0, 0, 255 ); + pLabel->m_pLabel->setText( paddedName ); + } + + pLabel->m_clientindex = iClient; + } + } + } + else + { + m_VoicePlayers[iClient] = false; + + // If we have a label for this guy, kill it. + if(pLabel) + { + pLabel->m_pBackground->setVisible(false); + pLabel->m_clientindex = -1; + } + } + } + + RepositionLabels(); +} + + +void CVoiceStatus::UpdateServerState(bool bForce) +{ + // Can't do anything when we're not in a level. + char const *pLevelName = gEngfuncs.pfnGetLevelName(); + if( pLevelName[0] == 0 ) + { + if( gEngfuncs.pfnGetCvarFloat("voice_clientdebug") ) + { + gEngfuncs.pfnConsolePrint( "CVoiceStatus::UpdateServerState: pLevelName[0]==0\n" ); + } + + return; + } + + int bCVarModEnable = !!gEngfuncs.pfnGetCvarFloat("voice_modenable"); + if(bForce || m_bServerModEnable != bCVarModEnable) + { + m_bServerModEnable = bCVarModEnable; + + char str[256]; + _snprintf(str, sizeof(str), "VModEnable %d", m_bServerModEnable); + ServerCmd(str); + + if(gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) + { + char msg[256]; + sprintf(msg, "CVoiceStatus::UpdateServerState: Sending '%s'\n", str); + gEngfuncs.pfnConsolePrint(msg); + } + } + + char str[2048]; + sprintf(str, "vban"); + bool bChange = false; + + for(unsigned long dw=0; dw < VOICE_MAX_PLAYERS_DW; dw++) + { + unsigned long serverBanMask = 0; + unsigned long banMask = 0; + for(unsigned long i=0; i < 32; i++) + { + char playerID[16]; + if(!gEngfuncs.GetPlayerUniqueID(i+1, playerID)) + continue; + + if(m_BanMgr.GetPlayerBan(playerID)) + banMask |= 1 << i; + + if(m_ServerBannedPlayers[dw*32 + i]) + serverBanMask |= 1 << i; + } + + if(serverBanMask != banMask) + bChange = true; + + // Ok, the server needs to be updated. + char numStr[512]; + sprintf(numStr, " %x", banMask); + strcat(str, numStr); + } + + if(bChange || bForce) + { + if(gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) + { + char msg[256]; + sprintf(msg, "CVoiceStatus::UpdateServerState: Sending '%s'\n", str); + gEngfuncs.pfnConsolePrint(msg); + } + + gEngfuncs.pfnServerCmdUnreliable(str); // Tell the server.. + } + else + { + if (gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) + { + gEngfuncs.pfnConsolePrint( "CVoiceStatus::UpdateServerState: no change\n" ); + } + } + + m_LastUpdateServerState = gEngfuncs.GetClientTime(); +} + +void CVoiceStatus::UpdateSpeakerImage(Label *pLabel, int iPlayer) +{ + m_pBanButtons[iPlayer-1] = pLabel; + UpdateBanButton(iPlayer-1); +} + +void CVoiceStatus::UpdateBanButton(int iClient) +{ + Label *pPanel = m_pBanButtons[iClient]; + + if (!pPanel) + return; + + char playerID[16]; + extern bool HACK_GetPlayerUniqueID( int iPlayer, char playerID[16] ); + if(!HACK_GetPlayerUniqueID(iClient+1, playerID)) + return; + + // Figure out if it's blinking or not. + bool bBlink = fmod(m_BlinkTimer, SCOREBOARD_BLINK_FREQUENCY*2) < SCOREBOARD_BLINK_FREQUENCY; + bool bTalking = !!m_VoicePlayers[iClient]; + bool bBanned = m_BanMgr.GetPlayerBan(playerID); + bool bNeverSpoken = !m_VoiceEnabledPlayers[iClient]; + + // Get the appropriate image to display on the panel. + if (bBanned) + { + pPanel->setImage(m_pScoreboardBanned); + } + else if (bTalking) + { + if (bBlink) + { + pPanel->setImage(m_pScoreboardSpeaking2); + } + else + { + pPanel->setImage(m_pScoreboardSpeaking); + } + pPanel->setFgColor(255, 170, 0, 1); + } + else if (bNeverSpoken) + { + pPanel->setImage(m_pScoreboardNeverSpoken); + pPanel->setFgColor(100, 100, 100, 1); + } + else + { + pPanel->setImage(m_pScoreboardNotSpeaking); + } +} + +#include "cl_dll\parsemsg.h" +void CVoiceStatus::HandleVoiceMaskMsg(int iSize, void *pbuf) +{ + BEGIN_READ( pbuf, iSize ); + + unsigned long dw; + for(dw=0; dw < VOICE_MAX_PLAYERS_DW; dw++) + { + m_AudiblePlayers.SetDWord(dw, (unsigned long)READ_LONG()); + m_ServerBannedPlayers.SetDWord(dw, (unsigned long)READ_LONG()); + + if(gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) + { + char str[256]; + gEngfuncs.pfnConsolePrint("CVoiceStatus::HandleVoiceMaskMsg\n"); + + sprintf(str, " - m_AudiblePlayers[%d] = %lu\n", dw, m_AudiblePlayers.GetDWord(dw)); + gEngfuncs.pfnConsolePrint(str); + + sprintf(str, " - m_ServerBannedPlayers[%d] = %lu\n", dw, m_ServerBannedPlayers.GetDWord(dw)); + gEngfuncs.pfnConsolePrint(str); + } + } + + m_bServerModEnable = READ_BYTE(); +} + +void CVoiceStatus::HandleReqStateMsg(int iSize, void *pbuf) +{ + if(gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) + { + gEngfuncs.pfnConsolePrint("CVoiceStatus::HandleReqStateMsg\n"); + } + + UpdateServerState(true); +} + +void CVoiceStatus::StartSquelchMode() +{ + if(m_bInSquelchMode) + return; + + m_bInSquelchMode = true; + m_pHelper->UpdateCursorState(); +} + +void CVoiceStatus::StopSquelchMode() +{ + m_bInSquelchMode = false; + m_pHelper->UpdateCursorState(); +} + +bool CVoiceStatus::IsInSquelchMode() +{ + return m_bInSquelchMode; +} + +CVoiceLabel* CVoiceStatus::FindVoiceLabel(int clientindex) +{ + for(int i=0; i < MAX_VOICE_SPEAKERS; i++) + { + if(m_Labels[i].m_clientindex == clientindex) + return &m_Labels[i]; + } + + return NULL; +} + + +CVoiceLabel* CVoiceStatus::GetFreeVoiceLabel() +{ + return FindVoiceLabel(-1); +} + + +void CVoiceStatus::RepositionLabels() +{ + // find starting position to draw from, along right-hand side of screen + int y = ScreenHeight() / 2; + + int iconWide = 8, iconTall = 8; + if( m_pSpeakerLabelIcon ) + { + m_pSpeakerLabelIcon->getSize( iconWide, iconTall ); + } + + // Reposition active labels. + for(int i = 0; i < MAX_VOICE_SPEAKERS; i++) + { + CVoiceLabel *pLabel = &m_Labels[i]; + + if( pLabel->m_clientindex == -1 || !pLabel->m_pLabel ) + { + if( pLabel->m_pBackground ) + pLabel->m_pBackground->setVisible( false ); + + continue; + } + + int textWide, textTall; + pLabel->m_pLabel->getContentSize( textWide, textTall ); + + // Don't let it stretch too far across their screen. + if( textWide > (ScreenWidth()*2)/3 ) + textWide = (ScreenWidth()*2)/3; + + // Setup the background label to fit everything in. + int border = 2; + int bgWide = textWide + iconWide + border*3; + int bgTall = max( textTall, iconTall ) + border*2; + pLabel->m_pBackground->setBounds( ScreenWidth() - bgWide - 8, y, bgWide, bgTall ); + + // Put the text at the left. + pLabel->m_pLabel->setBounds( border, (bgTall - textTall) / 2, textWide, textTall ); + + // Put the icon at the right. + int iconLeft = border + textWide + border; + int iconTop = (bgTall - iconTall) / 2; + if( pLabel->m_pIcon ) + { + pLabel->m_pIcon->setImage( m_pSpeakerLabelIcon ); + pLabel->m_pIcon->setBounds( iconLeft, iconTop, iconWide, iconTall ); + } + + y += bgTall + 2; + } + + if( m_pLocalBitmap && m_pAckBitmap && m_pLocalLabel && (m_bTalking || m_bServerAcked) ) + { + m_pLocalLabel->setParent(*m_pParentPanel); + m_pLocalLabel->setVisible( true ); + + if( m_bServerAcked && !!gEngfuncs.pfnGetCvarFloat("voice_clientdebug") ) + m_pLocalLabel->setImage( m_pAckBitmap ); + else + m_pLocalLabel->setImage( m_pLocalBitmap ); + + int sizeX, sizeY; + m_pLocalBitmap->getSize(sizeX, sizeY); + + int local_xPos = ScreenWidth() - sizeX - 10; + int local_yPos = m_pHelper->GetAckIconHeight() - sizeY; + + // Move bar depending on role + switch(gHUD.GetHUDUser3()) + { + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: + local_xPos -= kResourceEnergyBarWidth*ScreenWidth(); + break; + + case AVH_USER3_COMMANDER_PLAYER: + local_xPos -= kVoiceStatusCommanderRightEdgeInset*ScreenWidth(); + local_yPos -= (1.0f - kVoiceStatusCommanderTopEdgeInset)*ScreenHeight(); + break; + + case AVH_USER3_MARINE_PLAYER: + local_xPos -= kVoiceStatusMarineRightEdgeInset*ScreenWidth(); + break; + } + + m_pLocalLabel->setPos( local_xPos, local_yPos ); + } + else + { + m_pLocalLabel->setVisible( false ); + } +} + + +void CVoiceStatus::FreeBitmaps() +{ + // Delete all the images we have loaded. + delete m_pLocalBitmap; + m_pLocalBitmap = NULL; + + delete m_pAckBitmap; + m_pAckBitmap = NULL; + + delete m_pSpeakerLabelIcon; + m_pSpeakerLabelIcon = NULL; + + delete m_pScoreboardNeverSpoken; + m_pScoreboardNeverSpoken = NULL; + + delete m_pScoreboardNotSpeaking; + m_pScoreboardNotSpeaking = NULL; + + delete m_pScoreboardSpeaking; + m_pScoreboardSpeaking = NULL; + + delete m_pScoreboardSpeaking2; + m_pScoreboardSpeaking2 = NULL; + + delete m_pScoreboardSquelch; + m_pScoreboardSquelch = NULL; + + delete m_pScoreboardBanned; + m_pScoreboardBanned = NULL; + + // Clear references to the images in panels. + for(int i=0; i < VOICE_MAX_PLAYERS; i++) + { + if (m_pBanButtons[i]) + { + m_pBanButtons[i]->setImage(NULL); + } + } + + if(m_pLocalLabel) + m_pLocalLabel->setImage(NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the target client has been banned +// Input : playerID - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CVoiceStatus::IsPlayerBlocked(int iPlayer) +{ + char playerID[16]; + if (!gEngfuncs.GetPlayerUniqueID(iPlayer, playerID)) + return false; + + return m_BanMgr.GetPlayerBan(playerID); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the player can't hear the other client due to game rules (eg. the other team) +// Input : playerID - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CVoiceStatus::IsPlayerAudible(int iPlayer) +{ + return !!m_AudiblePlayers[iPlayer-1]; +} + +//----------------------------------------------------------------------------- +// Purpose: blocks/unblocks the target client from being heard +// Input : playerID - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +void CVoiceStatus::SetPlayerBlockedState(int iPlayer, bool blocked) +{ + if (gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) + { + gEngfuncs.pfnConsolePrint( "CVoiceStatus::SetPlayerBlockedState part 1\n" ); + } + + char playerID[16]; + if (!gEngfuncs.GetPlayerUniqueID(iPlayer, playerID)) + return; + + if (gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) + { + gEngfuncs.pfnConsolePrint( "CVoiceStatus::SetPlayerBlockedState part 2\n" ); + } + + // Squelch or (try to) unsquelch this player. + if (gEngfuncs.pfnGetCvarFloat("voice_clientdebug")) + { + char str[256]; + sprintf(str, "CVoiceStatus::SetPlayerBlockedState: setting player %d ban to %d\n", iPlayer, !m_BanMgr.GetPlayerBan(playerID)); + gEngfuncs.pfnConsolePrint(str); + } + + m_BanMgr.SetPlayerBan( playerID, blocked ); + UpdateServerState(false); +} diff --git a/main/source/linux/Makefile b/main/source/linux/Makefile index 92ee2cd..fc6f21e 100644 --- a/main/source/linux/Makefile +++ b/main/source/linux/Makefile @@ -31,7 +31,9 @@ TEXT_OBJDIR=$(TEXT_SRCDIR)/obj UTIL_OBJDIR=$(UTIL_SRCDIR)/obj OUTPUT_DIR=../../hlds_l/ns/dlls -BASE_CFLAGS=-Dstricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp -DAVH_SERVER -DLINUX -DVALVE_DLL -DQUIVER -DVOXEL -DQUAKE2 -DDEDICATED -DSWDS -D_STLP_USE_GLIBC -DAVH_PLAYTEST_BUILD -DUSE_OLDAUTH -DAVH_SECURE_PRERELEASE_BUILD +BASE_CFLAGS=-Dstricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp -DAVH_SERVER -DLINUX -DVALVE_DLL -DQUIVER -DVOXEL -DQUAKE2 -DDEDICATED -DSWDS -D_STLP_USE_GLIBC -DUSE_OLDAUTH + +# -DAVH_SECURE_PRERELEASE_BUILD for CM testing #full optimization CFLAGS=$(BASE_CFLAGS) -w -Wall -nostdinc++ -ffor-scope -fPIC -mcpu=i486 -O3 -pipe -funroll-loops -fdelayed-branch -malign-loops=4 -malign-jumps=4 -malign-functions=4 diff --git a/main/source/mod/AnimationUtil.cpp b/main/source/mod/AnimationUtil.cpp index a8c431b..c978a46 100644 --- a/main/source/mod/AnimationUtil.cpp +++ b/main/source/mod/AnimationUtil.cpp @@ -1,668 +1,668 @@ -#include -#include "types.h" - -#ifdef AVH_CLIENT -#include "cl_dll/wrect.h" -#include "cl_dll/cl_dll.h" -#include "cl_dll/r_studioint.h" -#include "common/com_model.h" -#include "common/cl_entity.h" -#include "common/vec_op.h" -#include "cl_dll/studio_util.h" - -extern engine_studio_api_t IEngineStudio; - -#endif - -#ifdef AVH_SERVER -#include "common/mathlib.h" -#include "common/const.h" -#include "engine/eiface.h" -#include "engine/edict.h" -#include "dlls/enginecallback.h" -#endif - -#include "mod/AnimationUtil.h" -#include "mod/AvHSpecials.h" -#include "util/MathUtil.h" - -#define PITCH 0 -#define YAW 1 -#define ROLL 2 - -//----------------------------------------------------------------------------- - -void NS_AngleMatrix (const float *angles, float (*matrix)[4] ) -{ - float angle; - float sr, sp, sy, cr, cp, cy; - - angle = angles[YAW] * (float(M_PI)*2 / 360); - sy = sinf(angle); - cy = cosf(angle); - angle = angles[PITCH] * (float(M_PI)*2 / 360); - sp = sinf(angle); - cp = cosf(angle); - angle = angles[ROLL] * (float(M_PI)*2 / 360); - sr = sinf(angle); - cr = cosf(angle); - - // matrix = (YAW * PITCH) * ROLL - matrix[0][0] = cp*cy; - matrix[1][0] = cp*sy; - matrix[2][0] = -sp; - matrix[0][1] = sr*sp*cy+cr*-sy; - matrix[1][1] = sr*sp*sy+cr*cy; - matrix[2][1] = sr*cp; - matrix[0][2] = (cr*sp*cy+-sr*-sy); - matrix[1][2] = (cr*sp*sy+-sr*cy); - matrix[2][2] = cr*cp; - matrix[0][3] = 0.0; - matrix[1][3] = 0.0; - matrix[2][3] = 0.0; -} - -//----------------------------------------------------------------------------- - -#ifdef AVH_SERVER - -bool NS_GetEntityAnimationData(int inEntityIndex, NS_AnimationData& outAnimationData) -{ - - edict_t* theEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex); - - if (theEdict == NULL) - { - return false; - } - - vec3_t theAngles; - - if (theEdict->v.iuser3 == AVH_USER3_ALIEN_PLAYER1) - { - VectorCopy(theEdict->v.vuser1, theAngles); - } - else - { - VectorCopy(theEdict->v.angles, theAngles); - } - - NS_AngleMatrix(theAngles, outAnimationData.mMatrix); - - outAnimationData.mMatrix[0][3] = theEdict->v.origin[0]; - outAnimationData.mMatrix[1][3] = theEdict->v.origin[1]; - outAnimationData.mMatrix[2][3] = theEdict->v.origin[2]; - - outAnimationData.mTime = theEdict->v.animtime; - outAnimationData.mFrame = theEdict->v.frame; - outAnimationData.mFrameRate = theEdict->v.framerate; - outAnimationData.mModelHeader = (studiohdr_t*)(GET_MODEL_PTR(theEdict)); - outAnimationData.mSequence = theEdict->v.sequence; - outAnimationData.mGaitSequence = theEdict->v.gaitsequence; - - // Get the bounding box for the sequence. - - studiohdr_t* theModelHeader = outAnimationData.mModelHeader; - - if (outAnimationData.mModelHeader != NULL) - { - - mstudioseqdesc_t* theSequence = (mstudioseqdesc_t*)((byte*)theModelHeader + theModelHeader->seqindex) + outAnimationData.mSequence; - - VectorCopy(theSequence->bbmin, outAnimationData.mMins); - VectorCopy(theSequence->bbmax, outAnimationData.mMaxs); - - } - - return true; - -} - -#endif - -//----------------------------------------------------------------------------- - -#ifdef AVH_CLIENT - -bool NS_GetEntityAnimationData(int inEntityIndex, NS_AnimationData& outAnimationData) -{ - - cl_entity_t* theEntity = gEngfuncs.GetEntityByIndex(inEntityIndex); - - if (theEntity == NULL || theEntity->model == NULL) - { - return false; - } - - vec3_t theAngles; - - if (theEntity->curstate.iuser3 == AVH_USER3_ALIEN_PLAYER1) - { - VectorCopy(theEntity->curstate.vuser1, theAngles); - } - else - { - VectorCopy(theEntity->curstate.angles, theAngles); - } - - NS_AngleMatrix(theAngles, outAnimationData.mMatrix); - - outAnimationData.mMatrix[0][3] = theEntity->curstate.origin[0]; - outAnimationData.mMatrix[1][3] = theEntity->curstate.origin[1]; - outAnimationData.mMatrix[2][3] = theEntity->curstate.origin[2]; - - outAnimationData.mTime = theEntity->curstate.animtime; - outAnimationData.mFrame = theEntity->curstate.frame; - outAnimationData.mFrameRate = theEntity->curstate.framerate; - outAnimationData.mModelHeader = (studiohdr_t *)IEngineStudio.Mod_Extradata(theEntity->model); - outAnimationData.mSequence = theEntity->curstate.sequence; - outAnimationData.mGaitSequence = theEntity->curstate.gaitsequence; - - // Get the bounding box for the sequence. - - studiohdr_t* theModelHeader = outAnimationData.mModelHeader; - - if (outAnimationData.mModelHeader != NULL) - { - - mstudioseqdesc_t* theSequence = (mstudioseqdesc_t*)((byte*)theModelHeader + theModelHeader->seqindex) + outAnimationData.mSequence; - - VectorCopy(theSequence->bbmin, outAnimationData.mMins); - VectorCopy(theSequence->bbmax, outAnimationData.mMaxs); - - } - - return true; - -} - -#endif - -//----------------------------------------------------------------------------- - -void NS_AngleQuaternion( float *angles, vec4_t quaternion ) -{ - float angle; - float sr, sp, sy, cr, cp, cy; - - // FIXME: rescale the inputs to 1/2 angle - angle = angles[2] * 0.5f; - sy = sinf(angle); - cy = cosf(angle); - angle = angles[1] * 0.5f; - sp = sinf(angle); - cp = cosf(angle); - angle = angles[0] * 0.5f; - sr = sinf(angle); - cr = cosf(angle); - - quaternion[0] = sr*cp*cy-cr*sp*sy; // X - quaternion[1] = cr*sp*cy+sr*cp*sy; // Y - quaternion[2] = cr*cp*sy-sr*sp*cy; // Z - quaternion[3] = cr*cp*cy+sr*sp*sy; // W -} - -//----------------------------------------------------------------------------- - -void NS_QuaternionSlerp( vec4_t p, vec4_t q, float t, vec4_t qt ) -{ - - int i; - float omega, cosom, sinom, sclp, sclq; - - // decide if one of the quaternions is backwards - float a = 0; - float b = 0; - - for (i = 0; i < 4; i++) - { - a += (p[i]-q[i])*(p[i]-q[i]); - b += (p[i]+q[i])*(p[i]+q[i]); - } - if (a > b) - { - for (i = 0; i < 4; i++) - { - q[i] = -q[i]; - } - } - - cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3]; - - if ((1.0 + cosom) > 0.000001f) - { - if ((1.0 - cosom) > 0.000001f) - { - omega = acosf( cosom ); - sinom = sinf( omega ); - sclp = sinf( (1.0f - t)*omega) / sinom; - sclq = sinf( t*omega ) / sinom; - } - else - { - sclp = 1.0f - t; - sclq = t; - } - for (i = 0; i < 4; i++) { - qt[i] = sclp * p[i] + sclq * q[i]; - } - } - else - { - qt[0] = -q[1]; - qt[1] = q[0]; - qt[2] = -q[3]; - qt[3] = q[2]; - sclp = sinf( (1.0f - t) * (0.5f * float(M_PI))); - sclq = sinf( t * (0.5f * float(M_PI))); - for (i = 0; i < 3; i++) - { - qt[i] = sclp * p[i] + sclq * qt[i]; - } - } -} - -//----------------------------------------------------------------------------- - -void NS_QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] ) -{ - matrix[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2]; - matrix[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2]; - matrix[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1]; - - matrix[0][1] = 2.0f * quaternion[0] * quaternion[1] - 2.0f * quaternion[3] * quaternion[2]; - matrix[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2]; - matrix[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0]; - - matrix[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1]; - matrix[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0]; - matrix[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1]; -} - -//----------------------------------------------------------------------------- - -void NS_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4]) -{ - out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + - in1[0][2] * in2[2][0]; - out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + - in1[0][2] * in2[2][1]; - out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + - in1[0][2] * in2[2][2]; - out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + - in1[0][2] * in2[2][3] + in1[0][3]; - out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + - in1[1][2] * in2[2][0]; - out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + - in1[1][2] * in2[2][1]; - out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + - in1[1][2] * in2[2][2]; - out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + - in1[1][2] * in2[2][3] + in1[1][3]; - out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + - in1[2][2] * in2[2][0]; - out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + - in1[2][2] * in2[2][1]; - out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + - in1[2][2] * in2[2][2]; - out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + - in1[2][2] * in2[2][3] + in1[2][3]; -} - -//----------------------------------------------------------------------------- - -void NS_CalcBonePosition(int frame, float s, mstudiobone_t* pbone, mstudioanim_t* panim, float* adj, float* pos) -{ - - // This is ripped out of StudioModelRenderer. - - int j, k; - mstudioanimvalue_t *panimvalue; - - for (j = 0; j < 3; j++) - { - pos[j] = pbone->value[j]; // default; - if (panim->offset[j] != 0) - { - panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]); - /* - if (i == 0 && j == 0) - Con_DPrintf("%d %d:%d %f\n", frame, panimvalue->num.valid, panimvalue->num.total, s ); - */ - - k = frame; - // DEBUG - if (panimvalue->num.total < panimvalue->num.valid) - k = 0; - // find span of values that includes the frame we want - while (panimvalue->num.total <= k) - { - k -= panimvalue->num.total; - panimvalue += panimvalue->num.valid + 1; - // DEBUG - if (panimvalue->num.total < panimvalue->num.valid) - k = 0; - } - // if we're inside the span - if (panimvalue->num.valid > k) - { - // and there's more data in the span - if (panimvalue->num.valid > k + 1) - { - pos[j] += (panimvalue[k+1].value * (1.0f - s) + s * panimvalue[k+2].value) * pbone->scale[j]; - } - else - { - pos[j] += panimvalue[k+1].value * pbone->scale[j]; - } - } - else - { - // are we at the end of the repeating values section and there's another section with data? - if (panimvalue->num.total <= k + 1) - { - pos[j] += (panimvalue[panimvalue->num.valid].value * (1.0f - s) + s * panimvalue[panimvalue->num.valid + 2].value) * pbone->scale[j]; - } - else - { - pos[j] += panimvalue[panimvalue->num.valid].value * pbone->scale[j]; - } - } - } - if ( pbone->bonecontroller[j] != -1 && adj ) - { - pos[j] += adj[pbone->bonecontroller[j]]; - } - } -} - -//----------------------------------------------------------------------------- - -void NS_CalcBoneAngles( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *q ) -{ - int j, k; - vec4_t q1, q2; - vec3_t angle1, angle2; - mstudioanimvalue_t *panimvalue; - - for (j = 0; j < 3; j++) - { - if (panim->offset[j+3] == 0) - { - angle2[j] = angle1[j] = pbone->value[j+3]; // default; - } - else - { - panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]); - k = frame; - // DEBUG - if (panimvalue->num.total < panimvalue->num.valid) - k = 0; - while (panimvalue->num.total <= k) - { - k -= panimvalue->num.total; - panimvalue += panimvalue->num.valid + 1; - // DEBUG - if (panimvalue->num.total < panimvalue->num.valid) - k = 0; - } - // Bah, missing blend! - if (panimvalue->num.valid > k) - { - angle1[j] = panimvalue[k+1].value; - - if (panimvalue->num.valid > k + 1) - { - angle2[j] = panimvalue[k+2].value; - } - else - { - if (panimvalue->num.total > k + 1) - angle2[j] = angle1[j]; - else - angle2[j] = panimvalue[panimvalue->num.valid+2].value; - } - } - else - { - angle1[j] = panimvalue[panimvalue->num.valid].value; - if (panimvalue->num.total > k + 1) - { - angle2[j] = angle1[j]; - } - else - { - angle2[j] = panimvalue[panimvalue->num.valid + 2].value; - } - } - angle1[j] = pbone->value[j+3] + angle1[j] * pbone->scale[j+3]; - angle2[j] = pbone->value[j+3] + angle2[j] * pbone->scale[j+3]; - } - - /* - if (pbone->bonecontroller[j+3] != -1) - { - angle1[j] += adj[pbone->bonecontroller[j+3]]; - angle2[j] += adj[pbone->bonecontroller[j+3]]; - } - */ - - } - - if (!VectorCompare( angle1, angle2 )) - { - NS_AngleQuaternion( angle1, q1 ); - NS_AngleQuaternion( angle2, q2 ); - NS_QuaternionSlerp( q1, q2, s, q ); - } - else - { - NS_AngleQuaternion( angle1, q ); - } -} - -//----------------------------------------------------------------------------- - -float NS_StudioEstimateFrame( mstudioseqdesc_t *pseqdesc, const NS_AnimationData& inAnimationData, float time, float inFrame) -{ - - float dfdt; - float f; - - if ( /*m_fDoInterp*/ 1 ) - { - if ( time < inAnimationData.mTime ) - { - dfdt = 0; - } - else - { - dfdt = (time - inAnimationData.mTime) * inAnimationData.mFrameRate * pseqdesc->fps; - - } - } - else - { - dfdt = 0; - } - - if (pseqdesc->numframes <= 1) - { - f = 0; - } - else - { - f = (inFrame * (pseqdesc->numframes - 1)) / 256.0f; - } - - f += dfdt; - - if (pseqdesc->flags & STUDIO_LOOPING) - { - if (pseqdesc->numframes > 1) - { - f -= (int)(f / (pseqdesc->numframes - 1)) * (pseqdesc->numframes - 1); - } - if (f < 0) - { - f += (pseqdesc->numframes - 1); - } - } - else - { - if (f >= pseqdesc->numframes - 1.001f) - { - f = pseqdesc->numframes - 1.001f; - } - if (f < 0.0) - { - f = 0.0; - } - } - - // This logic is from CStudioModelRenderer::StudioCalcRotations. - - if (f > pseqdesc->numframes - 1) - { - f = 0; - } - else if (f < -0.01f) - { - f = -0.01f; - } - - return f; - -} - -//----------------------------------------------------------------------------- - -mstudioanim_t* NS_GetAnimation(studiohdr_t* inModelHeader, mstudioseqdesc_t* inSequence) -{ - mstudioseqgroup_t* theSequenceGroup = (mstudioseqgroup_t*)((byte *)inModelHeader + inModelHeader->seqgroupindex) + inSequence->seqgroup; - // joev: 0000573 - // Unless we actually check for null, we can get null references... - if (theSequenceGroup) { - return (mstudioanim_t*)((byte*)inModelHeader + theSequenceGroup->data + inSequence->animindex); - } - else { - return NULL; - } - // :joev -} - -//----------------------------------------------------------------------------- - -void NS_GetBoneMatrices(const NS_AnimationData& inAnimationData, float time, NS_Matrix3x4 outBoneMatrix[]) -{ - if (!inAnimationData.mModelHeader || inAnimationData.mSequence < 0 || inAnimationData.mFrame < 0) - { - return; - } - - studiohdr_t* theModelHeader = inAnimationData.mModelHeader; - - // Get the world to object space transformation for the entity. - - mstudioseqdesc_t* theSequence = (mstudioseqdesc_t*)((byte*)theModelHeader + theModelHeader->seqindex) + inAnimationData.mSequence; - - if (!theSequence) { - return; - } - - float f = NS_StudioEstimateFrame(theSequence, inAnimationData, time, inAnimationData.mFrame); - - int frame = (int)f; - float s = (f - frame); - - mstudiobone_t* theBones = (mstudiobone_t*)((byte*)theModelHeader + theModelHeader->boneindex); - mstudiobbox_t* theHitBoxes = (mstudiobbox_t*)((byte*)theModelHeader + theModelHeader->hitboxindex); - - // joev: 0000573 - // Unless we actually check for null, we can get null references... - // Regardless if the model is borked, the server shouldn't crash. - // Also, why have NS_GetAnimation when it's not used? - mstudioanim_t* theAnimation = NS_GetAnimation(theModelHeader,theSequence); - - if (!theBones|| !theHitBoxes|| !theAnimation) - { - return; - } - // :joev - - // Get the position and orientation of all of the bones in the skeleton. - - vec3_t theBonePos[MAXSTUDIOBONES]; - vec4_t theBoneAngles[MAXSTUDIOBONES]; - - int i; - - for (i = 0; i < theModelHeader->numbones; ++i) - { - NS_CalcBonePosition(frame, s, &theBones[i], &theAnimation[i], NULL, theBonePos[i]); - NS_CalcBoneAngles(frame, s, &theBones[i], &theAnimation[i], NULL, theBoneAngles[i]); - } - - // Take the gait sequence into account. - - if (inAnimationData.mGaitSequence != 0 && inAnimationData.mGaitSequence != 255) - { - - int theGaitSequenceIndex = max(min(inAnimationData.mGaitSequence, theModelHeader->numseq - 1), 0); - - mstudioseqdesc_t* theGaitSequence = (mstudioseqdesc_t*)((byte*)theModelHeader + theModelHeader->seqindex) + theGaitSequenceIndex; - mstudioanim_t* theGaitAnimation = NS_GetAnimation(theModelHeader, theGaitSequence); - - // Compute the frame in the gait animation. - - float theGaitFrame = time * theGaitSequence->fps; - - while (theGaitFrame >= theGaitSequence->numframes) - { - theGaitFrame -= theGaitSequence->numframes; - } - - theGaitFrame = theGaitFrame * 256 / (theGaitSequence->numframes - 1); - - float f = NS_StudioEstimateFrame(theGaitSequence, inAnimationData, time, theGaitFrame); - - int frame = (int)f; - float s = (f - frame); - - for (i = 0; i < theModelHeader->numbones; i++) - { - - if (strcmp(theBones[i].name, "Bip01 Spine") == 0) - { - break; - } - - NS_CalcBonePosition(frame, s, &theBones[i], &theGaitAnimation[i], NULL, theBonePos[i]); - NS_CalcBoneAngles(frame, s, &theBones[i], &theGaitAnimation[i], NULL, theBoneAngles[i]); - - } - - } - - for (i = 0; i < theModelHeader->numbones; i++) - { - - NS_Matrix3x4 theRelMatrix; - NS_QuaternionMatrix(theBoneAngles[i], theRelMatrix); - - theRelMatrix[0][3] = theBonePos[i][0]; - theRelMatrix[1][3] = theBonePos[i][1]; - theRelMatrix[2][3] = theBonePos[i][2]; - - if (theBones[i].parent == -1) - { - NS_ConcatTransforms(inAnimationData.mMatrix, theRelMatrix, outBoneMatrix[i]); - } - else - { - NS_ConcatTransforms(outBoneMatrix[theBones[i].parent], theRelMatrix, outBoneMatrix[i]); - } - - } - +#include +#include "types.h" + +#ifdef AVH_CLIENT +#include "cl_dll/wrect.h" +#include "cl_dll/cl_dll.h" +#include "cl_dll/r_studioint.h" +#include "common/com_model.h" +#include "common/cl_entity.h" +#include "common/vec_op.h" +#include "cl_dll/studio_util.h" + +extern engine_studio_api_t IEngineStudio; + +#endif + +#ifdef AVH_SERVER +#include "common/mathlib.h" +#include "common/const.h" +#include "engine/eiface.h" +#include "engine/edict.h" +#include "dlls/enginecallback.h" +#endif + +#include "mod/AnimationUtil.h" +#include "mod/AvHSpecials.h" +#include "util/MathUtil.h" + +#define PITCH 0 +#define YAW 1 +#define ROLL 2 + +//----------------------------------------------------------------------------- + +void NS_AngleMatrix (const float *angles, float (*matrix)[4] ) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + angle = angles[YAW] * (float(M_PI)*2 / 360); + sy = sinf(angle); + cy = cosf(angle); + angle = angles[PITCH] * (float(M_PI)*2 / 360); + sp = sinf(angle); + cp = cosf(angle); + angle = angles[ROLL] * (float(M_PI)*2 / 360); + sr = sinf(angle); + cr = cosf(angle); + + // matrix = (YAW * PITCH) * ROLL + matrix[0][0] = cp*cy; + matrix[1][0] = cp*sy; + matrix[2][0] = -sp; + matrix[0][1] = sr*sp*cy+cr*-sy; + matrix[1][1] = sr*sp*sy+cr*cy; + matrix[2][1] = sr*cp; + matrix[0][2] = (cr*sp*cy+-sr*-sy); + matrix[1][2] = (cr*sp*sy+-sr*cy); + matrix[2][2] = cr*cp; + matrix[0][3] = 0.0; + matrix[1][3] = 0.0; + matrix[2][3] = 0.0; +} + +//----------------------------------------------------------------------------- + +#ifdef AVH_SERVER + +bool NS_GetEntityAnimationData(int inEntityIndex, NS_AnimationData& outAnimationData) +{ + + edict_t* theEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex); + + if (theEdict == NULL) + { + return false; + } + + vec3_t theAngles; + + if (theEdict->v.iuser3 == AVH_USER3_ALIEN_PLAYER1) + { + VectorCopy(theEdict->v.vuser1, theAngles); + } + else + { + VectorCopy(theEdict->v.angles, theAngles); + } + + NS_AngleMatrix(theAngles, outAnimationData.mMatrix); + + outAnimationData.mMatrix[0][3] = theEdict->v.origin[0]; + outAnimationData.mMatrix[1][3] = theEdict->v.origin[1]; + outAnimationData.mMatrix[2][3] = theEdict->v.origin[2]; + + outAnimationData.mTime = theEdict->v.animtime; + outAnimationData.mFrame = theEdict->v.frame; + outAnimationData.mFrameRate = theEdict->v.framerate; + outAnimationData.mModelHeader = (studiohdr_t*)(GET_MODEL_PTR(theEdict)); + outAnimationData.mSequence = theEdict->v.sequence; + outAnimationData.mGaitSequence = theEdict->v.gaitsequence; + + // Get the bounding box for the sequence. + + studiohdr_t* theModelHeader = outAnimationData.mModelHeader; + + if (outAnimationData.mModelHeader != NULL) + { + + mstudioseqdesc_t* theSequence = (mstudioseqdesc_t*)((byte*)theModelHeader + theModelHeader->seqindex) + outAnimationData.mSequence; + + VectorCopy(theSequence->bbmin, outAnimationData.mMins); + VectorCopy(theSequence->bbmax, outAnimationData.mMaxs); + + } + + return true; + +} + +#endif + +//----------------------------------------------------------------------------- + +#ifdef AVH_CLIENT + +bool NS_GetEntityAnimationData(int inEntityIndex, NS_AnimationData& outAnimationData) +{ + + cl_entity_t* theEntity = gEngfuncs.GetEntityByIndex(inEntityIndex); + + if (theEntity == NULL || theEntity->model == NULL) + { + return false; + } + + vec3_t theAngles; + + if (theEntity->curstate.iuser3 == AVH_USER3_ALIEN_PLAYER1) + { + VectorCopy(theEntity->curstate.vuser1, theAngles); + } + else + { + VectorCopy(theEntity->curstate.angles, theAngles); + } + + NS_AngleMatrix(theAngles, outAnimationData.mMatrix); + + outAnimationData.mMatrix[0][3] = theEntity->curstate.origin[0]; + outAnimationData.mMatrix[1][3] = theEntity->curstate.origin[1]; + outAnimationData.mMatrix[2][3] = theEntity->curstate.origin[2]; + + outAnimationData.mTime = theEntity->curstate.animtime; + outAnimationData.mFrame = theEntity->curstate.frame; + outAnimationData.mFrameRate = theEntity->curstate.framerate; + outAnimationData.mModelHeader = (studiohdr_t *)IEngineStudio.Mod_Extradata(theEntity->model); + outAnimationData.mSequence = theEntity->curstate.sequence; + outAnimationData.mGaitSequence = theEntity->curstate.gaitsequence; + + // Get the bounding box for the sequence. + + studiohdr_t* theModelHeader = outAnimationData.mModelHeader; + + if (outAnimationData.mModelHeader != NULL) + { + + mstudioseqdesc_t* theSequence = (mstudioseqdesc_t*)((byte*)theModelHeader + theModelHeader->seqindex) + outAnimationData.mSequence; + + VectorCopy(theSequence->bbmin, outAnimationData.mMins); + VectorCopy(theSequence->bbmax, outAnimationData.mMaxs); + + } + + return true; + +} + +#endif + +//----------------------------------------------------------------------------- + +void NS_AngleQuaternion( float *angles, vec4_t quaternion ) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + // FIXME: rescale the inputs to 1/2 angle + angle = angles[2] * 0.5f; + sy = sinf(angle); + cy = cosf(angle); + angle = angles[1] * 0.5f; + sp = sinf(angle); + cp = cosf(angle); + angle = angles[0] * 0.5f; + sr = sinf(angle); + cr = cosf(angle); + + quaternion[0] = sr*cp*cy-cr*sp*sy; // X + quaternion[1] = cr*sp*cy+sr*cp*sy; // Y + quaternion[2] = cr*cp*sy-sr*sp*cy; // Z + quaternion[3] = cr*cp*cy+sr*sp*sy; // W +} + +//----------------------------------------------------------------------------- + +void NS_QuaternionSlerp( vec4_t p, vec4_t q, float t, vec4_t qt ) +{ + + int i; + float omega, cosom, sinom, sclp, sclq; + + // decide if one of the quaternions is backwards + float a = 0; + float b = 0; + + for (i = 0; i < 4; i++) + { + a += (p[i]-q[i])*(p[i]-q[i]); + b += (p[i]+q[i])*(p[i]+q[i]); + } + if (a > b) + { + for (i = 0; i < 4; i++) + { + q[i] = -q[i]; + } + } + + cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3]; + + if ((1.0 + cosom) > 0.000001f) + { + if ((1.0 - cosom) > 0.000001f) + { + omega = acosf( cosom ); + sinom = sinf( omega ); + sclp = sinf( (1.0f - t)*omega) / sinom; + sclq = sinf( t*omega ) / sinom; + } + else + { + sclp = 1.0f - t; + sclq = t; + } + for (i = 0; i < 4; i++) { + qt[i] = sclp * p[i] + sclq * q[i]; + } + } + else + { + qt[0] = -q[1]; + qt[1] = q[0]; + qt[2] = -q[3]; + qt[3] = q[2]; + sclp = sinf( (1.0f - t) * (0.5f * float(M_PI))); + sclq = sinf( t * (0.5f * float(M_PI))); + for (i = 0; i < 3; i++) + { + qt[i] = sclp * p[i] + sclq * qt[i]; + } + } +} + +//----------------------------------------------------------------------------- + +void NS_QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] ) +{ + matrix[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2]; + matrix[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2]; + matrix[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1]; + + matrix[0][1] = 2.0f * quaternion[0] * quaternion[1] - 2.0f * quaternion[3] * quaternion[2]; + matrix[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2]; + matrix[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0]; + + matrix[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1]; + matrix[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0]; + matrix[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1]; +} + +//----------------------------------------------------------------------------- + +void NS_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4]) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + + in1[0][2] * in2[2][3] + in1[0][3]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + + in1[1][2] * in2[2][3] + in1[1][3]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; + out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + + in1[2][2] * in2[2][3] + in1[2][3]; +} + +//----------------------------------------------------------------------------- + +void NS_CalcBonePosition(int frame, float s, mstudiobone_t* pbone, mstudioanim_t* panim, float* adj, float* pos) +{ + + // This is ripped out of StudioModelRenderer. + + int j, k; + mstudioanimvalue_t *panimvalue; + + for (j = 0; j < 3; j++) + { + pos[j] = pbone->value[j]; // default; + if (panim->offset[j] != 0) + { + panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]); + /* + if (i == 0 && j == 0) + Con_DPrintf("%d %d:%d %f\n", frame, panimvalue->num.valid, panimvalue->num.total, s ); + */ + + k = frame; + // DEBUG + if (panimvalue->num.total < panimvalue->num.valid) + k = 0; + // find span of values that includes the frame we want + while (panimvalue->num.total <= k) + { + k -= panimvalue->num.total; + panimvalue += panimvalue->num.valid + 1; + // DEBUG + if (panimvalue->num.total < panimvalue->num.valid) + k = 0; + } + // if we're inside the span + if (panimvalue->num.valid > k) + { + // and there's more data in the span + if (panimvalue->num.valid > k + 1) + { + pos[j] += (panimvalue[k+1].value * (1.0f - s) + s * panimvalue[k+2].value) * pbone->scale[j]; + } + else + { + pos[j] += panimvalue[k+1].value * pbone->scale[j]; + } + } + else + { + // are we at the end of the repeating values section and there's another section with data? + if (panimvalue->num.total <= k + 1) + { + pos[j] += (panimvalue[panimvalue->num.valid].value * (1.0f - s) + s * panimvalue[panimvalue->num.valid + 2].value) * pbone->scale[j]; + } + else + { + pos[j] += panimvalue[panimvalue->num.valid].value * pbone->scale[j]; + } + } + } + if ( pbone->bonecontroller[j] != -1 && adj ) + { + pos[j] += adj[pbone->bonecontroller[j]]; + } + } +} + +//----------------------------------------------------------------------------- + +void NS_CalcBoneAngles( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *q ) +{ + int j, k; + vec4_t q1, q2; + vec3_t angle1, angle2; + mstudioanimvalue_t *panimvalue; + + for (j = 0; j < 3; j++) + { + if (panim->offset[j+3] == 0) + { + angle2[j] = angle1[j] = pbone->value[j+3]; // default; + } + else + { + panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]); + k = frame; + // DEBUG + if (panimvalue->num.total < panimvalue->num.valid) + k = 0; + while (panimvalue->num.total <= k) + { + k -= panimvalue->num.total; + panimvalue += panimvalue->num.valid + 1; + // DEBUG + if (panimvalue->num.total < panimvalue->num.valid) + k = 0; + } + // Bah, missing blend! + if (panimvalue->num.valid > k) + { + angle1[j] = panimvalue[k+1].value; + + if (panimvalue->num.valid > k + 1) + { + angle2[j] = panimvalue[k+2].value; + } + else + { + if (panimvalue->num.total > k + 1) + angle2[j] = angle1[j]; + else + angle2[j] = panimvalue[panimvalue->num.valid+2].value; + } + } + else + { + angle1[j] = panimvalue[panimvalue->num.valid].value; + if (panimvalue->num.total > k + 1) + { + angle2[j] = angle1[j]; + } + else + { + angle2[j] = panimvalue[panimvalue->num.valid + 2].value; + } + } + angle1[j] = pbone->value[j+3] + angle1[j] * pbone->scale[j+3]; + angle2[j] = pbone->value[j+3] + angle2[j] * pbone->scale[j+3]; + } + + /* + if (pbone->bonecontroller[j+3] != -1) + { + angle1[j] += adj[pbone->bonecontroller[j+3]]; + angle2[j] += adj[pbone->bonecontroller[j+3]]; + } + */ + + } + + if (!VectorCompare( angle1, angle2 )) + { + NS_AngleQuaternion( angle1, q1 ); + NS_AngleQuaternion( angle2, q2 ); + NS_QuaternionSlerp( q1, q2, s, q ); + } + else + { + NS_AngleQuaternion( angle1, q ); + } +} + +//----------------------------------------------------------------------------- + +float NS_StudioEstimateFrame( mstudioseqdesc_t *pseqdesc, const NS_AnimationData& inAnimationData, float time, float inFrame) +{ + + float dfdt; + float f; + + if ( /*m_fDoInterp*/ 1 ) + { + if ( time < inAnimationData.mTime ) + { + dfdt = 0; + } + else + { + dfdt = (time - inAnimationData.mTime) * inAnimationData.mFrameRate * pseqdesc->fps; + + } + } + else + { + dfdt = 0; + } + + if (pseqdesc->numframes <= 1) + { + f = 0; + } + else + { + f = (inFrame * (pseqdesc->numframes - 1)) / 256.0f; + } + + f += dfdt; + + if (pseqdesc->flags & STUDIO_LOOPING) + { + if (pseqdesc->numframes > 1) + { + f -= (int)(f / (pseqdesc->numframes - 1)) * (pseqdesc->numframes - 1); + } + if (f < 0) + { + f += (pseqdesc->numframes - 1); + } + } + else + { + if (f >= pseqdesc->numframes - 1.001f) + { + f = pseqdesc->numframes - 1.001f; + } + if (f < 0.0) + { + f = 0.0; + } + } + + // This logic is from CStudioModelRenderer::StudioCalcRotations. + + if (f > pseqdesc->numframes - 1) + { + f = 0; + } + else if (f < -0.01f) + { + f = -0.01f; + } + + return f; + +} + +//----------------------------------------------------------------------------- + +mstudioanim_t* NS_GetAnimation(studiohdr_t* inModelHeader, mstudioseqdesc_t* inSequence) +{ + mstudioseqgroup_t* theSequenceGroup = (mstudioseqgroup_t*)((byte *)inModelHeader + inModelHeader->seqgroupindex) + inSequence->seqgroup; + // : 0000573 + // Unless we actually check for null, we can get null references... + if (theSequenceGroup) { + return (mstudioanim_t*)((byte*)inModelHeader + theSequenceGroup->data + inSequence->animindex); + } + else { + return NULL; + } + // : +} + +//----------------------------------------------------------------------------- + +void NS_GetBoneMatrices(const NS_AnimationData& inAnimationData, float time, NS_Matrix3x4 outBoneMatrix[]) +{ + if (!inAnimationData.mModelHeader || inAnimationData.mSequence < 0 || inAnimationData.mFrame < 0) + { + return; + } + + studiohdr_t* theModelHeader = inAnimationData.mModelHeader; + + // Get the world to object space transformation for the entity. + + mstudioseqdesc_t* theSequence = (mstudioseqdesc_t*)((byte*)theModelHeader + theModelHeader->seqindex) + inAnimationData.mSequence; + + if (!theSequence) { + return; + } + + float f = NS_StudioEstimateFrame(theSequence, inAnimationData, time, inAnimationData.mFrame); + + int frame = (int)f; + float s = (f - frame); + + mstudiobone_t* theBones = (mstudiobone_t*)((byte*)theModelHeader + theModelHeader->boneindex); + mstudiobbox_t* theHitBoxes = (mstudiobbox_t*)((byte*)theModelHeader + theModelHeader->hitboxindex); + + // : 0000573 + // Unless we actually check for null, we can get null references... + // Regardless if the model is borked, the server shouldn't crash. + // Also, why have NS_GetAnimation when it's not used? + mstudioanim_t* theAnimation = NS_GetAnimation(theModelHeader,theSequence); + + if (!theBones|| !theHitBoxes|| !theAnimation) + { + return; + } + // : + + // Get the position and orientation of all of the bones in the skeleton. + + vec3_t theBonePos[MAXSTUDIOBONES]; + vec4_t theBoneAngles[MAXSTUDIOBONES]; + + int i; + + for (i = 0; i < theModelHeader->numbones; ++i) + { + NS_CalcBonePosition(frame, s, &theBones[i], &theAnimation[i], NULL, theBonePos[i]); + NS_CalcBoneAngles(frame, s, &theBones[i], &theAnimation[i], NULL, theBoneAngles[i]); + } + + // Take the gait sequence into account. + + if (inAnimationData.mGaitSequence != 0 && inAnimationData.mGaitSequence != 255) + { + + int theGaitSequenceIndex = max(min(inAnimationData.mGaitSequence, theModelHeader->numseq - 1), 0); + + mstudioseqdesc_t* theGaitSequence = (mstudioseqdesc_t*)((byte*)theModelHeader + theModelHeader->seqindex) + theGaitSequenceIndex; + mstudioanim_t* theGaitAnimation = NS_GetAnimation(theModelHeader, theGaitSequence); + + // Compute the frame in the gait animation. + + float theGaitFrame = time * theGaitSequence->fps; + + while (theGaitFrame >= theGaitSequence->numframes) + { + theGaitFrame -= theGaitSequence->numframes; + } + + theGaitFrame = theGaitFrame * 256 / (theGaitSequence->numframes - 1); + + float f = NS_StudioEstimateFrame(theGaitSequence, inAnimationData, time, theGaitFrame); + + int frame = (int)f; + float s = (f - frame); + + for (i = 0; i < theModelHeader->numbones; i++) + { + + if (strcmp(theBones[i].name, "Bip01 Spine") == 0) + { + break; + } + + NS_CalcBonePosition(frame, s, &theBones[i], &theGaitAnimation[i], NULL, theBonePos[i]); + NS_CalcBoneAngles(frame, s, &theBones[i], &theGaitAnimation[i], NULL, theBoneAngles[i]); + + } + + } + + for (i = 0; i < theModelHeader->numbones; i++) + { + + NS_Matrix3x4 theRelMatrix; + NS_QuaternionMatrix(theBoneAngles[i], theRelMatrix); + + theRelMatrix[0][3] = theBonePos[i][0]; + theRelMatrix[1][3] = theBonePos[i][1]; + theRelMatrix[2][3] = theBonePos[i][2]; + + if (theBones[i].parent == -1) + { + NS_ConcatTransforms(inAnimationData.mMatrix, theRelMatrix, outBoneMatrix[i]); + } + else + { + NS_ConcatTransforms(outBoneMatrix[theBones[i].parent], theRelMatrix, outBoneMatrix[i]); + } + + } + } \ No newline at end of file diff --git a/main/source/mod/AvHAlienAbilities.cpp b/main/source/mod/AvHAlienAbilities.cpp index affafc6..0428468 100644 --- a/main/source/mod/AvHAlienAbilities.cpp +++ b/main/source/mod/AvHAlienAbilities.cpp @@ -57,6 +57,9 @@ #include "cl_dll/hud.h" #include "mod/AvHHud.h" extern int g_runfuncs; +void IN_Attack2Down(); +void IN_Attack2Up(); +bool CheckInAttack(); #endif LINK_ENTITY_TO_CLASS(kwLeap, AvHLeap); @@ -123,8 +126,8 @@ void AvHLeap::Precache(void) PRECACHE_UNMODIFIED_SOUND(kLeapSound); PRECACHE_UNMODIFIED_SOUND(kLeapHitSound1); PRECACHE_UNMODIFIED_SOUND(kLeapKillSound); - - this->mEvent = PRECACHE_EVENT(1, kLeapEventName); + + this->mLeapEvent = PRECACHE_EVENT(1, kLeapEventName); this->mAbilityEvent = PRECACHE_EVENT(1, kAbilityEventName); } @@ -144,11 +147,23 @@ void AvHLeap::Spawn() FallInit();// get ready to fall down. } +float AvHLeap::GetRateOfFire(void) const +{ + return (float)BALANCE_VAR(kLeapROF);// * 0.5f; +} + bool AvHLeap::UsesAmmo(void) const { return false; } +void AvHLeap::SecondaryAttack() +{ +#ifdef AVH_CLIENT + this->FireProjectiles(); +#endif +} + void AvHLeap::FireProjectiles(void) { #ifdef AVH_SERVER @@ -161,6 +176,8 @@ void AvHLeap::FireProjectiles(void) #ifdef AVH_CLIENT if(g_runfuncs) { + //IN_Attack2Down(); + //CBasePlayerWeapon::SendWeaponAnim(3); gHUD.SetAlienAbility(this->GetAbilityImpulse()); } #endif @@ -220,7 +237,7 @@ int AvHCharge::GetDeployAnimation() const float AvHCharge::GetDeployTime() const { - return .6f; + return 0.0f; //.6f; } bool AvHCharge::GetFiresUnderwater() const @@ -250,7 +267,7 @@ void AvHCharge::Precache(void) PRECACHE_UNMODIFIED_MODEL(kLevel5ViewModel); PRECACHE_UNMODIFIED_MODEL(kNullModel); - PRECACHE_UNMODIFIED_SOUND(kChargeSound); + PRECACHE_UNMODIFIED_SOUND(kChargeSound2); PRECACHE_UNMODIFIED_SOUND(kChargeKillSound); this->mEvent = PRECACHE_EVENT(1, kChargeEventName); @@ -278,14 +295,30 @@ bool AvHCharge::UsesAmmo(void) const return false; } +void AvHCharge::SecondaryAttack() +{ +#ifdef AVH_CLIENT + this->FireProjectiles(); +#endif +} + void AvHCharge::FireProjectiles(void) { +#ifdef AVH_CLIENT + if (CheckInAttack()) + IN_Attack2Down(); + else + IN_Attack2Up(); + + //gHUD.SetAlienAbility(this->GetAbilityImpulse()); +#endif + // Event is played back. Mark pmove with proper flag so the alien Charges forward. - PLAYBACK_EVENT_FULL(0, this->m_pPlayer->edict(), this->mAbilityEvent, 0, this->m_pPlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, this->GetAbilityImpulse(), 0, 1, 0 ); + //PLAYBACK_EVENT_FULL(0, this->m_pPlayer->edict(), this->mAbilityEvent, 0, this->m_pPlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, this->GetAbilityImpulse(), 0, 1, 0 ); // Send fire anim //SendWeaponAnim(5); - this->PlaybackEvent(this->mWeaponAnimationEvent, 5); + //this->PlaybackEvent(this->mWeaponAnimationEvent, 5); } void AvHCharge::Init() @@ -295,6 +328,6 @@ void AvHCharge::Init() float AvHCharge::GetRateOfFire() const { // Approximate length of charge sound - return 5.0f; + return 1.0f; } diff --git a/main/source/mod/AvHAlienAbilities.h b/main/source/mod/AvHAlienAbilities.h index 01812fb..df725c9 100644 --- a/main/source/mod/AvHAlienAbilities.h +++ b/main/source/mod/AvHAlienAbilities.h @@ -45,7 +45,9 @@ class AvHAlienAbilityWeapon : public AvHAlienWeapon { public: virtual AvHMessageID GetAbilityImpulse() const = 0; + virtual void PlaybackLeapEvent(void) { this->PlaybackEvent(this->mLeapEvent, this->GetShootAnimation());}; int mAbilityEvent; + int mLeapEvent; }; class AvHLeap : public AvHAlienAbilityWeapon @@ -83,6 +85,10 @@ public: virtual bool UsesAmmo(void) const; virtual BOOL GetTakesEnergy() { return FALSE; } + void SecondaryAttack(); + + virtual float GetRateOfFire(void) const; + protected: virtual void FireProjectiles(void); @@ -129,6 +135,8 @@ public: virtual bool UsesAmmo(void) const; virtual BOOL GetTakesEnergy() { return FALSE; } + + void SecondaryAttack(); protected: virtual void FireProjectiles(void); @@ -174,6 +182,8 @@ public: virtual bool UsesAmmo(void) const; virtual float GetRateOfFire() const; + + void SecondaryAttack(); protected: virtual void FireProjectiles(void); diff --git a/main/source/mod/AvHAlienAbilityConstants.h b/main/source/mod/AvHAlienAbilityConstants.h index f9154fc..5a6e8ac 100644 --- a/main/source/mod/AvHAlienAbilityConstants.h +++ b/main/source/mod/AvHAlienAbilityConstants.h @@ -1,70 +1,74 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHAlienAbilityConstants.h$ -// $Date: 2002/11/22 22:01:00 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHAlienAbilityConstants.h,v $ -// Revision 1.8 2002/11/22 22:01:00 Flayra -// - Case-sensitive problem that is now an issue for linux with mp_consistency -// -// Revision 1.7 2002/10/16 00:45:44 Flayra -// - Toned down leap and charge damage (is this frame-rate dependent?) -// -// Revision 1.6 2002/08/16 02:31:01 Flayra -// - Big balance change: all weapons reduced by 20% damage -// -// Revision 1.5 2002/06/25 17:26:29 Flayra -// - Regular update for leap and charge -// -// Revision 1.4 2002/06/03 16:20:35 Flayra -// - Removed outdated v_leap.mdl (!) -// -// Revision 1.3 2002/05/23 02:34:00 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifndef AVH_ALIEN_ABILITY_CONSTANTS_H -#define AVH_ALIEN_ABILITY_CONSTANTS_H - -// Leap constants. -const int kLeapRange = 600; -const float kLeapPunch = 2.5; -#define kLeapSound "weapons/leap1.wav" -#define kLeapHitSound1 "weapons/leaphit1.wav" -#define kLeapKillSound "weapons/leapkill.wav" -#define kLeapEventName "events/Leap.sc" -#define kLeapPModel "models/null.mdl" -const float kLeapROF = 1.5f; -const float kLeapDuration = 1.0f; - -// Charge constants. -const float kChargePunch = 2.5; -#define kChargeSound "weapons/charge1.wav" -#define kChargeKillSound "weapons/chargekill.wav" -#define kChargeEventName "events/Charge.sc" -const float kChargeROF = 5.0f; - -#define kAlienSightOnSound "misc/aliensighton.wav" -#define kAlienSightOffSound "misc/aliensightoff.wav" - -const int kAlienCloakRenderMode = kRenderTransTexture; -const int kAlienCloakAmount = 25; -// puzl: 1061 full cloaking -const int kAlienStructureCloakAmount = 0; -// :puzl - -const int kAlienSelfCloakingBaseOpacity = 130; - -const int kAlienCloakViewModelRenderMode = kRenderTransAdd; -const int kAlienCloakViewModelAmount = 35; -const int kAlienCloakViewModelLevelAmount = 10; - +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHAlienAbilityConstants.h$ +// $Date: 2002/11/22 22:01:00 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHAlienAbilityConstants.h,v $ +// Revision 1.8 2002/11/22 22:01:00 Flayra +// - Case-sensitive problem that is now an issue for linux with mp_consistency +// +// Revision 1.7 2002/10/16 00:45:44 Flayra +// - Toned down leap and charge damage (is this frame-rate dependent?) +// +// Revision 1.6 2002/08/16 02:31:01 Flayra +// - Big balance change: all weapons reduced by 20% damage +// +// Revision 1.5 2002/06/25 17:26:29 Flayra +// - Regular update for leap and charge +// +// Revision 1.4 2002/06/03 16:20:35 Flayra +// - Removed outdated v_leap.mdl (!) +// +// Revision 1.3 2002/05/23 02:34:00 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifndef AVH_ALIEN_ABILITY_CONSTANTS_H +#define AVH_ALIEN_ABILITY_CONSTANTS_H + +// Leap constants. +const int kLeapRange = 600; +const float kLeapPunch = 2.5; +#define kLeapSound "weapons/leap1.wav" +#define kLeapHitSound1 "weapons/leaphit1.wav" +#define kLeapKillSound "weapons/leapkill.wav" +#define kLeapEventName "events/Leap.sc" +#define kLeapPModel "models/null.mdl" +const float kLeapDuration = 1.0f; + +// Charge constants. +const float kChargePunch = 2.5; +#define kChargeSound "weapons/charge1.wav" +#define kChargeSound2 "weapons/charge2.wav" +#define kChargeKillSound "weapons/chargekill.wav" +#define kChargeEventName "events/Charge.sc" +const float kChargeROF = 5.0f; + +#define kAlienSightOnSound "misc/aliensighton.wav" +#define kAlienSightOffSound "misc/aliensightoff.wav" + +// Blink constants +#define kBlinkSound "weapons/blinksuccess.wav" + +const int kAlienCloakRenderMode = kRenderTransTexture; +const int kAlienCloakAmount = 25; +// : 1061 full cloaking +const int kAlienStructureCloakAmount = 0; +// : + +const int kAlienSelfCloakingBaseOpacity = 130; +const int kAlienSelfCloakingMinOpacity = 186; + +const int kAlienCloakViewModelRenderMode = kRenderTransAdd; +const int kAlienCloakViewModelAmount = 35; +const int kAlienCloakViewModelLevelAmount = 10; + #endif \ No newline at end of file diff --git a/main/source/mod/AvHAlienEquipment.cpp b/main/source/mod/AvHAlienEquipment.cpp index 2662983..285e556 100644 --- a/main/source/mod/AvHAlienEquipment.cpp +++ b/main/source/mod/AvHAlienEquipment.cpp @@ -389,16 +389,18 @@ void AvHDefenseChamber::RegenAliensThink() { AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); + float thePercent=BALANCE_VAR(kDefensiveChamberRegenPercent)/100.0f; + float amount=BALANCE_VAR(kDefensiveChamberRegenAmount) + (theBaseEntity->pev->max_health*thePercent); if(thePlayer && thePlayer->IsAlive()) { - if(thePlayer->Heal(BALANCE_VAR(kDefensiveChamberRegenAmount))) + if(thePlayer->Heal(amount, true, true)) { theNumEntsHealed++; } } else if(theBuildable && theBuildable->GetIsBuilt() && (theBuildable != this)) { - if(theBuildable->Regenerate(BALANCE_VAR(kDefensiveChamberRegenAmount))) + if(theBuildable->Regenerate(amount, true, true)) { theNumEntsHealed++; } @@ -605,7 +607,7 @@ void AvHMovementChamber::TeleportUse(CBaseEntity* inActivator, CBaseEntity* inCa { bool theHiveIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntity->entindex()); - if(theEntity->GetIsActive() || theHiveIsUnderAttack) + if(theEntity->GetIsActive() || theHiveIsUnderAttack || theEntity->GetEmergencyUse() ) { float theCurrentDistance = VectorDistance(theEntity->pev->origin, inActivator->pev->origin); bool theHiveIsFarther = (theCurrentDistance > theFarthestDistance); @@ -655,7 +657,7 @@ void AvHMovementChamber::TeleportUse(CBaseEntity* inActivator, CBaseEntity* inCa { thePlayer->SetPosition(theOriginToSpawn); thePlayer->pev->velocity = Vector(0, 0, 0); - + thePlayer->TriggerUncloak(); // Play teleport sound before and after EMIT_SOUND(inActivator->edict(), CHAN_AUTO, kAlienSightOffSound, 1.0f, ATTN_NORM); } diff --git a/main/source/mod/AvHAlienEquipmentConstants.h b/main/source/mod/AvHAlienEquipmentConstants.h index f11f7ff..b3d6552 100644 --- a/main/source/mod/AvHAlienEquipmentConstants.h +++ b/main/source/mod/AvHAlienEquipmentConstants.h @@ -70,6 +70,9 @@ const float kWebThinkInterval = .1f; const int kWebStrandWidth = 60; const int kWebStrandLifetime = 50; #define kWebStrandBreakSound "misc/web_break.wav" +#define kWebStrandHardenSound "misc/web_harden.wav" +#define kWebStrandFormSound "misc/web_form.wav" +#define kWebStrandHitSound "misc/web_hit.wav" #define kAlienResourceTowerModel "models/ba_resource.mdl" diff --git a/main/source/mod/AvHAlienTurret.cpp b/main/source/mod/AvHAlienTurret.cpp index eeec0e0..561c241 100644 --- a/main/source/mod/AvHAlienTurret.cpp +++ b/main/source/mod/AvHAlienTurret.cpp @@ -305,7 +305,7 @@ void AvHAlienTurret::Shoot(const Vector &inOrigin, const Vector &inToEnemy, cons Vector theNetworkDirToEnemy; VectorScale(theDirToEnemy, 100.0f, theNetworkDirToEnemy); - PLAYBACK_EVENT_FULL(0, this->edict(), this->mEvent, 0, theOrigin, theNetworkDirToEnemy, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + PLAYBACK_EVENT_FULL(0, 0, this->mEvent, 0, theOrigin, theNetworkDirToEnemy, 1.0f, 0.0, /*theWeaponIndex*/ this->entindex(), 0, 0, 0 ); // Play attack anim this->PlayAnimationAtIndex(6, true); diff --git a/main/source/mod/AvHAlienWeapon.cpp b/main/source/mod/AvHAlienWeapon.cpp index 4fa9e5b..a9042c1 100644 --- a/main/source/mod/AvHAlienWeapon.cpp +++ b/main/source/mod/AvHAlienWeapon.cpp @@ -1,287 +1,300 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHAlienWeapon.cpp $ -// $Date: 2002/10/03 18:37:32 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHAlienWeapon.cpp,v $ -// Revision 1.9 2002/10/03 18:37:32 Flayra -// - Moved alien energy to fuser3 -// -// Revision 1.8 2002/09/23 22:08:59 Flayra -// - Updates to allow marine weapons stick around -// -// Revision 1.7 2002/08/31 18:01:00 Flayra -// - Work at VALVe -// -// Revision 1.6 2002/08/16 02:32:09 Flayra -// - Added damage types -// - Swapped umbra and bile bomb -// -// Revision 1.5 2002/07/08 16:43:26 Flayra -// - Web change? -// -// Revision 1.4 2002/06/25 17:29:56 Flayra -// - Better default behavior -// -// Revision 1.3 2002/06/03 16:25:10 Flayra -// - Switch alien weapons quickly, renamed CheckValidAttack() -// -// Revision 1.2 2002/05/28 17:37:14 Flayra -// - Reduced times where alien weapons are dropped and picked up again (but didn't eliminate, they are still being instantiated somewhere) -// -// Revision 1.1 2002/05/23 02:34:00 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHAlienWeapon.h" -#include "mod/AvHMovementUtil.h" -#include "mod/AvHSpecials.h" - -#ifdef AVH_CLIENT -#include "cl_dll/wrect.h" -#include "cl_dll/cl_dll.h" -#include "cl_dll/hud.h" -#include "cl_dll/cl_util.h" -extern int g_runfuncs; -#include "cl_dll/com_weapons.h" - -#include "pm_shared/pm_defs.h" -#include "pm_shared/pm_shared.h" -#include "pm_shared/pm_movevars.h" -extern playermove_t *pmove; -#endif - -#include "mod/AvHSharedUtil.h" -#include "util/Balance.h" - -AvHAlienWeapon::AvHAlienWeapon() -{ - #ifdef AVH_SERVER - this->SetGroundLifetime(0); - #endif -} - -bool AvHAlienWeapon::ProcessValidAttack(void) -{ - bool theCanAttack = false; - - if(AvHBasePlayerWeapon::ProcessValidAttack()) - { - if(this->IsUseable()) - { - theCanAttack = true; - } - } - - return theCanAttack; -} - -void AvHAlienWeapon::DeductCostForShot(void) -{ - if(this->GetTakesEnergy()) - { - AvHBasePlayerWeapon::DeductCostForShot(); - - float theEnergyCost = this->GetEnergyForAttack(); - float& theFuser = this->GetEnergyLevel(); - AvHMUDeductAlienEnergy(theFuser, theEnergyCost); - } -} - -// Cannot ever drop alien weapons -void AvHAlienWeapon::Drop(void) -{ -} - -bool AvHAlienWeapon::GetAllowedForUser3(AvHUser3 inUser3) -{ - bool theAllowed = false; - - // Alien weapons for aliens. Don't take into account exact roles until needed (and until weapons have stabilized) - switch(inUser3) - { - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: - theAllowed = true; - break; - } - - return theAllowed; -} - -int AvHAlienWeapon::GetDamageType() const -{ - // Assume melee attack, piercing - //return NS_DMG_PIERCING; - return NS_DMG_NORMAL; -} - -float AvHAlienWeapon::GetDeployTime() const -{ - // Aliens are Quake-style/arcadey, make weapon switch fast - return .1f; -} - -float AvHAlienWeapon::GetEnergyForAttack() const -{ - float theEnergy = 0.0f; - - AvHMUGetEnergyCost((AvHWeaponID)(this->m_iId), theEnergy); - - return theEnergy; -} - -bool AvHAlienWeapon::GetFiresUnderwater() const -{ - return true; -} - -// Never live on ground -int AvHAlienWeapon::GetGroundLifetime() const -{ - return 0; -} - -float& AvHAlienWeapon::GetEnergyLevel() -{ -#ifdef AVH_CLIENT - float& theFuser = pmove->fuser3; -#endif - -#ifdef AVH_SERVER - float& theFuser = this->m_pPlayer->pev->fuser3; -#endif - - return theFuser; -} - -bool AvHAlienWeapon::GetIsDroppable() const -{ - return false; -} - -bool AvHAlienWeapon::GetIsGunPositionValid() const -{ - // Check to make sure our gun barrel isn't coming out on the other side of a wall. - - vec3_t theBarrelOrigin; - vec3_t theBarrelEnd; - -#ifdef AVH_CLIENT - - extern vec3_t v_view_ofs; // In view.cpp - extern vec3_t v_cl_angles; - - cl_entity_s* thePlayer = gHUD.GetVisiblePlayer(); - - VectorAdd(thePlayer->curstate.origin, v_view_ofs, theBarrelOrigin); - - vec3_t theBarrelDirection; - AngleVectors(v_angles, theBarrelDirection, NULL, NULL); - - VectorMA(theBarrelOrigin, GetBarrelLength(), theBarrelDirection, theBarrelEnd); - -#endif - -#ifdef AVH_SERVER - - VectorCopy(m_pPlayer->GetGunPosition(), theBarrelOrigin); - VectorCopy(GetWorldBarrelPoint(), theBarrelEnd); - -#endif - - return AvHTraceLineAgainstWorld(theBarrelOrigin, theBarrelEnd) == 1; -} - -float AvHAlienWeapon::ComputeAttackInterval() const -{ - float theROF = this->GetRateOfFire(); - - int theUser4 = this->m_pPlayer->pev->iuser4; - - #ifdef AVH_CLIENT - cl_entity_t* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - if(theLocalPlayer) - { - theUser4 = theLocalPlayer->curstate.iuser4; - } - #endif - - // Speed attack if in range of primal scream - if(GetHasUpgrade(theUser4, MASK_BUFFED)) - { - float thePrimalScreamROFFactor = 1.0f + BALANCE_VAR(kPrimalScreamROFFactor); - theROF /= thePrimalScreamROFFactor; - } - - // Check for focus - if(AvHSHUGetIsWeaponFocusable(AvHWeaponID(this->m_iId))) - { - int theFocusLevel = AvHGetAlienUpgradeLevel(theUser4, MASK_UPGRADE_8); - if(theFocusLevel > 0) - { - // Slow down ROF by x% for each level - const float theFocusROFPercentSlowdownPerLevel = BALANCE_VAR(kFocusROFPercentSlowdownPerLevel); - float theSlowDownPercentage = 1.0f + theFocusLevel*theFocusROFPercentSlowdownPerLevel; - - theROF *= theSlowDownPercentage; - } - } - - return theROF; -} - -void AvHAlienWeapon::Precache(void) -{ - AvHBasePlayerWeapon::Precache(); -} - -void AvHAlienWeapon::Spawn() -{ - AvHBasePlayerWeapon::Spawn(); - - this->pev->iuser3 = AVH_USER3_NONE; - - #ifdef AVH_SERVER - this->SetGroundLifetime(0); - #endif -} - - -BOOL AvHAlienWeapon::IsUseable(void) -{ - BOOL theIsUseable = FALSE; - - // Make sure we have enough energy for this attack - float theEnergyCost = this->GetEnergyForAttack(); - float& theFuser = this->GetEnergyLevel(); - - if(AvHMUHasEnoughAlienEnergy(theFuser, theEnergyCost) && this->GetEnabledState()) - { - theIsUseable = TRUE; - } - - return theIsUseable; -} - -bool AvHAlienWeapon::UsesAmmo(void) const -{ - return false; -} - -BOOL AvHAlienWeapon::UseDecrement(void) -{ - return true; -} - +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHAlienWeapon.cpp $ +// $Date: 2002/10/03 18:37:32 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHAlienWeapon.cpp,v $ +// Revision 1.9 2002/10/03 18:37:32 Flayra +// - Moved alien energy to fuser3 +// +// Revision 1.8 2002/09/23 22:08:59 Flayra +// - Updates to allow marine weapons stick around +// +// Revision 1.7 2002/08/31 18:01:00 Flayra +// - Work at VALVe +// +// Revision 1.6 2002/08/16 02:32:09 Flayra +// - Added damage types +// - Swapped umbra and bile bomb +// +// Revision 1.5 2002/07/08 16:43:26 Flayra +// - Web change? +// +// Revision 1.4 2002/06/25 17:29:56 Flayra +// - Better default behavior +// +// Revision 1.3 2002/06/03 16:25:10 Flayra +// - Switch alien weapons quickly, renamed CheckValidAttack() +// +// Revision 1.2 2002/05/28 17:37:14 Flayra +// - Reduced times where alien weapons are dropped and picked up again (but didn't eliminate, they are still being instantiated somewhere) +// +// Revision 1.1 2002/05/23 02:34:00 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHAlienWeapon.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHSpecials.h" + +#ifdef AVH_CLIENT +#include "cl_dll/wrect.h" +#include "cl_dll/cl_dll.h" +#include "cl_dll/hud.h" +#include "cl_dll/cl_util.h" +extern int g_runfuncs; +#include "cl_dll/com_weapons.h" + +#include "common/net_api.h" + +#include "pm_shared/pm_defs.h" +#include "pm_shared/pm_shared.h" +#include "pm_shared/pm_movevars.h" +extern playermove_t *pmove; +#endif + +#include "mod/AvHSharedUtil.h" +#include "util/Balance.h" + +AvHAlienWeapon::AvHAlienWeapon() +{ + #ifdef AVH_SERVER + this->SetGroundLifetime(0); + #endif +} + +bool AvHAlienWeapon::ProcessValidAttack(void) +{ + bool theCanAttack = false; + + if(AvHBasePlayerWeapon::ProcessValidAttack()) + { + if(this->IsUseable()) + { + theCanAttack = true; + } + } + + return theCanAttack; +} + +void AvHAlienWeapon::DeductCostForShot(void) +{ + if(this->GetTakesEnergy()) + { + AvHBasePlayerWeapon::DeductCostForShot(); + + float theEnergyCost = this->GetEnergyForAttack(); + float& theFuser = this->GetEnergyLevel(); + AvHMUDeductAlienEnergy(theFuser, theEnergyCost); + } +} + +// Cannot ever drop alien weapons +void AvHAlienWeapon::Drop(void) +{ +} + +bool AvHAlienWeapon::GetAllowedForUser3(AvHUser3 inUser3) +{ + bool theAllowed = false; + + // Alien weapons for aliens. Don't take into account exact roles until needed (and until weapons have stabilized) + switch(inUser3) + { + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: + theAllowed = true; + break; + } + + return theAllowed; +} + +int AvHAlienWeapon::GetDamageType() const +{ + // Assume melee attack, piercing + //return NS_DMG_PIERCING; + return NS_DMG_NORMAL; +} + +float AvHAlienWeapon::GetDeployTime() const +{ + // Aliens are Quake-style/arcadey, make weapon switch fast + return .1f; +} + +float AvHAlienWeapon::GetEnergyForAttack() const +{ + float theEnergy = 0.0f; + + AvHMUGetEnergyCost((AvHWeaponID)(this->m_iId), theEnergy); + + return theEnergy; +} + +bool AvHAlienWeapon::GetFiresUnderwater() const +{ + return true; +} + +// Never live on ground +int AvHAlienWeapon::GetGroundLifetime() const +{ + return 0; +} + +float& AvHAlienWeapon::GetEnergyLevel() +{ +#ifdef AVH_CLIENT + float& theFuser = pmove->fuser3; +#endif + +#ifdef AVH_SERVER + float& theFuser = this->m_pPlayer->pev->fuser3; +#endif + + return theFuser; +} + +bool AvHAlienWeapon::GetIsDroppable() const +{ + return false; +} + +bool AvHAlienWeapon::GetIsGunPositionValid() const +{ + // Check to make sure our gun barrel isn't coming out on the other side of a wall. + + vec3_t theBarrelOrigin; + vec3_t theBarrelEnd; + +#ifdef AVH_CLIENT + + extern vec3_t v_view_ofs; // In view.cpp + extern vec3_t v_cl_angles; + + cl_entity_s* thePlayer = gHUD.GetVisiblePlayer(); + + VectorAdd(thePlayer->curstate.origin, v_view_ofs, theBarrelOrigin); + + vec3_t theBarrelDirection; + AngleVectors(v_angles, theBarrelDirection, NULL, NULL); + + VectorMA(theBarrelOrigin, GetBarrelLength(), theBarrelDirection, theBarrelEnd); + +#endif + +#ifdef AVH_SERVER + + VectorCopy(m_pPlayer->GetGunPosition(), theBarrelOrigin); + VectorCopy(GetWorldBarrelPoint(), theBarrelEnd); + +#endif + + return AvHTraceLineAgainstWorld(theBarrelOrigin, theBarrelEnd) == 1; +} + +float AvHAlienWeapon::ComputeAttackInterval() const +{ + float theROF = this->GetRateOfFire(); + + int theUser4 = this->m_pPlayer->pev->iuser4; + + #ifdef AVH_CLIENT + cl_entity_t* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + if(theLocalPlayer) + { + theUser4 = theLocalPlayer->curstate.iuser4; + } + #endif + + // Speed attack if in range of primal scream + if(GetHasUpgrade(theUser4, MASK_BUFFED)) + { + float thePrimalScreamROFFactor = 1.0f + BALANCE_VAR(kPrimalScreamROFFactor); + theROF /= thePrimalScreamROFFactor; + } + + // Check for focus + if(AvHSHUGetIsWeaponFocusable(AvHWeaponID(this->m_iId))) + { + int theFocusLevel = AvHGetAlienUpgradeLevel(theUser4, MASK_UPGRADE_8); + if(theFocusLevel > 0) + { + // Slow down ROF by x% for each level + const float theFocusROFPercentSlowdownPerLevel = BALANCE_VAR(kFocusROFPercentSlowdownPerLevel); + float theSlowDownPercentage = 1.0f + theFocusLevel*theFocusROFPercentSlowdownPerLevel; + + theROF *= theSlowDownPercentage; + } + } + + return theROF; +} + +void AvHAlienWeapon::Precache(void) +{ + AvHBasePlayerWeapon::Precache(); +} + +void AvHAlienWeapon::Spawn() +{ + AvHBasePlayerWeapon::Spawn(); + + this->pev->iuser3 = AVH_USER3_NONE; + + #ifdef AVH_SERVER + this->SetGroundLifetime(0); + #endif +} + + +BOOL AvHAlienWeapon::IsUseable(void) +{ + BOOL theIsUseable = FALSE; + + // Make sure we have enough energy for this attack + float theEnergyCost = this->GetEnergyForAttack(); + float& theFuser = this->GetEnergyLevel(); + float theLatency = 0.0f; +#ifdef AVH_CLIENT + // : 991 -- added latency-based prediction for the ammount of energy available to the alien + net_status_s current_status; + gEngfuncs.pNetAPI->Status(¤t_status); + theLatency = max(0.0f, current_status.latency); + + int theNumLevels = AvHGetAlienUpgradeLevel(this->m_pPlayer->pev->iuser4, MASK_UPGRADE_5); + if(theNumLevels > 0) + theLatency *= (1.0 + theNumLevels * BALANCE_VAR(kAdrenalineEnergyPercentPerLevel)); +#endif + + if(AvHMUHasEnoughAlienEnergy(theFuser, theEnergyCost, theLatency) && this->GetEnabledState()) + { + theIsUseable = TRUE; + } + + return theIsUseable; +} + +bool AvHAlienWeapon::UsesAmmo(void) const +{ + return false; +} + +BOOL AvHAlienWeapon::UseDecrement(void) +{ + return true; +} + diff --git a/main/source/mod/AvHAlienWeaponConstants.h b/main/source/mod/AvHAlienWeaponConstants.h index a7f62f5..2d98489 100644 --- a/main/source/mod/AvHAlienWeaponConstants.h +++ b/main/source/mod/AvHAlienWeaponConstants.h @@ -339,7 +339,7 @@ const float kBileBombPunch = 8.0; const int kBileBombBarrelLength = 20; const float kBileBombFrictionConstant = .8f; -const int kBileBombVelocity = 750; +const int kBileBombVelocity = 650; // The bile bomb size must be small so that the collision results on the server // are close to the collision results for the temp entity on the client. diff --git a/main/source/mod/AvHBaseBuildable.cpp b/main/source/mod/AvHBaseBuildable.cpp index 245f94c..35302e0 100644 --- a/main/source/mod/AvHBaseBuildable.cpp +++ b/main/source/mod/AvHBaseBuildable.cpp @@ -1,1448 +1,1512 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHBaseBuildable.cpp$ -// $Date: 2002/11/22 21:28:15 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHBaseBuildable.cpp,v $ -// Revision 1.19 2002/11/22 21:28:15 Flayra -// - mp_consistency changes -// -// Revision 1.18 2002/11/15 04:47:11 Flayra -// - Regenerate now returns bool if healed or not, for def chamber tweak -// -// Revision 1.17 2002/11/12 02:22:35 Flayra -// - Removed draw damage from public build -// - Don't allow +use to speed research -// -// Revision 1.16 2002/11/06 01:38:24 Flayra -// - Added ability for buildings to be enabled and disabled, for turrets to be shut down -// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) -// -// Revision 1.15 2002/10/24 21:21:49 Flayra -// - Added code to award attacker a frag when destroying a building but thought better of it -// -// Revision 1.14 2002/10/16 00:49:35 Flayra -// - Reworked build times so they are real numbers -// -// Revision 1.13 2002/10/03 18:38:56 Flayra -// - Fixed problem where regenerating builders via healing spray wasn't updating health ring -// - Allow buildings to play custom damage alerts (for towers) -// -// Revision 1.12 2002/09/23 22:10:14 Flayra -// - Removed progress bar when building -// - Removed vestiges of fading building as building -// - Changed point costs -// -// Revision 1.11 2002/09/09 19:49:09 Flayra -// - Buildables now play animations better, without interrupting previous anims -// -// Revision 1.10 2002/08/31 18:01:00 Flayra -// - Work at VALVe -// -// Revision 1.9 2002/08/16 02:32:45 Flayra -// - Added damage types -// - Added visual health for commander and marines -// -// Revision 1.8 2002/08/02 22:02:09 Flayra -// - New alert system -// -// Revision 1.7 2002/07/26 23:03:56 Flayra -// - Don't play "hurt/wound" sounds when we don't actually take damage (GetCanEntityDoDamageTo) -// - Generate numerical feedback for damage events -// -// Revision 1.6 2002/07/23 16:58:38 Flayra -// - Auto-build can't happen before game starts -// -// Revision 1.5 2002/07/01 21:15:46 Flayra -// - Added auto-build capability -// -// Revision 1.4 2002/06/25 17:31:24 Flayra -// - Regular update, don't assume anything about player building, renamed arsenal to armory -// -// Revision 1.3 2002/06/03 16:27:59 Flayra -// - Allow alien buildings to regenerate, renamed weapons factory and armory -// -// Revision 1.2 2002/05/28 17:37:27 Flayra -// - Added building recycling, mark mapper placed buildables so they aren't destroyed at the end of the round -// -// Revision 1.1 2002/05/23 02:34:00 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHBaseBuildable.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHParticleConstants.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHSoundListManager.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "mod/AvHPlayerUpgrade.h" -#include "dlls/animation.h" -#include "mod/AvHMovementUtil.h" - -const int kBaseBuildableSpawnAnimation = 0; -const int kBaseBuildableDeployAnimation = 1; -const int kBaseBuildableIdle1Animation = 2; -const int kBaseBuildableIdle2Animation = 3; -const int kBaseBuildableResearchingAnimation = 4; -const int kBaseBuildableActiveAnimation = 5; -const int kBaseBuildableFireAnimation = 6; -const int kBaseBuildableTakeDamageAnimation = 7; -const int kBaseBuildableDieForwardAnimation = 8; -const int kBaseBuildableDieLeftAnimation = 9; -const int kBaseBuildableDieBackwardAnimation = 10; -const int kBaseBuildableDieRightAnimation = 11; -const int kBaseBuildableSpecialAnimation = 12; - -extern int gRegenerationEventID; -extern AvHSoundListManager gSoundListManager; - -AvHBaseBuildable::AvHBaseBuildable(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser3) : AvHBuildable(inTechID), kStartAlpha(128), mAverageUseSoundLength(.5f) -{ - this->mClassName = inClassName; - this->mMessageID = inMessageID; - - this->mBaseHealth = GetGameRules()->GetBaseHealthForMessageID(inMessageID); - - char* theModelName = AvHSHUGetBuildTechModelName(inMessageID); - ASSERT(theModelName); - this->mModelName = theModelName; - - this->mSelectID = inUser3; - this->mTimeToConstruct = GetGameRules()->GetBuildTimeForMessageID(inMessageID); - - // Very important that this doesn't go in Init(), else mapper-placed structures disappear on map-reset - this->mPersistent = false; - - this->Init(); -} - -void AvHBaseBuildable::Init() -{ - if(this->pev) - { - InitializeBuildable(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, this->mSelectID); - } - - this->mTimeAnimationDone = 0; - this->mLastAnimationPlayed = -1; - this->mIsResearching = false; - this->mTimeOfLastAutoHeal = -1; - this->mInternalSetConstructionComplete = false; - this->mKilled = false; - this->mTimeOfLastDamageEffect = -1; - this->mTimeOfLastDamageUpdate = -1; - this->mTimeRecycleStarted = -1; - this->mTimeRecycleDone = -1; - - SetThink(NULL); -} - -const float kAnimateThinkTime = .1f; - -void AvHBaseBuildable::AnimateThink() -{ - int theSequence = this->GetResearchAnimation(); - if(!this->mIsResearching) - { - // Play a random idle animation - theSequence = this->GetIdleAnimation(); - } - else - { - int a = 0; - } - - this->PlayAnimationAtIndex(theSequence); - - // Set our next think - float theUpdateTime = this->GetTimeForAnimation(theSequence); - this->pev->nextthink = gpGlobals->time + theUpdateTime; -} - -int AvHBaseBuildable::BloodColor( void ) -{ - int theBloodColor = DONT_BLEED; - - if(this->GetIsOrganic()) - { - theBloodColor = BLOOD_COLOR_GREEN; - } - - return theBloodColor; -} - -void AvHBaseBuildable::BuildableTouch(CBaseEntity* inEntity) -{ - if(inEntity->pev->team != this->pev->team) - { - this->Uncloak(); - } -} - -void AvHBaseBuildable::CheckEnabledState() -{ -} - -void AvHBaseBuildable::ConstructUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - bool theSuccess = false; - bool theIsBuilding = false; - bool theIsResearching = false; - float thePercentage = 0.0f; - - AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); - - // Only allow players to help along building, not researching - if(theIsBuilding) - { - // Only allow users from same team as turret deployer - float thePercentage = this->GetNormalizedBuildPercentage(); - if(pActivator->pev->team == this->pev->team && (thePercentage < 1.0f)) - { - AvHPlayer* thePlayer = dynamic_cast(pActivator); - ASSERT(thePlayer); - - // Only soldiers and builders can build - if(thePlayer->GetIsAbleToAct() && ((thePlayer->pev->iuser3 == AVH_USER3_MARINE_PLAYER) || (thePlayer->pev->iuser3 == AVH_USER3_ALIEN_PLAYER2))) - { - AvHBasePlayerWeapon* theWeapon = dynamic_cast(thePlayer->m_pActiveItem); - if(!theWeapon || theWeapon->CanHolster()) - { - thePlayer->PlayerConstructUse(); - - bool thePlaySound = false; - - // Ensure that buildings are never absolutely painful to create - int theBuildTime = max(GetGameRules()->GetBuildTimeForMessageID(this->mMessageID), 1); - - if(GetGameRules()->GetIsTesting() || GetGameRules()->GetCheatsEnabled()) - { - theBuildTime = 2; - } - - // Make non-frame-rate dependent - const float kDefaultInterval = .1f; - float theTimeOfLastConstructUse = thePlayer->GetTimeOfLastConstructUse(); - - float theInterval = min(max(gpGlobals->time - theTimeOfLastConstructUse, 0.0f), kDefaultInterval); - thePercentage += (theInterval/(float)theBuildTime); - - thePlayer->SetTimeOfLastConstructUse(gpGlobals->time); - - if(gpGlobals->time > (this->mLastTimePlayedSound + this->mAverageUseSoundLength)) - { - AvHSUPlayRandomConstructionEffect(thePlayer, this); - this->mLastTimePlayedSound = gpGlobals->time; - } - - // Given the number of constructors, what's chance of starting a new sound? - float theChanceForNewSound = (gpGlobals->frametime/(this->mAverageUseSoundLength));// /2.0f)); - float theRandomFloat = RANDOM_FLOAT(0.0f, 1.0f); - if(theRandomFloat < theChanceForNewSound) - { - AvHSUPlayRandomConstructionEffect(thePlayer, this); - } - - //if(RANDOM_LONG(0, 20) == 0) - //{ - // char theMessage[128]; - // sprintf(theMessage, "Time passed: %f, ticks: %d, rate: %f\n", theTimePassed, this->mPreThinkTicks, this->mPreThinkFrameRate); - // UTIL_SayText(theMessage, this); - //} - - this->SetNormalizedBuildPercentage(thePercentage); - - theSuccess = true; - } - } - } - } - - // Clear out +use sound when ineffective - if(!theSuccess) - { - EMIT_SOUND(pActivator->edict(), CHAN_ITEM, "common/null.wav", 1.0, ATTN_NORM); - } -} - -bool AvHBaseBuildable::Energize(float inEnergyAmount) -{ - return false; -} - -int AvHBaseBuildable::GetBaseHealth() const -{ - return this->mBaseHealth; -} - -char* AvHBaseBuildable::GetClassName() const -{ - return this->mClassName; -} - -int AvHBaseBuildable::GetIdleAnimation() const -{ - int theAnimation = this->GetIdle1Animation(); - - if(RANDOM_LONG(0, 1)) - { - theAnimation = this->GetIdle2Animation(); - } - - return theAnimation; -} - -char* AvHBaseBuildable::GetDeploySound() const -{ - return NULL; -} - -bool AvHBaseBuildable::GetIsBuilt() const -{ - return this->mInternalSetConstructionComplete; -} - -bool AvHBaseBuildable::GetIsOrganic() const -{ - return false; -} - -char* AvHBaseBuildable::GetKilledSound() const -{ - return NULL; -} - -float AvHBaseBuildable::GetNormalizedBuildPercentage() const -{ - //return this->pev->fuser1/kNormalizationNetworkFactor; - - bool theIsBuilding; - bool theIsResearching; - float thePercentage; - AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); - - // Check for energy special case - if(theIsBuilding && theIsResearching) - { - thePercentage = 1.0f; - } - - return thePercentage; -} - -float AvHBaseBuildable::GetTimeForAnimation(int inIndex) const -{ - return GetSequenceDuration(GET_MODEL_PTR(ENT(pev)), this->pev); -} - -int AvHBaseBuildable::GetStartAlpha() const -{ - return kStartAlpha; -} - -void AvHBaseBuildable::FireDeathTarget() const -{ - if(this->mTargetOnDeath != "") - { - FireTargets(this->mTargetOnDeath.c_str(), NULL, NULL, USE_TOGGLE, 0.0f); - } -} - -void AvHBaseBuildable::FireSpawnTarget() const -{ - if(this->mTargetOnSpawn != "") - { - FireTargets(this->mTargetOnSpawn.c_str(), NULL, NULL, USE_TOGGLE, 0.0f); - } -} - -void AvHBaseBuildable::KeyValue(KeyValueData* pkvd) -{ - // Any entity placed by the mapper is persistent - this->SetPersistent(); - - if(FStrEq(pkvd->szKeyName, "targetonspawn")) - { - this->mTargetOnSpawn = pkvd->szValue; - pkvd->fHandled = TRUE; - } - else if(FStrEq(pkvd->szKeyName, "targetondeath")) - { - this->mTargetOnDeath = pkvd->szValue; - pkvd->fHandled = TRUE; - } - else if(FStrEq(pkvd->szKeyName, "teamchoice")) - { - //this->mTeam = (AvHTeamNumber)(atoi(pkvd->szValue)); - this->pev->team = (AvHTeamNumber)(atoi(pkvd->szValue)); - - pkvd->fHandled = TRUE; - } - else if(FStrEq(pkvd->szKeyName, "angles")) - { - // TODO: Insert code here - //pkvd->fHandled = TRUE; - int a = 0; - } - else - { - CBaseAnimating::KeyValue(pkvd); - } -} - -void AvHBaseBuildable::PlayAnimationAtIndex(int inIndex, bool inForce, float inFrameRate) -{ - // Only play animations on buildings that we have artwork for - bool thePlayAnim = false; - - if(inIndex >= 0) - { - switch(this->mMessageID) - { - case BUILD_RESOURCES: - case BUILD_ARMSLAB: - case BUILD_COMMANDSTATION: - case BUILD_INFANTRYPORTAL: - case BUILD_TURRET_FACTORY: - case TURRET_FACTORY_UPGRADE: - case BUILD_ARMORY: - case ARMORY_UPGRADE: - case BUILD_OBSERVATORY: - case BUILD_TURRET: - case BUILD_SIEGE: - case BUILD_PROTOTYPE_LAB: - case ALIEN_BUILD_HIVE: - case ALIEN_BUILD_RESOURCES: - case ALIEN_BUILD_OFFENSE_CHAMBER: - case ALIEN_BUILD_DEFENSE_CHAMBER: - case ALIEN_BUILD_SENSORY_CHAMBER: - case ALIEN_BUILD_MOVEMENT_CHAMBER: - thePlayAnim = true; - } - } - - // Make sure we're not interrupting another animation - if(thePlayAnim) - { - // Allow forcing of new animation, but it's better to complete current animation then interrupt it and play it again - float theCurrentTime = gpGlobals->time; - if((theCurrentTime >= this->mTimeAnimationDone) || (inForce && (inIndex != this->mLastAnimationPlayed))) - { - this->pev->sequence = inIndex; - this->pev->frame = 0; - ResetSequenceInfo(); - - this->pev->framerate = inFrameRate; - - // Set to last frame to play backwards - if(this->pev->framerate < 0) - { - this->pev->frame = 255; - } - - this->mLastAnimationPlayed = inIndex; - float theTimeForAnim = this->GetTimeForAnimation(inIndex); - this->mTimeAnimationDone = theCurrentTime + theTimeForAnim; - - // Recalculate size - //Vector theMinSize, theMaxSize; - //this->ExtractBbox(this->pev->sequence, (float*)&theMinSize, (float*)&theMaxSize); - //UTIL_SetSize(this->pev, theMinSize, theMaxSize); - } - } -} - -void AvHBaseBuildable::SetNormalizedBuildPercentage(float inPercentage, bool inForceIfComplete) -{ - // Get previous build percentage so we can add hitpoints as structure is building. This means that structures that are hurt while building finish hurt. - bool theIsBuilding, theIsResearching; - float theNormalizedBuildPercentage = 0.0f; - AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, theNormalizedBuildPercentage); - - float theDiff = inPercentage - theNormalizedBuildPercentage; - if(theDiff > 0) - { - this->pev->health += theDiff*(1.0f - kBaseHealthPercentage)*this->mBaseHealth; - this->pev->health = min(max(0.0f, this->pev->health), (float)this->mBaseHealth); - } - else - { - int a = 0; - } - - // Set new build state - AvHSHUSetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, true, inPercentage); - - if(inPercentage >= 1.0f) - { - this->InternalSetConstructionComplete(inForceIfComplete); - } - - this->HealthChanged(); -} - -void AvHBaseBuildable::UpdateOnRecycle() -{ - // empty, override to add events on recycle for buildings -} - -void AvHBaseBuildable::StartRecycle() -{ - if(!GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING)) - { - int theRecycleTime = (GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsCheatEnabled(kcSlowResearch)) ? 2 : BALANCE_VAR(kRecycleTime); - - // Play recycle animation in reverse (would like to play them slower according to recycle time, but it doesn't work for all structures, seems dependent on # of keyframes) - int theAnimation = this->GetRecycleAnimation(); - float theTimeForAnim = this->GetTimeForAnimation(theAnimation); - float theFrameRate = -1;//-theTimeForAnim/theRecycleTime; - this->PlayAnimationAtIndex(theAnimation, true, theFrameRate); - - // Schedule time to give points back - SetThink(&AvHBaseBuildable::RecycleComplete); - - this->mTimeRecycleStarted = gpGlobals->time; - - this->mTimeRecycleDone = gpGlobals->time + theRecycleTime; - - this->pev->nextthink = this->mTimeRecycleDone; - - float theVolume = .5f; - EMIT_SOUND(this->edict(), CHAN_AUTO, kBuildableRecycleSound, theVolume, ATTN_NORM); - - SetUpgradeMask(&this->pev->iuser4, MASK_RECYCLING); - - // run any events for this class on recycling the structure - this->UpdateOnRecycle(); - - // Remove tech immediately, so research or building isn't started using this tech - this->TriggerRemoveTech(); - } -} - -bool AvHBaseBuildable::GetIsRecycling() const -{ - return GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING); -} - -bool AvHBaseBuildable::GetIsTechActive() const -{ - bool theIsActive = false; - - if(this->GetIsBuilt() && (this->pev->health > 0) && !GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING)) - { - theIsActive = true; - } - - return theIsActive; -} - -int AvHBaseBuildable::GetActiveAnimation() const -{ - return kBaseBuildableActiveAnimation; -} - -CBaseEntity* AvHBaseBuildable::GetAttacker() -{ - CBaseEntity* theAttacker = this; - - AvHBuildable* theBuildable = dynamic_cast(this); - if(theBuildable) - { - int theBuilderIndex = theBuildable->GetBuilder(); - CBaseEntity* theBuilderEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theBuilderIndex)); - if(theBuilderEntity) - { - theAttacker = theBuilderEntity; - } - } - - return theAttacker; -} - -int AvHBaseBuildable::GetDeployAnimation() const -{ - return kBaseBuildableDeployAnimation; -} - -int AvHBaseBuildable::GetIdle1Animation() const -{ - return kBaseBuildableIdle1Animation; -} - -int AvHBaseBuildable::GetIdle2Animation() const -{ - return kBaseBuildableIdle2Animation; -} - -int AvHBaseBuildable::GetKilledAnimation() const -{ - return kBaseBuildableDieForwardAnimation; -} - -AvHMessageID AvHBaseBuildable::GetMessageID() const -{ - return this->mMessageID; -} - -int AvHBaseBuildable::GetMoveType() const -{ - return MOVETYPE_TOSS; -} - -bool AvHBaseBuildable::GetTriggerAlertOnDamage() const -{ - return true; -} - -float AvHBaseBuildable::GetTimeAnimationDone() const -{ - return this->mTimeAnimationDone; -} - -int AvHBaseBuildable::GetResearchAnimation() const -{ - return kBaseBuildableResearchingAnimation; -} - -// Play deploy animation backwards -int AvHBaseBuildable::GetRecycleAnimation() const -{ - int theAnimation = -1; - - if(this->GetIsBuilt()) - { - theAnimation = this->GetDeployAnimation(); - } - - return theAnimation; -} - -char* AvHBaseBuildable::GetModelName() const -{ - return this->mModelName; -} - -int AvHBaseBuildable::GetSpawnAnimation() const -{ - return kBaseBuildableSpawnAnimation; -} - -int AvHBaseBuildable::GetTakeDamageAnimation() const -{ - int theAnimation = -1; - - if(this->GetIsBuilt()) - { - theAnimation = kBaseBuildableTakeDamageAnimation; - } - - return theAnimation; -} - - -AvHTeamNumber AvHBaseBuildable::GetTeamNumber() const -{ - return (AvHTeamNumber)this->pev->team; -} - -void AvHBaseBuildable::Killed(entvars_t* pevAttacker, int iGib) -{ - AvHBaseBuildable::SetHasBeenKilled(); - - this->mKilled = true; - this->mInternalSetConstructionComplete = false; - - // puzl: 980 - // Less smoke for recycled buildings - this->TriggerDeathAudioVisuals(iGib == GIB_RECYCLED); - - if(!this->GetIsOrganic()) - { - // More sparks for recycled buildings - int numSparks = ( iGib == GIB_RECYCLED ) ? 7 : 3; - for ( int i=0; i < numSparks; i++ ) { - Vector vecSrc = Vector( (float)RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ), (float)RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ), (float)0 ); - vecSrc = vecSrc + Vector( (float)0, (float)0, (float)RANDOM_FLOAT( pev->origin.z, pev->absmax.z ) ); - UTIL_Sparks(vecSrc); - } - } - // :puzl - this->TriggerRemoveTech(); - - AvHSURemoveEntityFromHotgroupsAndSelection(this->entindex()); - - if(pevAttacker) - { - const char* theClassName = STRING(this->pev->classname); - AvHPlayer* inPlayer = dynamic_cast(CBaseEntity::Instance(ENT(pevAttacker))); - if(inPlayer && theClassName) - { - inPlayer->LogPlayerAction("structure_destroyed", theClassName); - GetGameRules()->RewardPlayerForKill(inPlayer, this); - } - } - - if(this->GetIsPersistent()) - { - this->SetInactive(); - } - else - { - CBaseAnimating::Killed(pevAttacker, iGib); - } -} - -void AvHBaseBuildable::SetActive() -{ - this->pev->effects &= ~EF_NODRAW; -} - -void AvHBaseBuildable::SetInactive() -{ - this->pev->health = 0; - this->pev->effects |= EF_NODRAW; - this->pev->solid = SOLID_NOT; - this->pev->takedamage = DAMAGE_NO; - SetUpgradeMask(&this->pev->iuser4, MASK_PARASITED, false);//voogru: remove parasite flag to prevent phantom parasites. - //this->pev->deadflag = DEAD_DEAD; - SetThink(NULL); -} - -int AvHBaseBuildable::ObjectCaps(void) -{ - return FCAP_CONTINUOUS_USE; -} - -void AvHBaseBuildable::Precache(void) -{ - CBaseAnimating::Precache(); - - char* theDeploySound = this->GetDeploySound(); - if(theDeploySound) - { - PRECACHE_UNMODIFIED_SOUND(theDeploySound); - } - char* theKilledSound = this->GetKilledSound(); - if(theKilledSound) - { - PRECACHE_UNMODIFIED_SOUND(theKilledSound); - } - - PRECACHE_UNMODIFIED_MODEL(this->mModelName); - - PRECACHE_UNMODIFIED_SOUND(kBuildableRecycleSound); - //PRECACHE_UNMODIFIED_SOUND(kBuildableHurt1Sound); - //PRECACHE_UNMODIFIED_SOUND(kBuildableHurt2Sound); - - this->mElectricalSprite = PRECACHE_UNMODIFIED_MODEL(kElectricalSprite); -} - -void AvHBaseBuildable::RecycleComplete() -{ - // Look at whether it has been built and health to determine how many points to give back - float thePercentage = BALANCE_VAR(kRecycleResourcePercentage); - - if(!this->GetIsBuilt()) - { - thePercentage = .8f; - } - - // Make sure the building is still alive, can't get points back if it's dead - if(this->pev->health <= 0) - { - thePercentage = 0.0f; - } - - // Look up team - AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)this->pev->team); - if(theTeam) - { - bool theIsEnergyTech = AvHSHUGetDoesTechCostEnergy(this->mMessageID); - ASSERT(!theIsEnergyTech); - - float thePointsBack = GetGameRules()->GetCostForMessageID(this->mMessageID)*thePercentage; - theTeam->SetTeamResources(theTeam->GetTeamResources() + thePointsBack); - - // Play "+ resources" event - AvHSUPlayNumericEventAboveStructure(thePointsBack, this); - - // puzl: 980 - // Less smoke and more sparks for recycled buildings - this->Killed(this->pev, GIB_RECYCLED); - // :puzl - } -} - -// Sets the template iuser3 for this buildable. This is stored outside of the actual iuser3 because sometimes the pev isn't allocated yet. -void AvHBaseBuildable::SetSelectID(int inSelectID) -{ - this->mSelectID = inSelectID; -} - -bool AvHBaseBuildable::Regenerate(float inRegenerationAmount, bool inPlaySound) -{ - bool theDidHeal = false; - - float theMaxHealth = this->mBaseHealth; - - if(!this->GetIsBuilt()) - { - float theNormalizedBuildPercentage = this->GetNormalizedBuildPercentage(); - - theMaxHealth = (kBaseHealthPercentage + theNormalizedBuildPercentage*(1.0f - kBaseHealthPercentage))*this->mBaseHealth; - } - - // If we aren't at full health, heal health - if(this->pev->health < theMaxHealth) - { - this->pev->health = min(theMaxHealth, this->pev->health + inRegenerationAmount); - this->HealthChanged(); - theDidHeal = true; - } - - // Play regen event - if(theDidHeal) - { - if(inPlaySound) - { - // Play regeneration event - PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - } - } - - return theDidHeal; -} - -void AvHBaseBuildable::ResetEntity() -{ - CBaseAnimating::ResetEntity(); - - this->Init(); - - this->Materialize(); - - this->pev->effects = 0; - - // Build it if marked as starting built - if(this->pev->spawnflags & 1) - this->SetConstructionComplete(true); - - this->mKilled = false; -} - -void AvHBaseBuildable::InternalSetConstructionComplete(bool inForce) -{ - if(!this->mInternalSetConstructionComplete || inForce) - { - // Fully built items are no longer marked as buildable - SetUpgradeMask(&this->pev->iuser4, MASK_BUILDABLE, false); - - this->pev->rendermode = kRenderNormal; - this->pev->renderamt = 255; - - this->SetHasBeenBuilt(); - - this->SetActive(); - - this->mInternalSetConstructionComplete = true; - - this->TriggerAddTech(); - - char* theDeploySound = this->GetDeploySound(); - if(theDeploySound) - { - EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, theDeploySound, 1, ATTN_NORM); - } - - int theDeployAnimation = this->GetDeployAnimation(); - - this->PlayAnimationAtIndex(theDeployAnimation, true); - } -} - -void AvHBaseBuildable::SetConstructionComplete(bool inForce) -{ - this->SetNormalizedBuildPercentage(1.0f, inForce); -} - -void AvHBaseBuildable::SetAverageUseSoundLength(float inLength) -{ - this->mAverageUseSoundLength = inLength; -} - -void AvHBaseBuildable::SetResearching(bool inState) -{ - int theSequence = this->GetResearchAnimation(); - - if(!inState) - { - theSequence = this->GetIdleAnimation(); - } - - this->PlayAnimationAtIndex(theSequence, true); - - this->mIsResearching = inState; -} - -// Requires mSelectID and mMessageID to be set -// Sets the pev user variables, mBaseHealth, pev->health and pev->armorvalue -void AvHBaseBuildable::InternalInitializeBuildable() -{ - // Always buildable - InitializeBuildable(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, this->mSelectID); - this->mBaseHealth = GetGameRules()->GetBaseHealthForMessageID(this->mMessageID); - this->pev->health = this->mBaseHealth*kBaseHealthPercentage; - this->pev->max_health = this->mBaseHealth; - - // Store max health in armorvalue - //this->pev->armorvalue = GetGameRules()->GetBaseHealthForMessageID(this->mMessageID); -} - -const float kFallThinkInterval = .1f; - -void AvHBaseBuildable::Spawn() -{ - this->Precache(); - - CBaseAnimating::Spawn(); - - // Get building size in standard way - SET_MODEL(ENT(this->pev), this->mModelName); - - pev->movetype = this->GetMoveType(); - pev->solid = SOLID_BBOX; - - UTIL_SetOrigin( pev, pev->origin ); - - this->Materialize(); - - SetTouch(&AvHBaseBuildable::BuildableTouch); - - if(this->pev->spawnflags & 1) - this->SetConstructionComplete(true); -} - - -void AvHBaseBuildable::FallThink() -{ - pev->nextthink = gpGlobals->time + kFallThinkInterval; - - if ( pev->flags & FL_ONGROUND ) - { - this->Materialize(); - - // Start animating - SetThink(&AvHBaseBuildable::AnimateThink); - this->pev->nextthink = gpGlobals->time + kAnimateThinkTime; - } -} - -int AvHBaseBuildable::GetSequenceForBoundingBox() const -{ - return 0; -} - -void AvHBaseBuildable::Materialize() -{ - this->pev->solid = SOLID_BBOX; - this->pev->movetype = this->GetMoveType(); - - this->pev->classname = MAKE_STRING(this->mClassName); - - this->pev->takedamage = DAMAGE_YES; - SetBits(this->pev->flags, FL_MONSTER); - - // Always buildable - this->InternalInitializeBuildable(); - - this->SetNormalizedBuildPercentage(0.0f); - - // NOTE: fuser2 is used for repairing structures - - Vector theMinSize, theMaxSize; - //int theSequence = this->GetSequenceForBoundingBox(); - - // Get height needed for model - //this->ExtractBbox(theSequence, (float*)&theMinSize, (float*)&theMaxSize); - //float theHeight = theMaxSize.z - theMinSize.z; - - AvHSHUGetSizeForTech(this->GetMessageID(), theMinSize, theMaxSize); - - UTIL_SetSize(pev, theMinSize, theMaxSize); - - this->PlayAnimationAtIndex(this->GetSpawnAnimation(), true); - - SetUse(&AvHBaseBuildable::ConstructUse); -} - -int AvHBaseBuildable::TakeDamage(entvars_t* inInflictor, entvars_t* inAttacker, float inDamage, int inBitsDamageType) -{ - if(GetGameRules()->GetIsCheatEnabled(kcHighDamage)) - { - inDamage *= 50; - } - - if(!inAttacker) - { - inAttacker = inInflictor; - } - - if(!inInflictor) - { - inInflictor = inAttacker; - } - - // Take into account handicap - AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(inAttacker->team)); - if(theTeam) - { - float theHandicap = theTeam->GetHandicap(); - inDamage *= theHandicap; - } - - CBaseEntity* inInflictorEntity = CBaseEntity::Instance(inInflictor); - float theDamage = 0; - - // Take half damage from piercing - if(inBitsDamageType & NS_DMG_PIERCING) - { - inDamage /= 2.0f; - } - - // Take double damage from blast - if(inBitsDamageType & NS_DMG_BLAST) - { - inDamage *= 2.0f; - } - - if((inBitsDamageType & NS_DMG_ORGANIC) && !this->GetIsOrganic()) - { - inDamage = 0.0f; - } - - theDamage = AvHPlayerUpgrade::CalculateDamageLessArmor((AvHUser3)this->pev->iuser3, this->pev->iuser4, inDamage, this->pev->armorvalue, inBitsDamageType, GetGameRules()->GetNumActiveHives((AvHTeamNumber)this->pev->team)); - if(theDamage > 0) - { - int theAnimationIndex = this->GetTakeDamageAnimation(); - if(theAnimationIndex >= 0) - { - this->PlayAnimationAtIndex(theAnimationIndex, true); - } - - // Award experience to attacker - CBaseEntity* theEntity = CBaseEntity::Instance(ENT(inAttacker)); - AvHPlayer* inAttacker = dynamic_cast(theEntity); - if(inAttacker && (inAttacker->pev->team != this->pev->team)) - { - inAttacker->AwardExperienceForObjective(theDamage, this->GetMessageID()); - } - } - - int theReturnValue = 0; - - if(theDamage > 0.0f) - { - if(this->GetTriggerAlertOnDamage()) - GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_UNDER_ATTACK, this->entindex()); - - theDamage = CBaseAnimating::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType); - - bool theDrawDamage = (CVAR_GET_FLOAT(kvDrawDamage) > 0); - - if(theDrawDamage) - { - Vector theMinSize; - Vector theMaxSize; - AvHSHUGetSizeForTech(this->GetMessageID(), theMinSize, theMaxSize); - - Vector theStartPos = this->pev->origin; - theStartPos.z += theMaxSize.z; - - // Draw for everyone (team is 0 after inDamage parameter) - AvHSUPlayNumericEvent(-inDamage, this->edict(), theStartPos, 0, kNumericalInfoHealthEvent, 0); - } - } - - // Structures uncloak when damaged - this->Uncloak(); - - this->HealthChanged(); - - return theDamage; -} - -void AvHBaseBuildable::TechnologyBuilt(AvHMessageID inMessageID) -{ -} - -void AvHBaseBuildable::WorldUpdate() -{ - this->UpdateTechSlots(); - - // Organic buildings heal themselves - if(this->GetIsOrganic()) - { - this->UpdateAutoHeal(); - } - else - { - //this->UpdateDamageEffects(); - } - - // If we're electrified, set render mode - if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_11)) - { - // Base marine building - const int kElectrifyRenderMode = kRenderFxGlowShell; - const int kElectrifyRenderAmount = 40; - - this->pev->renderfx = kElectrifyRenderMode; - this->pev->renderamt = kElectrifyRenderAmount; - this->pev->rendercolor.x = kTeamColors[this->pev->team][0]; - this->pev->rendercolor.y = kTeamColors[this->pev->team][1]; - this->pev->rendercolor.z = kTeamColors[this->pev->team][2]; - - // Check for enemy players/structures nearby - CBaseEntity* theBaseEntity = NULL; - int theNumEntsDamaged = 0; - - while(((theBaseEntity = UTIL_FindEntityInSphere(theBaseEntity, this->pev->origin, BALANCE_VAR(kElectricalRange))) != NULL) && (theNumEntsDamaged < BALANCE_VAR(kElectricalMaxTargets))) - { - // When "electric" cheat is enabled, shock all non-self entities, else shock enemies - if((GetGameRules()->GetIsCheatEnabled(kcElectric) && (theBaseEntity != this)) || ((theBaseEntity->pev->team != this->pev->team) && theBaseEntity->IsAlive())) - { - // Make sure it's not blocked - TraceResult theTraceResult; - UTIL_TraceLine(this->pev->origin, theBaseEntity->pev->origin, ignore_monsters, dont_ignore_glass, this->edict(), &theTraceResult); - if(theTraceResult.flFraction == 1.0f) - { - CBaseEntity* theAttacker = this->GetAttacker(); - ASSERT(theAttacker); - - if(theBaseEntity->TakeDamage(this->pev, theAttacker->pev, BALANCE_VAR(kElectricalDamage), DMG_GENERIC) > 0) - { - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE(TE_BEAMENTPOINT); - WRITE_SHORT(theBaseEntity->entindex()); - WRITE_COORD( this->pev->origin.x); - WRITE_COORD( this->pev->origin.y); - WRITE_COORD( this->pev->origin.z); - - WRITE_SHORT( this->mElectricalSprite ); - WRITE_BYTE( 0 ); // framestart - WRITE_BYTE( (int)15); // framerate - WRITE_BYTE( (int)(2) ); // life - WRITE_BYTE( 60 ); // width - WRITE_BYTE( 15 ); // noise - WRITE_BYTE( (int)this->pev->rendercolor.x ); // r, g, b - WRITE_BYTE( (int)this->pev->rendercolor.y ); // r, g, b - WRITE_BYTE( (int)this->pev->rendercolor.z ); // r, g, b - WRITE_BYTE( 200 ); // brightness - WRITE_BYTE( 10 ); // speed - MESSAGE_END(); - - gSoundListManager.PlaySoundInList(kElectricSparkSoundList, this, CHAN_AUTO, .7f); - - UTIL_Sparks(theBaseEntity->pev->origin); - - theNumEntsDamaged++; - } - } - } - } - } -} - -bool AvHBaseBuildable::GetHasBeenKilled() const -{ - return this->mKilled; -} - -bool AvHBaseBuildable::GetIsTechnologyAvailable(AvHMessageID inMessageID) const -{ - bool theTechnologyAvailable = false; - - const AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)this->pev->team); - if(theTeam) - { - // Don't allow electrical upgrade if we're already electrified - if((inMessageID != RESEARCH_ELECTRICAL) || !GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_11)) - { - theTechnologyAvailable = (theTeam->GetIsTechnologyAvailable(inMessageID) && this->GetIsBuilt() && !GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING)); - - // Enable recycle button for unbuilt structures - if(!this->GetIsBuilt() && (inMessageID == BUILD_RECYCLE)) - { - theTechnologyAvailable = true; - } - } - } - return theTechnologyAvailable; -} - - -void AvHBaseBuildable::UpdateTechSlots() -{ - // Get tech slot for this structure - AvHGamerules* theGameRules = GetGameRules(); - const AvHTeam* theTeam = theGameRules->GetTeam((AvHTeamNumber)this->pev->team); - if(theTeam) - { - // Update tech slots - AvHTechSlots theTechSlots; - if(theTeam->GetTechSlotManager().GetTechSlotList((AvHUser3)this->pev->iuser3, theTechSlots)) - { - // Clear the existing slots - int theMasks[kNumTechSlots] = {MASK_UPGRADE_1, MASK_UPGRADE_2, MASK_UPGRADE_3, MASK_UPGRADE_4, MASK_UPGRADE_5, MASK_UPGRADE_6, MASK_UPGRADE_7, MASK_UPGRADE_8}; - - // Each slot if we technology is available - for(int i = 0; i < kNumTechSlots; i++) - { - int theCurrentMask = theMasks[i]; - this->pev->iuser4 &= ~theCurrentMask; - - AvHMessageID theMessage = theTechSlots.mTechSlots[i]; - if(theMessage != MESSAGE_NULL) - { - if(this->GetIsTechnologyAvailable(theMessage)) - { - this->pev->iuser4 |= theCurrentMask; - } - } - } - } - - // Update recycling status bar - if(GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING)) - { - float theNormalizedRecyclingFactor = (gpGlobals->time - this->mTimeRecycleStarted)/(this->mTimeRecycleDone - this->mTimeRecycleStarted); - theNormalizedRecyclingFactor = min(max(theNormalizedRecyclingFactor, 0.0f), 1.0f); - - //theResearchEntity->pev->fuser1 = (kResearchFuser1Base + theNormalizedResearchFactor)*kNormalizationNetworkFactor; - AvHSHUSetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, false, theNormalizedRecyclingFactor); - } - } -} - -void AvHBaseBuildable::TriggerDeathAudioVisuals(bool isRecycled) -{ - AvHClassType theTeamType = AVH_CLASS_TYPE_UNDEFINED; - AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)this->pev->team); - if(theTeam) - { - theTeamType = theTeam->GetTeamType(); - } - - switch(theTeamType) - { - case AVH_CLASS_TYPE_ALIEN: - AvHSUPlayParticleEvent(kpsChamberDeath, this->edict(), this->pev->origin); - break; - - case AVH_CLASS_TYPE_MARINE: - // lots of smoke - // puzl: 980 - // Less smoke for recycled buildings - int smokeScale = isRecycled ? 15 : 25; - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ) ); - WRITE_COORD( RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ) ); - WRITE_COORD( RANDOM_FLOAT( pev->absmin.z, pev->absmax.z ) ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( smokeScale ); // scale * 10 - WRITE_BYTE( 10 ); // framerate - MESSAGE_END(); - break; - } - - char* theKilledSound = this->GetKilledSound(); - if(theKilledSound) - { - EMIT_SOUND(ENT(this->pev), CHAN_AUTO, theKilledSound, 1.0, ATTN_IDLE); - } -} - -void AvHBaseBuildable::UpdateAutoBuild(float inTimePassed) -{ - if(GetGameRules()->GetGameStarted()) - { - // TF2 snippet for making sure players don't get stuck in buildings - if(this->pev->solid == SOLID_NOT) - { - //trace_t tr; - //UTIL_TraceHull(this->pev->origin, this->pev->origin, this->pev->mins, this->pev->maxs, this->pev, &tr); - //if(!tr.startsolid && !tr.allsolid ) - //if(AvHSHUGetIsAreaFree(this->pev->origin, this->pev->mins, this->pev->maxs, this->edict())) - - // Check point contents for corner points - float theMinX = this->pev->origin.x + this->pev->mins.x; - float theMinY = this->pev->origin.y + this->pev->mins.y; - float theMinZ = this->pev->origin.z + this->pev->mins.z; - float theMaxX = this->pev->origin.x + this->pev->maxs.x; - float theMaxY = this->pev->origin.y + this->pev->maxs.y; - float theMaxZ = this->pev->origin.z + this->pev->maxs.z; - - // Do tracelines between the corners, to make sure there's no geometry inside the box - Vector theMinVector(theMinX, theMinY, theMinZ); - Vector theMaxVector(theMaxX, theMaxY, theMaxZ); - if(AvHSHUTraceLineIsAreaFree(theMinVector, theMaxVector, this->edict())) - { - theMinVector = Vector(theMaxX, theMinY, theMinZ); - theMaxVector = Vector(theMinX, theMaxY, theMaxZ); - if(AvHSHUTraceLineIsAreaFree(theMinVector, theMaxVector, this->edict())) - { - theMinVector = Vector(theMaxX, theMaxY, theMinZ); - theMaxVector = Vector(theMinX, theMinY, theMaxZ); - if(AvHSHUTraceLineIsAreaFree(theMinVector, theMaxVector, this->edict())) - { - this->pev->solid = SOLID_BBOX; - - // Relink into world (not sure if this is necessary) - UTIL_SetOrigin(this->pev, this->pev->origin); - } - } - } - } - else - { - // If it's not fully built, build more - bool theIsBuilding, theIsResearching; - float thePercentage; - AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); - - float theBuildTime = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID()); - float theBuildPercentage = inTimePassed/theBuildTime; - - float theNewPercentage = min(thePercentage + theBuildPercentage, 1.0f); - this->SetNormalizedBuildPercentage(theNewPercentage); - -// // Increase built time if not fully built -// if(!this->GetHasBeenBuilt() && (theNewPercentage >= 1.0f)) -// { -// this->SetConstructionComplete(); -// } -//// else -//// { -//// this->pev->rendermode = kRenderTransTexture; -//// int theStartAlpha = this->GetStartAlpha(); -//// this->pev->renderamt = theStartAlpha + theNewPercentage*(255 - theStartAlpha); -//// } -// -// AvHSHUSetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, true, theNewPercentage); - - // TODO: Heal self? - // TODO: Play ambient sounds? - } - } -} - -void AvHBaseBuildable::UpdateAutoHeal() -{ - if(GetGameRules()->GetGameStarted() && this->GetIsBuilt()) - { - if((this->mTimeOfLastAutoHeal != -1) && (gpGlobals->time > this->mTimeOfLastAutoHeal)) - { - float theMaxHealth = GetGameRules()->GetBaseHealthForMessageID(this->GetMessageID()); - if(this->pev->health < theMaxHealth) - { - float theTimePassed = (gpGlobals->time - this->mTimeOfLastAutoHeal); - float theHitPointsToGain = theTimePassed*BALANCE_VAR(kOrganicStructureHealRate); - - this->pev->health += theHitPointsToGain; - this->pev->health = min(this->pev->health, theMaxHealth); - - this->HealthChanged(); - } - } - - this->mTimeOfLastAutoHeal = gpGlobals->time; - } -} - -void AvHBaseBuildable::UpdateDamageEffects() -{ - if(GetGameRules()->GetGameStarted() && this->GetIsBuilt()) - { - // Add special effects for structures that are hurt or almost dead - float theMaxHealth = GetGameRules()->GetBaseHealthForMessageID(this->GetMessageID()); - float theHealthScalar = this->pev->health/theMaxHealth; - float theTimeInterval = max(gpGlobals->time - this->mTimeOfLastDamageUpdate, .1f); - - const float kParticleSystemLifetime = 5.0f; - int theAverageSoundInterval = -1; - - // If we're at 25% health or less, emit black smoke - if(gpGlobals->time > (this->mTimeOfLastDamageEffect + kParticleSystemLifetime)) - { - if(theHealthScalar < .25f) - { - AvHSUPlayParticleEvent(kpsBuildableLightDamage, this->edict(), this->pev->origin); - this->mTimeOfLastDamageEffect = gpGlobals->time; - theAverageSoundInterval = 3; - } - // If we're at 50% health or less, emit light smoke - else if(theHealthScalar < .5f) - { - AvHSUPlayParticleEvent(kpsBuildableLightDamage, this->edict(), this->pev->origin); - this->mTimeOfLastDamageEffect = gpGlobals->time; - theAverageSoundInterval = 5; - } - } - - // If we're at less then 75% health, spark occasionally - if(theHealthScalar < .75f) - { - int theRandomChance = RANDOM_LONG(0, (float)8/theTimeInterval); - if(theRandomChance == 0) - { - UTIL_Sparks(this->pev->origin); - UTIL_Sparks(this->pev->origin); - - const char* theHurtSoundToPlay = kBuildableHurt1Sound; - if(RANDOM_LONG(0, 1) == 1) - { - theHurtSoundToPlay = kBuildableHurt2Sound; - } - - float theVolume = .3f; - EMIT_SOUND(this->edict(), CHAN_AUTO, theHurtSoundToPlay, theVolume, ATTN_NORM); - } - } - - this->mTimeOfLastDamageUpdate = gpGlobals->time; - } -} - - -void AvHBaseBuildable::HealthChanged() -{ - int theMaxHealth = this->mBaseHealth;//this->pev->armorvalue; - int theCurrentHealth = this->pev->health; - - float theNewHealthPercentage = (float)theCurrentHealth/theMaxHealth; - this->pev->fuser2 = theNewHealthPercentage*kNormalizationNetworkFactor; -} - -bool AvHBaseBuildable::GetIsPersistent() const -{ - return this->mPersistent; -} - -void AvHBaseBuildable::SetPersistent() -{ - this->mPersistent = true; -} - +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHBaseBuildable.cpp$ +// $Date: 2002/11/22 21:28:15 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHBaseBuildable.cpp,v $ +// Revision 1.19 2002/11/22 21:28:15 Flayra +// - mp_consistency changes +// +// Revision 1.18 2002/11/15 04:47:11 Flayra +// - Regenerate now returns bool if healed or not, for def chamber tweak +// +// Revision 1.17 2002/11/12 02:22:35 Flayra +// - Removed draw damage from public build +// - Don't allow +use to speed research +// +// Revision 1.16 2002/11/06 01:38:24 Flayra +// - Added ability for buildings to be enabled and disabled, for turrets to be shut down +// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) +// +// Revision 1.15 2002/10/24 21:21:49 Flayra +// - Added code to award attacker a frag when destroying a building but thought better of it +// +// Revision 1.14 2002/10/16 00:49:35 Flayra +// - Reworked build times so they are real numbers +// +// Revision 1.13 2002/10/03 18:38:56 Flayra +// - Fixed problem where regenerating builders via healing spray wasn't updating health ring +// - Allow buildings to play custom damage alerts (for towers) +// +// Revision 1.12 2002/09/23 22:10:14 Flayra +// - Removed progress bar when building +// - Removed vestiges of fading building as building +// - Changed point costs +// +// Revision 1.11 2002/09/09 19:49:09 Flayra +// - Buildables now play animations better, without interrupting previous anims +// +// Revision 1.10 2002/08/31 18:01:00 Flayra +// - Work at VALVe +// +// Revision 1.9 2002/08/16 02:32:45 Flayra +// - Added damage types +// - Added visual health for commander and marines +// +// Revision 1.8 2002/08/02 22:02:09 Flayra +// - New alert system +// +// Revision 1.7 2002/07/26 23:03:56 Flayra +// - Don't play "hurt/wound" sounds when we don't actually take damage (GetCanEntityDoDamageTo) +// - Generate numerical feedback for damage events +// +// Revision 1.6 2002/07/23 16:58:38 Flayra +// - Auto-build can't happen before game starts +// +// Revision 1.5 2002/07/01 21:15:46 Flayra +// - Added auto-build capability +// +// Revision 1.4 2002/06/25 17:31:24 Flayra +// - Regular update, don't assume anything about player building, renamed arsenal to armory +// +// Revision 1.3 2002/06/03 16:27:59 Flayra +// - Allow alien buildings to regenerate, renamed weapons factory and armory +// +// Revision 1.2 2002/05/28 17:37:27 Flayra +// - Added building recycling, mark mapper placed buildables so they aren't destroyed at the end of the round +// +// Revision 1.1 2002/05/23 02:34:00 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHBaseBuildable.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHParticleConstants.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHSoundListManager.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "mod/AvHPlayerUpgrade.h" +#include "dlls/animation.h" +#include "mod/AvHMovementUtil.h" + +const int kBaseBuildableSpawnAnimation = 0; +const int kBaseBuildableDeployAnimation = 1; +const int kBaseBuildableIdle1Animation = 2; +const int kBaseBuildableIdle2Animation = 3; +const int kBaseBuildableResearchingAnimation = 4; +const int kBaseBuildableActiveAnimation = 5; +const int kBaseBuildableFireAnimation = 6; +const int kBaseBuildableTakeDamageAnimation = 7; +const int kBaseBuildableDieForwardAnimation = 8; +const int kBaseBuildableDieLeftAnimation = 9; +const int kBaseBuildableDieBackwardAnimation = 10; +const int kBaseBuildableDieRightAnimation = 11; +const int kBaseBuildableSpecialAnimation = 12; + +extern int gRegenerationEventID; +extern AvHSoundListManager gSoundListManager; + +AvHBaseBuildable::AvHBaseBuildable(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser3) : AvHBuildable(inTechID), kStartAlpha(128), mAverageUseSoundLength(.5f) +{ + this->mClassName = inClassName; + this->mMessageID = inMessageID; + + this->mBaseHealth = GetGameRules()->GetBaseHealthForMessageID(inMessageID); + + char* theModelName = AvHSHUGetBuildTechModelName(inMessageID); + ASSERT(theModelName); + this->mModelName = theModelName; + + this->mSelectID = inUser3; + this->mTimeToConstruct = GetGameRules()->GetBuildTimeForMessageID(inMessageID); + + // Very important that this doesn't go in Init(), else mapper-placed structures disappear on map-reset + this->mPersistent = false; + + this->Init(); +} + +void AvHBaseBuildable::Init() +{ + if(this->pev) + { + InitializeBuildable(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, this->mSelectID); + } + + this->mTimeAnimationDone = 0; + this->mLastAnimationPlayed = -1; + this->mIsResearching = false; + this->mTimeOfLastAutoHeal = -1; + this->mInternalSetConstructionComplete = false; + this->mKilled = false; + this->mTimeOfLastDamageEffect = -1; + this->mTimeOfLastDamageUpdate = -1; + this->mTimeRecycleStarted = -1; + this->mTimeRecycleDone = -1; + this->mTimeOfLastDCRegeneration = -1; + SetThink(NULL); +} + +const float kAnimateThinkTime = .1f; + +void AvHBaseBuildable::AnimateThink() +{ + int theSequence = this->GetResearchAnimation(); + if(!this->mIsResearching) + { + // Play a random idle animation + theSequence = this->GetIdleAnimation(); + } + else + { + int a = 0; + } + + this->PlayAnimationAtIndex(theSequence); + + // Set our next think + float theUpdateTime = this->GetTimeForAnimation(theSequence); + this->pev->nextthink = gpGlobals->time + theUpdateTime; +} + +int AvHBaseBuildable::BloodColor( void ) +{ + int theBloodColor = DONT_BLEED; + + if(this->GetIsOrganic()) + { + theBloodColor = BLOOD_COLOR_GREEN; + } + + return theBloodColor; +} + +void AvHBaseBuildable::BuildableTouch(CBaseEntity* inEntity) +{ + if(inEntity->pev->team != this->pev->team) + { + this->Uncloak(); + + // GHOSTBUILDING: Destroy and return res. + if (this->mGhost && inEntity->IsAlive() && inEntity->IsPlayer()) + { + this->TakeDamage(inEntity->pev, this->pev, 80000, DMG_GENERIC); + + AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(this->pev->team)); + + if (theTeam) + { + float thePercentage = .8f; + float thePointsBack = GetGameRules()->GetCostForMessageID(this->mMessageID) * thePercentage; + theTeam->SetTeamResources(theTeam->GetTeamResources() + thePointsBack); + + AvHSUPlayNumericEventAboveStructure(thePointsBack, this); + } + + // Uncloak the player + AvHCloakable *theCloakable=dynamic_cast(inEntity); + if ( theCloakable ) { + theCloakable->Uncloak(); + } + } + } +} + +void AvHBaseBuildable::CheckEnabledState() +{ +} + +void AvHBaseBuildable::ConstructUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + bool theSuccess = false; + bool theIsBuilding = false; + bool theIsResearching = false; + float thePercentage = 0.0f; + + AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); + + // Only allow players to help along building, not researching + if(theIsBuilding) + { + // Only allow users from same team as turret deployer + float thePercentage = this->GetNormalizedBuildPercentage(); + if(pActivator->pev->team == this->pev->team && (thePercentage < 1.0f)) + { + AvHPlayer* thePlayer = dynamic_cast(pActivator); + ASSERT(thePlayer); + + // Only soldiers and builders can build + if(thePlayer->GetIsAbleToAct() && ((thePlayer->pev->iuser3 == AVH_USER3_MARINE_PLAYER) || (thePlayer->pev->iuser3 == AVH_USER3_ALIEN_PLAYER2))) + { + AvHBasePlayerWeapon* theWeapon = dynamic_cast(thePlayer->m_pActiveItem); + if(!theWeapon || theWeapon->CanHolster()) + { + thePlayer->PlayerConstructUse(); + + bool thePlaySound = false; + + // Ensure that buildings are never absolutely painful to create + int theBuildTime = max(GetGameRules()->GetBuildTimeForMessageID(this->mMessageID), 1); + + if((GetGameRules()->GetIsTesting() || GetGameRules()->GetCheatsEnabled()) && !GetGameRules()->GetIsCheatEnabled(kcSlowResearch)) + { + theBuildTime = 2; + } + + // Make non-frame-rate dependent + const float kDefaultInterval = .1f; + float theTimeOfLastConstructUse = thePlayer->GetTimeOfLastConstructUse(); + + float theInterval = min(max(gpGlobals->time - theTimeOfLastConstructUse, 0.0f), kDefaultInterval); + thePercentage += (theInterval/(float)theBuildTime); + + thePlayer->SetTimeOfLastConstructUse(gpGlobals->time); + + if(gpGlobals->time > (this->mLastTimePlayedSound + this->mAverageUseSoundLength)) + { + AvHSUPlayRandomConstructionEffect(thePlayer, this); + this->mLastTimePlayedSound = gpGlobals->time; + } + + // Given the number of constructors, what's chance of starting a new sound? + float theChanceForNewSound = (gpGlobals->frametime/(this->mAverageUseSoundLength));// /2.0f)); + float theRandomFloat = RANDOM_FLOAT(0.0f, 1.0f); + if(theRandomFloat < theChanceForNewSound) + { + AvHSUPlayRandomConstructionEffect(thePlayer, this); + } + + //if(RANDOM_LONG(0, 20) == 0) + //{ + // char theMessage[128]; + // sprintf(theMessage, "Time passed: %f, ticks: %d, rate: %f\n", theTimePassed, this->mPreThinkTicks, this->mPreThinkFrameRate); + // UTIL_SayText(theMessage, this); + //} + + this->SetNormalizedBuildPercentage(thePercentage); + + theSuccess = true; + + // GHOSTBUILD: Manifest structure. + pev->renderamt = 255; + pev->rendermode = kRenderNormal; + pev->solid = SOLID_BBOX; + this->mGhost = false; + + } + } + } + } + + // Clear out +use sound when ineffective + if(!theSuccess) + { + EMIT_SOUND(pActivator->edict(), CHAN_ITEM, "common/null.wav", 1.0, ATTN_NORM); + } +} + +bool AvHBaseBuildable::Energize(float inEnergyAmount) +{ + return false; +} + +int AvHBaseBuildable::GetBaseHealth() const +{ + return this->mBaseHealth; +} + +char* AvHBaseBuildable::GetClassName() const +{ + return this->mClassName; +} + +int AvHBaseBuildable::GetIdleAnimation() const +{ + int theAnimation = this->GetIdle1Animation(); + + if(RANDOM_LONG(0, 1)) + { + theAnimation = this->GetIdle2Animation(); + } + + return theAnimation; +} + +char* AvHBaseBuildable::GetDeploySound() const +{ + return NULL; +} + +bool AvHBaseBuildable::GetIsBuilt() const +{ + return this->mInternalSetConstructionComplete; +} + +bool AvHBaseBuildable::GetIsOrganic() const +{ + return false; +} + +char* AvHBaseBuildable::GetKilledSound() const +{ + return NULL; +} + +float AvHBaseBuildable::GetNormalizedBuildPercentage() const +{ + //return this->pev->fuser1/kNormalizationNetworkFactor; + + bool theIsBuilding; + bool theIsResearching; + float thePercentage; + AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); + + // Check for energy special case + if(theIsBuilding && theIsResearching) + { + thePercentage = 1.0f; + } + + return thePercentage; +} + +float AvHBaseBuildable::GetTimeForAnimation(int inIndex) const +{ + return GetSequenceDuration(GET_MODEL_PTR(ENT(pev)), this->pev); +} + +int AvHBaseBuildable::GetStartAlpha() const +{ + return kStartAlpha; +} + +void AvHBaseBuildable::FireDeathTarget() const +{ + if(this->mTargetOnDeath != "") + { + FireTargets(this->mTargetOnDeath.c_str(), NULL, NULL, USE_TOGGLE, 0.0f); + } +} + +void AvHBaseBuildable::FireSpawnTarget() const +{ + if(this->mTargetOnSpawn != "") + { + FireTargets(this->mTargetOnSpawn.c_str(), NULL, NULL, USE_TOGGLE, 0.0f); + } +} + +void AvHBaseBuildable::KeyValue(KeyValueData* pkvd) +{ + // Any entity placed by the mapper is persistent + this->SetPersistent(); + + if(FStrEq(pkvd->szKeyName, "targetonspawn")) + { + this->mTargetOnSpawn = pkvd->szValue; + pkvd->fHandled = TRUE; + } + else if(FStrEq(pkvd->szKeyName, "targetondeath")) + { + this->mTargetOnDeath = pkvd->szValue; + pkvd->fHandled = TRUE; + } + else if(FStrEq(pkvd->szKeyName, "teamchoice")) + { + //this->mTeam = (AvHTeamNumber)(atoi(pkvd->szValue)); + this->pev->team = (AvHTeamNumber)(atoi(pkvd->szValue)); + + pkvd->fHandled = TRUE; + } + else if(FStrEq(pkvd->szKeyName, "angles")) + { + // TODO: Insert code here + //pkvd->fHandled = TRUE; + int a = 0; + } + else + { + CBaseAnimating::KeyValue(pkvd); + } +} + +void AvHBaseBuildable::PlayAnimationAtIndex(int inIndex, bool inForce, float inFrameRate) +{ + // Only play animations on buildings that we have artwork for + bool thePlayAnim = false; + + if(inIndex >= 0) + { + switch(this->mMessageID) + { + case BUILD_RESOURCES: + case BUILD_ARMSLAB: + case BUILD_COMMANDSTATION: + case BUILD_INFANTRYPORTAL: + case BUILD_TURRET_FACTORY: + case TURRET_FACTORY_UPGRADE: + case BUILD_ARMORY: + case ARMORY_UPGRADE: + case BUILD_OBSERVATORY: + case BUILD_TURRET: + case BUILD_SIEGE: + case BUILD_PROTOTYPE_LAB: + case ALIEN_BUILD_HIVE: + case ALIEN_BUILD_RESOURCES: + case ALIEN_BUILD_OFFENSE_CHAMBER: + case ALIEN_BUILD_DEFENSE_CHAMBER: + case ALIEN_BUILD_SENSORY_CHAMBER: + case ALIEN_BUILD_MOVEMENT_CHAMBER: + thePlayAnim = true; + } + } + + // Make sure we're not interrupting another animation + if(thePlayAnim) + { + // Allow forcing of new animation, but it's better to complete current animation then interrupt it and play it again + float theCurrentTime = gpGlobals->time; + if((theCurrentTime >= this->mTimeAnimationDone) || (inForce && (inIndex != this->mLastAnimationPlayed))) + { + this->pev->sequence = inIndex; + this->pev->frame = 0; + ResetSequenceInfo(); + + this->pev->framerate = inFrameRate; + + // Set to last frame to play backwards + if(this->pev->framerate < 0) + { + this->pev->frame = 255; + } + + this->mLastAnimationPlayed = inIndex; + float theTimeForAnim = this->GetTimeForAnimation(inIndex); + this->mTimeAnimationDone = theCurrentTime + theTimeForAnim; + + // Recalculate size + //Vector theMinSize, theMaxSize; + //this->ExtractBbox(this->pev->sequence, (float*)&theMinSize, (float*)&theMaxSize); + //UTIL_SetSize(this->pev, theMinSize, theMaxSize); + } + } +} + +void AvHBaseBuildable::SetNormalizedBuildPercentage(float inPercentage, bool inForceIfComplete) +{ + // Get previous build percentage so we can add hitpoints as structure is building. This means that structures that are hurt while building finish hurt. + bool theIsBuilding, theIsResearching; + float theNormalizedBuildPercentage = 0.0f; + AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, theNormalizedBuildPercentage); + + float theDiff = inPercentage - theNormalizedBuildPercentage; + if(theDiff > 0) + { + this->pev->health += theDiff*(1.0f - kBaseHealthPercentage)*this->mBaseHealth; + this->pev->health = min(max(0.0f, this->pev->health), (float)this->mBaseHealth); + } + else + { + int a = 0; + } + + // Set new build state + AvHSHUSetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, true, inPercentage); + + if(inPercentage >= 1.0f) + { + this->InternalSetConstructionComplete(inForceIfComplete); + } + + this->HealthChanged(); +} + +void AvHBaseBuildable::UpdateOnRecycle() +{ + // empty, override to add events on recycle for buildings +} + +Vector AvHBaseBuildable::EyePosition( ) { + + if ( this->pev->iuser3 == AVH_USER3_HIVE ) + return CBaseEntity::EyePosition(); + + vec3_t position=AvHSHUGetRealLocation(this->pev->origin, this->pev->mins, this->pev->maxs); + position[2]+=10; + return position; +} +void AvHBaseBuildable::StartRecycle() +{ + if(!GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING)) + { + int theRecycleTime = (GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsCheatEnabled(kcSlowResearch)) ? 2 : BALANCE_VAR(kRecycleTime); + + // Play recycle animation in reverse (would like to play them slower according to recycle time, but it doesn't work for all structures, seems dependent on # of keyframes) + int theAnimation = this->GetRecycleAnimation(); + float theTimeForAnim = this->GetTimeForAnimation(theAnimation); + float theFrameRate = -1;//-theTimeForAnim/theRecycleTime; + this->PlayAnimationAtIndex(theAnimation, true, theFrameRate); + + // Schedule time to give points back + SetThink(&AvHBaseBuildable::RecycleComplete); + + this->mTimeRecycleStarted = gpGlobals->time; + + this->mTimeRecycleDone = gpGlobals->time + theRecycleTime; + + this->pev->nextthink = this->mTimeRecycleDone; + + float theVolume = .5f; + EMIT_SOUND(this->edict(), CHAN_AUTO, kBuildableRecycleSound, theVolume, ATTN_NORM); + + SetUpgradeMask(&this->pev->iuser4, MASK_RECYCLING); + + // run any events for this class on recycling the structure + this->UpdateOnRecycle(); + + // Remove tech immediately, so research or building isn't started using this tech + this->TriggerRemoveTech(); + } +} + +bool AvHBaseBuildable::GetIsRecycling() const +{ + return GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING); +} + +bool AvHBaseBuildable::GetIsTechActive() const +{ + bool theIsActive = false; + + if(this->GetIsBuilt() && (this->pev->health > 0) && !GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING)) + { + theIsActive = true; + } + + return theIsActive; +} + +int AvHBaseBuildable::GetActiveAnimation() const +{ + return kBaseBuildableActiveAnimation; +} + +CBaseEntity* AvHBaseBuildable::GetAttacker() +{ + CBaseEntity* theAttacker = this; + + AvHBuildable* theBuildable = dynamic_cast(this); + if(theBuildable) + { + int theBuilderIndex = theBuildable->GetBuilder(); + CBaseEntity* theBuilderEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theBuilderIndex)); + if(theBuilderEntity) + { + theAttacker = theBuilderEntity; + } + } + + return theAttacker; +} + +int AvHBaseBuildable::GetDeployAnimation() const +{ + return kBaseBuildableDeployAnimation; +} + +int AvHBaseBuildable::GetIdle1Animation() const +{ + return kBaseBuildableIdle1Animation; +} + +int AvHBaseBuildable::GetIdle2Animation() const +{ + return kBaseBuildableIdle2Animation; +} + +int AvHBaseBuildable::GetKilledAnimation() const +{ + return kBaseBuildableDieForwardAnimation; +} + +AvHMessageID AvHBaseBuildable::GetMessageID() const +{ + return this->mMessageID; +} + +int AvHBaseBuildable::GetMoveType() const +{ + return MOVETYPE_TOSS; +} + +bool AvHBaseBuildable::GetTriggerAlertOnDamage() const +{ + return true; +} + +float AvHBaseBuildable::GetTimeAnimationDone() const +{ + return this->mTimeAnimationDone; +} + +int AvHBaseBuildable::GetResearchAnimation() const +{ + return kBaseBuildableResearchingAnimation; +} + +// Play deploy animation backwards +int AvHBaseBuildable::GetRecycleAnimation() const +{ + int theAnimation = -1; + + if(this->GetIsBuilt()) + { + theAnimation = this->GetDeployAnimation(); + } + + return theAnimation; +} + +char* AvHBaseBuildable::GetModelName() const +{ + return this->mModelName; +} + +int AvHBaseBuildable::GetSpawnAnimation() const +{ + return kBaseBuildableSpawnAnimation; +} + +int AvHBaseBuildable::GetTakeDamageAnimation() const +{ + int theAnimation = -1; + + if(this->GetIsBuilt()) + { + theAnimation = kBaseBuildableTakeDamageAnimation; + } + + return theAnimation; +} + + +AvHTeamNumber AvHBaseBuildable::GetTeamNumber() const +{ + AvHTeamNumber ret=TEAM_IND; + if ( this->pev ) + ret=(AvHTeamNumber)this->pev->team; + return ret; +} + +void AvHBaseBuildable::Killed(entvars_t* pevAttacker, int iGib) +{ + bool theInReset = GetGameRules()->GetIsGameInReset(); + + AvHBaseBuildable::SetHasBeenKilled(); + GetGameRules()->RemoveEntityUnderAttack( this->entindex() ); + + this->mKilled = true; + this->mInternalSetConstructionComplete = false; + this->mTimeOfLastAutoHeal = -1; + + if (!theInReset) + { + // : 980 + // Less smoke for recycled buildings + this->TriggerDeathAudioVisuals(iGib == GIB_RECYCLED); + + if(!this->GetIsOrganic()) + { + // More sparks for recycled buildings + int numSparks = ( iGib == GIB_RECYCLED ) ? 7 : 3; + for ( int i=0; i < numSparks; i++ ) { + Vector vecSrc = Vector( (float)RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ), (float)RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ), (float)0 ); + vecSrc = vecSrc + Vector( (float)0, (float)0, (float)RANDOM_FLOAT( pev->origin.z, pev->absmax.z ) ); + UTIL_Sparks(vecSrc); + } + } + // : + } + this->TriggerRemoveTech(); + + AvHSURemoveEntityFromHotgroupsAndSelection(this->entindex()); + + if(pevAttacker) + { + const char* theClassName = STRING(this->pev->classname); + AvHPlayer* inPlayer = dynamic_cast(CBaseEntity::Instance(ENT(pevAttacker))); + if(inPlayer && theClassName) + { + inPlayer->LogPlayerAction("structure_destroyed", theClassName); + GetGameRules()->RewardPlayerForKill(inPlayer, this); + } + } + + if(this->GetIsPersistent()) + { + this->SetInactive(); + } + else + { + CBaseAnimating::Killed(pevAttacker, iGib); + } +} + +void AvHBaseBuildable::SetActive() +{ + this->pev->effects &= ~EF_NODRAW; +} + +void AvHBaseBuildable::SetInactive() +{ + this->pev->health = 0; + this->pev->effects |= EF_NODRAW; + this->pev->solid = SOLID_NOT; + this->pev->takedamage = DAMAGE_NO; + SetUpgradeMask(&this->pev->iuser4, MASK_PARASITED, false);//: remove parasite flag to prevent phantom parasites. + //this->pev->deadflag = DEAD_DEAD; + SetThink(NULL); +} + +int AvHBaseBuildable::ObjectCaps(void) +{ + return FCAP_CONTINUOUS_USE; +} + +void AvHBaseBuildable::Precache(void) +{ + CBaseAnimating::Precache(); + + char* theDeploySound = this->GetDeploySound(); + if(theDeploySound) + { + PRECACHE_UNMODIFIED_SOUND(theDeploySound); + } + char* theKilledSound = this->GetKilledSound(); + if(theKilledSound) + { + PRECACHE_UNMODIFIED_SOUND(theKilledSound); + } + + PRECACHE_UNMODIFIED_MODEL(this->mModelName); + + PRECACHE_UNMODIFIED_SOUND(kBuildableRecycleSound); + //PRECACHE_UNMODIFIED_SOUND(kBuildableHurt1Sound); + //PRECACHE_UNMODIFIED_SOUND(kBuildableHurt2Sound); + + this->mElectricalSprite = PRECACHE_UNMODIFIED_MODEL(kElectricalSprite); +} + +void AvHBaseBuildable::RecycleComplete() +{ + // Look at whether it has been built and health to determine how many points to give back + float thePercentage = BALANCE_VAR(kRecycleResourcePercentage); + + if(!this->GetIsBuilt()) + { + thePercentage = .8f; + } + + // Make sure the building is still alive, can't get points back if it's dead + if(this->pev->health <= 0) + { + thePercentage = 0.0f; + } + + // Look up team + AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)this->pev->team); + if(theTeam) + { + bool theIsEnergyTech = AvHSHUGetDoesTechCostEnergy(this->mMessageID); + ASSERT(!theIsEnergyTech); + + float thePointsBack = GetGameRules()->GetCostForMessageID(this->mMessageID)*thePercentage; + theTeam->SetTeamResources(theTeam->GetTeamResources() + thePointsBack); + + // Play "+ resources" event + AvHSUPlayNumericEventAboveStructure(thePointsBack, this); + + // : 980 + // Less smoke and more sparks for recycled buildings + this->Killed(this->pev, GIB_RECYCLED); + // : + } +} + +// Sets the template iuser3 for this buildable. This is stored outside of the actual iuser3 because sometimes the pev isn't allocated yet. +void AvHBaseBuildable::SetSelectID(int inSelectID) +{ + this->mSelectID = inSelectID; +} + +bool AvHBaseBuildable::Regenerate(float inRegenerationAmount, bool inPlaySound, bool dcHealing) +{ + bool theDidHeal = false; + if ( gpGlobals->time > this->mTimeOfLastDCRegeneration + BALANCE_VAR(kDefenseChamberThinkInterval) - 0.05f || (dcHealing == false)) { + if ( dcHealing ) + this->mTimeOfLastDCRegeneration = gpGlobals->time; + float theMaxHealth = this->mBaseHealth; + + if(!this->GetIsBuilt()) + { + float theNormalizedBuildPercentage = this->GetNormalizedBuildPercentage(); + + theMaxHealth = (kBaseHealthPercentage + theNormalizedBuildPercentage*(1.0f - kBaseHealthPercentage))*this->mBaseHealth; + } + + // If we aren't at full health, heal health + if(this->pev->health < theMaxHealth) + { + this->pev->health = min(theMaxHealth, this->pev->health + inRegenerationAmount); + this->HealthChanged(); + theDidHeal = true; + } + + // Play regen event + if(theDidHeal) + { + if(inPlaySound) + { + // Play regeneration event + PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + } + } + } + return theDidHeal; +} + +void AvHBaseBuildable::ResetEntity() +{ + CBaseAnimating::ResetEntity(); + + this->Init(); + + this->Materialize(); + + this->pev->effects = 0; + + // Build it if marked as starting built + if(this->pev->spawnflags & 1) + this->SetConstructionComplete(true); + + this->mKilled = false; +} + +void AvHBaseBuildable::InternalSetConstructionComplete(bool inForce) +{ + if(!this->mInternalSetConstructionComplete || inForce) + { + // Fully built items are no longer marked as buildable + SetUpgradeMask(&this->pev->iuser4, MASK_BUILDABLE, false); + + this->pev->rendermode = kRenderNormal; + this->pev->renderamt = 255; + + // GHOSTBUILD: Ensure that finished buildings aren't ghosted. + this->mGhost = false; + this->pev->solid = SOLID_BBOX; + + this->SetHasBeenBuilt(); + + this->SetActive(); + + this->mInternalSetConstructionComplete = true; + + this->TriggerAddTech(); + + char* theDeploySound = this->GetDeploySound(); + if(theDeploySound) + { + EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, theDeploySound, 1, ATTN_NORM); + } + + int theDeployAnimation = this->GetDeployAnimation(); + + this->PlayAnimationAtIndex(theDeployAnimation, true); + } +} + +void AvHBaseBuildable::SetConstructionComplete(bool inForce) +{ + this->SetNormalizedBuildPercentage(1.0f, inForce); +} + +void AvHBaseBuildable::SetAverageUseSoundLength(float inLength) +{ + this->mAverageUseSoundLength = inLength; +} + +void AvHBaseBuildable::SetResearching(bool inState) +{ + int theSequence = this->GetResearchAnimation(); + + if(!inState) + { + theSequence = this->GetIdleAnimation(); + } + + this->PlayAnimationAtIndex(theSequence, true); + + this->mIsResearching = inState; +} + +// Requires mSelectID and mMessageID to be set +// Sets the pev user variables, mBaseHealth, pev->health and pev->armorvalue +void AvHBaseBuildable::InternalInitializeBuildable() +{ + // Always buildable + InitializeBuildable(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, this->mSelectID); + this->mBaseHealth = GetGameRules()->GetBaseHealthForMessageID(this->mMessageID); + this->pev->health = this->mBaseHealth*kBaseHealthPercentage; + this->pev->max_health = this->mBaseHealth; + + // Store max health in armorvalue + //this->pev->armorvalue = GetGameRules()->GetBaseHealthForMessageID(this->mMessageID); +} + +const float kFallThinkInterval = .1f; + +void AvHBaseBuildable::Spawn() +{ + this->Precache(); + + CBaseAnimating::Spawn(); + + // Get building size in standard way + SET_MODEL(ENT(this->pev), this->mModelName); + + pev->movetype = this->GetMoveType(); + pev->solid = SOLID_BBOX; + + UTIL_SetOrigin( pev, pev->origin ); + + this->Materialize(); + + SetTouch(&AvHBaseBuildable::BuildableTouch); + + if(this->pev->spawnflags & 1) + this->SetConstructionComplete(true); + + // GHOSTBUILD: Mark as unmanifested if it's a marine structure. + if (!this->GetIsOrganic()) + { + pev->renderamt = 170; + pev->rendermode = kRenderTransTexture; + this->mGhost = true; + } +} + + +void AvHBaseBuildable::FallThink() +{ + pev->nextthink = gpGlobals->time + kFallThinkInterval; + + if ( pev->flags & FL_ONGROUND ) + { + this->Materialize(); + + // Start animating + SetThink(&AvHBaseBuildable::AnimateThink); + this->pev->nextthink = gpGlobals->time + kAnimateThinkTime; + } +} + +int AvHBaseBuildable::GetSequenceForBoundingBox() const +{ + return 0; +} + +void AvHBaseBuildable::Materialize() +{ + this->pev->solid = SOLID_BBOX; + + this->pev->movetype = this->GetMoveType(); + + this->pev->classname = MAKE_STRING(this->mClassName); + + this->pev->takedamage = DAMAGE_YES; + SetBits(this->pev->flags, FL_MONSTER); + + // Always buildable + this->InternalInitializeBuildable(); + + this->SetNormalizedBuildPercentage(0.0f); + + // NOTE: fuser2 is used for repairing structures + + Vector theMinSize, theMaxSize; + //int theSequence = this->GetSequenceForBoundingBox(); + + // Get height needed for model + //this->ExtractBbox(theSequence, (float*)&theMinSize, (float*)&theMaxSize); + //float theHeight = theMaxSize.z - theMinSize.z; + + AvHSHUGetSizeForTech(this->GetMessageID(), theMinSize, theMaxSize); + + UTIL_SetSize(pev, theMinSize, theMaxSize); + + this->PlayAnimationAtIndex(this->GetSpawnAnimation(), true); + + SetUse(&AvHBaseBuildable::ConstructUse); +} + +int AvHBaseBuildable::TakeDamage(entvars_t* inInflictor, entvars_t* inAttacker, float inDamage, int inBitsDamageType) +{ + if(GetGameRules()->GetIsCheatEnabled(kcHighDamage)) + { + inDamage *= 50; + } + + if(!inAttacker) + { + inAttacker = inInflictor; + } + + if(!inInflictor) + { + inInflictor = inAttacker; + } + + // Take into account handicap + AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(inAttacker->team)); + if(theTeam) + { + float theHandicap = theTeam->GetHandicap(); + inDamage *= theHandicap; + } + + CBaseEntity* inInflictorEntity = CBaseEntity::Instance(inInflictor); + float theDamage = 0; + + // Take half damage from piercing + if(inBitsDamageType & NS_DMG_PIERCING) + { + inDamage /= 2.0f; + } + + // Take double damage from blast + if(inBitsDamageType & NS_DMG_BLAST) + { + inDamage *= 2.0f; + } + + if((inBitsDamageType & NS_DMG_ORGANIC) && !this->GetIsOrganic()) + { + inDamage = 0.0f; + } + + theDamage = AvHPlayerUpgrade::CalculateDamageLessArmor((AvHUser3)this->pev->iuser3, this->pev->iuser4, inDamage, this->pev->armorvalue, inBitsDamageType, GetGameRules()->GetNumActiveHives((AvHTeamNumber)this->pev->team)); + if(theDamage > 0) + { + int theAnimationIndex = this->GetTakeDamageAnimation(); + if(theAnimationIndex >= 0) + { + this->PlayAnimationAtIndex(theAnimationIndex, true); + } + + // Award experience to attacker + CBaseEntity* theEntity = CBaseEntity::Instance(ENT(inAttacker)); + AvHPlayer* inAttacker = dynamic_cast(theEntity); + if(inAttacker && (inAttacker->pev->team != this->pev->team)) + { + inAttacker->AwardExperienceForObjective(theDamage, this->GetMessageID()); + } + } + + int theReturnValue = 0; + + if(theDamage > 0.0f) + { + if(this->GetTriggerAlertOnDamage()) + GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_UNDER_ATTACK, this->entindex()); + + theDamage = CBaseAnimating::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType); + + bool theDrawDamage = (ns_cvar_float(&avh_drawdamage) > 0); + + if(theDrawDamage) + { + Vector theMinSize; + Vector theMaxSize; + AvHSHUGetSizeForTech(this->GetMessageID(), theMinSize, theMaxSize); + + Vector theStartPos = this->pev->origin; + theStartPos.z += theMaxSize.z; + + // Draw for everyone (team is 0 after inDamage parameter) + AvHSUPlayNumericEvent(-inDamage, this->edict(), theStartPos, 0, kNumericalInfoHealthEvent, 0); + } + } + + // Structures uncloak when damaged + this->Uncloak(); + + this->HealthChanged(); + + return theDamage; +} + +void AvHBaseBuildable::TechnologyBuilt(AvHMessageID inMessageID) +{ +} + +void AvHBaseBuildable::WorldUpdate() +{ + this->UpdateTechSlots(); + + // Organic buildings heal themselves + if(this->GetIsOrganic()) + { + this->UpdateAutoHeal(); + } + else + { + //this->UpdateDamageEffects(); + } + + // If we're electrified, set render mode + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_11)) + { + // Base marine building + const int kElectrifyRenderMode = kRenderFxGlowShell; + const int kElectrifyRenderAmount = 40; + + this->pev->renderfx = kElectrifyRenderMode; + this->pev->renderamt = kElectrifyRenderAmount; + this->pev->rendercolor.x = kTeamColors[this->pev->team][0]; + this->pev->rendercolor.y = kTeamColors[this->pev->team][1]; + this->pev->rendercolor.z = kTeamColors[this->pev->team][2]; + + // Check for enemy players/structures nearby + CBaseEntity* theBaseEntity = NULL; + int theNumEntsDamaged = 0; + + while(((theBaseEntity = UTIL_FindEntityInSphere(theBaseEntity, this->pev->origin, BALANCE_VAR(kElectricalRange))) != NULL) && (theNumEntsDamaged < BALANCE_VAR(kElectricalMaxTargets))) + { + // When "electric" cheat is enabled, shock all non-self entities, else shock enemies + if((GetGameRules()->GetIsCheatEnabled(kcElectric) && (theBaseEntity != this)) || ((theBaseEntity->pev->team != this->pev->team) && theBaseEntity->IsAlive())) + { + // Make sure it's not blocked + TraceResult theTraceResult; + UTIL_TraceLine(this->pev->origin, theBaseEntity->pev->origin, ignore_monsters, dont_ignore_glass, this->edict(), &theTraceResult); + if(theTraceResult.flFraction == 1.0f) + { + CBaseEntity* theAttacker = this->GetAttacker(); + ASSERT(theAttacker); + + if(theBaseEntity->TakeDamage(this->pev, theAttacker->pev, BALANCE_VAR(kElectricalDamage), DMG_GENERIC) > 0) + { + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE(TE_BEAMENTPOINT); + WRITE_SHORT(theBaseEntity->entindex()); + WRITE_COORD( this->pev->origin.x); + WRITE_COORD( this->pev->origin.y); + WRITE_COORD( this->pev->origin.z); + + WRITE_SHORT( this->mElectricalSprite ); + WRITE_BYTE( 0 ); // framestart + WRITE_BYTE( (int)15); // framerate + WRITE_BYTE( (int)(2) ); // life + WRITE_BYTE( 60 ); // width + WRITE_BYTE( 15 ); // noise + WRITE_BYTE( (int)this->pev->rendercolor.x ); // r, g, b + WRITE_BYTE( (int)this->pev->rendercolor.y ); // r, g, b + WRITE_BYTE( (int)this->pev->rendercolor.z ); // r, g, b + WRITE_BYTE( 200 ); // brightness + WRITE_BYTE( 10 ); // speed + MESSAGE_END(); + + gSoundListManager.PlaySoundInList(kElectricSparkSoundList, this, CHAN_AUTO, .7f); + + UTIL_Sparks(theBaseEntity->pev->origin); + + theNumEntsDamaged++; + } + } + } + } + } +} + +bool AvHBaseBuildable::GetHasBeenKilled() const +{ + return this->mKilled; +} + +bool AvHBaseBuildable::GetIsTechnologyAvailable(AvHMessageID inMessageID) const +{ + bool theTechnologyAvailable = false; + + const AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)this->pev->team); + if(theTeam) + { + // Don't allow electrical upgrade if we're already electrified + if((inMessageID != RESEARCH_ELECTRICAL) || !GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_11)) + { + theTechnologyAvailable = (theTeam->GetIsTechnologyAvailable(inMessageID) && this->GetIsBuilt() && !GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING)); + + // Enable recycle button for unbuilt structures + if(!this->GetIsBuilt() && (inMessageID == BUILD_RECYCLE)) + { + theTechnologyAvailable = true; + } + } + } + return theTechnologyAvailable; +} + + +void AvHBaseBuildable::UpdateTechSlots() +{ + // Get tech slot for this structure + AvHGamerules* theGameRules = GetGameRules(); + const AvHTeam* theTeam = theGameRules->GetTeam((AvHTeamNumber)this->pev->team); + if(theTeam) + { + // Update tech slots + AvHTechSlots theTechSlots; + if(theTeam->GetTechSlotManager().GetTechSlotList((AvHUser3)this->pev->iuser3, theTechSlots)) + { + // Clear the existing slots + int theMasks[kNumTechSlots] = {MASK_UPGRADE_1, MASK_UPGRADE_2, MASK_UPGRADE_3, MASK_UPGRADE_4, MASK_UPGRADE_5, MASK_UPGRADE_6, MASK_UPGRADE_7, MASK_UPGRADE_8}; + + // Each slot if we technology is available + for(int i = 0; i < kNumTechSlots; i++) + { + int theCurrentMask = theMasks[i]; + this->pev->iuser4 &= ~theCurrentMask; + + AvHMessageID theMessage = theTechSlots.mTechSlots[i]; + if(theMessage != MESSAGE_NULL) + { + if(this->GetIsTechnologyAvailable(theMessage)) + { + this->pev->iuser4 |= theCurrentMask; + } + } + } + } + + // Update recycling status bar + if(GetHasUpgrade(this->pev->iuser4, MASK_RECYCLING)) + { + float theNormalizedRecyclingFactor = (gpGlobals->time - this->mTimeRecycleStarted)/(this->mTimeRecycleDone - this->mTimeRecycleStarted); + theNormalizedRecyclingFactor = min(max(theNormalizedRecyclingFactor, 0.0f), 1.0f); + + //theResearchEntity->pev->fuser1 = (kResearchFuser1Base + theNormalizedResearchFactor)*kNormalizationNetworkFactor; + AvHSHUSetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, false, theNormalizedRecyclingFactor); + } + } +} + +void AvHBaseBuildable::TriggerDeathAudioVisuals(bool isRecycled) +{ + AvHClassType theTeamType = AVH_CLASS_TYPE_UNDEFINED; + AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)this->pev->team); + if(theTeam) + { + theTeamType = theTeam->GetTeamType(); + } + + switch(theTeamType) + { + case AVH_CLASS_TYPE_ALIEN: + AvHSUPlayParticleEvent(kpsChamberDeath, this->edict(), this->pev->origin); + break; + + case AVH_CLASS_TYPE_MARINE: + // lots of smoke + // : 980 + // Less smoke for recycled buildings + int smokeScale = isRecycled ? 15 : 25; + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ) ); + WRITE_COORD( RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ) ); + WRITE_COORD( RANDOM_FLOAT( pev->absmin.z, pev->absmax.z ) ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( smokeScale ); // scale * 10 + WRITE_BYTE( 10 ); // framerate + MESSAGE_END(); + break; + } + + char* theKilledSound = this->GetKilledSound(); + if(theKilledSound) + { + EMIT_SOUND(ENT(this->pev), CHAN_AUTO, theKilledSound, 1.0, ATTN_IDLE); + } +} + +void AvHBaseBuildable::UpdateAutoBuild(float inTimePassed) +{ + if(GetGameRules()->GetGameStarted()) + { + // TF2 snippet for making sure players don't get stuck in buildings + if(this->pev->solid == SOLID_NOT) + { + //trace_t tr; + //UTIL_TraceHull(this->pev->origin, this->pev->origin, this->pev->mins, this->pev->maxs, this->pev, &tr); + //if(!tr.startsolid && !tr.allsolid ) + //if(AvHSHUGetIsAreaFree(this->pev->origin, this->pev->mins, this->pev->maxs, this->edict())) + + // Check point contents for corner points + float theMinX = this->pev->origin.x + this->pev->mins.x; + float theMinY = this->pev->origin.y + this->pev->mins.y; + float theMinZ = this->pev->origin.z + this->pev->mins.z; + float theMaxX = this->pev->origin.x + this->pev->maxs.x; + float theMaxY = this->pev->origin.y + this->pev->maxs.y; + float theMaxZ = this->pev->origin.z + this->pev->maxs.z; + + // Do tracelines between the corners, to make sure there's no geometry inside the box + Vector theMinVector(theMinX, theMinY, theMinZ); + Vector theMaxVector(theMaxX, theMaxY, theMaxZ); + if(AvHSHUTraceLineIsAreaFree(theMinVector, theMaxVector, this->edict())) + { + theMinVector = Vector(theMaxX, theMinY, theMinZ); + theMaxVector = Vector(theMinX, theMaxY, theMaxZ); + if(AvHSHUTraceLineIsAreaFree(theMinVector, theMaxVector, this->edict())) + { + theMinVector = Vector(theMaxX, theMaxY, theMinZ); + theMaxVector = Vector(theMinX, theMinY, theMaxZ); + if(AvHSHUTraceLineIsAreaFree(theMinVector, theMaxVector, this->edict())) + { + this->pev->solid = SOLID_BBOX; + + // Relink into world (not sure if this is necessary) + UTIL_SetOrigin(this->pev, this->pev->origin); + } + } + } + } + else + { + // If it's not fully built, build more + bool theIsBuilding, theIsResearching; + float thePercentage; + AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); + + float theBuildTime = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID()); + float theBuildPercentage = inTimePassed/theBuildTime; + + float theNewPercentage = min(thePercentage + theBuildPercentage, 1.0f); + this->SetNormalizedBuildPercentage(theNewPercentage); + +// // Increase built time if not fully built +// if(!this->GetHasBeenBuilt() && (theNewPercentage >= 1.0f)) +// { +// this->SetConstructionComplete(); +// } +//// else +//// { +//// this->pev->rendermode = kRenderTransTexture; +//// int theStartAlpha = this->GetStartAlpha(); +//// this->pev->renderamt = theStartAlpha + theNewPercentage*(255 - theStartAlpha); +//// } +// +// AvHSHUSetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, true, theNewPercentage); + + // TODO: Heal self? + // TODO: Play ambient sounds? + } + } +} + +void AvHBaseBuildable::UpdateAutoHeal() +{ + if(GetGameRules()->GetGameStarted() && this->GetIsBuilt()) + { + if((this->mTimeOfLastAutoHeal != -1) && (gpGlobals->time > this->mTimeOfLastAutoHeal)) + { + float theMaxHealth = GetGameRules()->GetBaseHealthForMessageID(this->GetMessageID()); + if(this->pev->health < theMaxHealth) + { + float theTimePassed = (gpGlobals->time - this->mTimeOfLastAutoHeal); + float theHitPointsToGain = theTimePassed*BALANCE_VAR(kOrganicStructureHealRate); + + this->pev->health += theHitPointsToGain; + this->pev->health = min(this->pev->health, theMaxHealth); + + this->HealthChanged(); + } + } + + this->mTimeOfLastAutoHeal = gpGlobals->time; + } +} + +void AvHBaseBuildable::UpdateDamageEffects() +{ + if(GetGameRules()->GetGameStarted() && this->GetIsBuilt()) + { + // Add special effects for structures that are hurt or almost dead + float theMaxHealth = GetGameRules()->GetBaseHealthForMessageID(this->GetMessageID()); + float theHealthScalar = this->pev->health/theMaxHealth; + float theTimeInterval = max(gpGlobals->time - this->mTimeOfLastDamageUpdate, .1f); + + const float kParticleSystemLifetime = 5.0f; + int theAverageSoundInterval = -1; + + // If we're at 25% health or less, emit black smoke + if(gpGlobals->time > (this->mTimeOfLastDamageEffect + kParticleSystemLifetime)) + { + if(theHealthScalar < .25f) + { + AvHSUPlayParticleEvent(kpsBuildableLightDamage, this->edict(), this->pev->origin); + this->mTimeOfLastDamageEffect = gpGlobals->time; + theAverageSoundInterval = 3; + } + // If we're at 50% health or less, emit light smoke + else if(theHealthScalar < .5f) + { + AvHSUPlayParticleEvent(kpsBuildableLightDamage, this->edict(), this->pev->origin); + this->mTimeOfLastDamageEffect = gpGlobals->time; + theAverageSoundInterval = 5; + } + } + + // If we're at less then 75% health, spark occasionally + if(theHealthScalar < .75f) + { + int theRandomChance = RANDOM_LONG(0, (float)8/theTimeInterval); + if(theRandomChance == 0) + { + UTIL_Sparks(this->pev->origin); + UTIL_Sparks(this->pev->origin); + + const char* theHurtSoundToPlay = kBuildableHurt1Sound; + if(RANDOM_LONG(0, 1) == 1) + { + theHurtSoundToPlay = kBuildableHurt2Sound; + } + + float theVolume = .3f; + EMIT_SOUND(this->edict(), CHAN_AUTO, theHurtSoundToPlay, theVolume, ATTN_NORM); + } + } + + this->mTimeOfLastDamageUpdate = gpGlobals->time; + } +} + + +void AvHBaseBuildable::HealthChanged() +{ + int theMaxHealth = this->mBaseHealth;//this->pev->armorvalue; + int theCurrentHealth = this->pev->health; + + float theNewHealthPercentage = (float)theCurrentHealth/theMaxHealth; + this->pev->fuser2 = theNewHealthPercentage*kNormalizationNetworkFactor; +} + +bool AvHBaseBuildable::GetIsPersistent() const +{ + return this->mPersistent; +} + +void AvHBaseBuildable::SetPersistent() +{ + this->mPersistent = true; +} + diff --git a/main/source/mod/AvHBaseBuildable.h b/main/source/mod/AvHBaseBuildable.h index 2d322e4..abde352 100644 --- a/main/source/mod/AvHBaseBuildable.h +++ b/main/source/mod/AvHBaseBuildable.h @@ -163,7 +163,7 @@ public: virtual void Precache(void); - virtual bool Regenerate(float inRegenerationAmount, bool inPlaySound = true); + virtual bool Regenerate(float inRegenerationAmount, bool inPlaySound = true, bool dcHealing = false); virtual void ResetEntity(); @@ -183,6 +183,7 @@ public: virtual void UpdateOnRecycle(); + virtual Vector EyePosition( ); protected: virtual bool GetHasBeenKilled() const; @@ -253,8 +254,10 @@ private: float mTimeRecycleStarted; float mTimeRecycleDone; - + float mTimeOfLastDCRegeneration; bool mKilled; + + bool mGhost; }; #endif \ No newline at end of file diff --git a/main/source/mod/AvHBasePlayerWeapon.cpp b/main/source/mod/AvHBasePlayerWeapon.cpp index 640ea5b..f92364f 100644 --- a/main/source/mod/AvHBasePlayerWeapon.cpp +++ b/main/source/mod/AvHBasePlayerWeapon.cpp @@ -1,1317 +1,1332 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHBasePlayerWeapon.cpp$ -// $Date: 2002/11/22 21:28:15 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHBasePlayerWeapon.cpp,v $ -// Revision 1.38 2002/11/22 21:28:15 Flayra -// - mp_consistency changes -// -// Revision 1.37 2002/11/03 04:47:23 Flayra -// - Moved weapon expiring into .dll out of .cfg -// -// Revision 1.36 2002/10/24 21:22:10 Flayra -// - Fixes muzzle-flash showing when firing an empty weapon -// -// Revision 1.35 2002/10/17 17:34:14 Flayra -// - Part 2 of persistent weapons fix (found with Grendel) -// -// Revision 1.34 2002/10/16 20:51:17 Flayra -// - Fixed problem where acid projectile hit player -// -// Revision 1.33 2002/10/03 18:39:24 Flayra -// - Added heavy view models -// -// Revision 1.32 2002/09/23 22:10:46 Flayra -// - Weapons now stick around the way they should (forever when dropped by commander, weapon stay time when dropped by player) -// -// Revision 1.31 2002/08/16 02:33:12 Flayra -// - Added damage types -// -// Revision 1.30 2002/07/24 19:11:45 Flayra -// - Linux issues -// -// Revision 1.29 2002/07/24 18:45:40 Flayra -// - Linux and scripting changes -// -// Revision 1.28 2002/07/08 16:47:31 Flayra -// - Reworked bullet firing to add random spread (bug #236), temporarily hacked shotty player animation, removed old adrenaline, don't allow using weapons when invulnerable -// -// Revision 1.27 2002/07/01 21:17:13 Flayra -// - Removed outdated adrenaline concept, made ROF generic for primal scream -// -// Revision 1.26 2002/06/25 17:41:13 Flayra -// - Reworking for correct player animations and new enable/disable state -// -// Revision 1.25 2002/06/10 19:50:37 Flayra -// - First-pass at level 1 animated view model (different anims when running and walking) -// -// Revision 1.24 2002/06/03 16:29:53 Flayra -// - Added resupply (from arsenal), better animation support (for both view model and player model) -// -// Revision 1.23 2002/05/28 17:37:33 Flayra -// - Max entities fix, added animation empty fire -// -// Revision 1.22 2002/05/23 02:34:00 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifdef AVH_CLIENT -#include "cl_dll/wrect.h" -#include "cl_dll/cl_dll.h" -#include "cl_dll/hud.h" -#include "cl_dll/cl_util.h" -#include "cl_dll/ammo.h" -#include "cl_dll/ammohistory.h" -extern int g_runfuncs; -#endif - -#include "util/nowarnings.h" -#include "mod/AvHBasePlayerWeapon.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHPlayer.h" -#include "common/usercmd.h" -#include "pm_shared/pm_defs.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHMarineWeapons.h" - -#ifdef AVH_SERVER -#include "mod/AvHServerUtil.h" -#include "mod/AvHGamerules.h" - -extern int gWelderConstEventID; -#endif - -#ifdef AVH_CLIENT -#include "cl_dll/com_weapons.h" -#endif - -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHSharedUtil.h" -#include "types.h" -#include "common/vector_util.h" -#include "util/MathUtil.h" - -// extern "C" this guy because delcared in pm_shared.c, not pm_shared.cpp -extern playermove_t* pmove; -extern cvar_t weaponstay; - -Vector UTIL_GetRandomSpreadDir(unsigned int inSeed, int inShotNumber, const Vector& inBaseDirection, const Vector& inRight, const Vector& inUp, const Vector& inSpread) -{ - // Use player's random seed. - // get circular gaussian spread - float x = UTIL_SharedRandomFloat( inSeed + inShotNumber, -0.5, 0.5 ) + UTIL_SharedRandomFloat( inSeed + ( 1 + inShotNumber ) , -0.5, 0.5 ); - float y = UTIL_SharedRandomFloat( inSeed + ( 2 + inShotNumber ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( inSeed + ( 3 + inShotNumber ), -0.5, 0.5 ); - float z = x * x + y * y; - - Vector theRandomDir = inBaseDirection + x * inSpread.x * inRight + y * inSpread.y * inUp; - - return theRandomDir; -} - - -AvHBasePlayerWeapon::AvHBasePlayerWeapon() -{ - // reasonable defaults for everything else - this->mEvent = 0; - this->mWeaponAnimationEvent = 0; - this->mStartEvent = 0; - this->mEndEvent = 0; - this->mRange = 8012; - this->mDamage = 10; - this->mAttackButtonDownLastFrame = false; - this->mTimeOfLastResupply = -1; - this->mTimeOfLastPrime = -1; - this->mWeaponPrimeStarted = false; - -#ifdef AVH_SERVER - this->mInOverwatch = false; - this->mIsPersistent = false; - this->mLifetime = -1; -#endif -} -void AvHBasePlayerWeapon::PrintWeaponToClient(CBaseEntity *theAvHPlayer) { - char msg[1024]; - ItemInfo theItemInfo; - this->GetItemInfo(&theItemInfo); - sprintf(msg, "%s iuser3=%d\tenabled = %d\n", theItemInfo.pszName, this->pev->iuser3, this->m_iEnabled); - ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, msg); -} - -int AvHBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer ) -{ - // Can we predict weapon pick-ups? I bet we can. - int theAddedToPlayer = 0; - -#ifdef AVH_SERVER - AvHPlayer* inPlayer = dynamic_cast(pPlayer); - ASSERT(inPlayer != NULL); - - if(this->GetAllowedForUser3(inPlayer->GetUser3())) - { - // Make sure player doesn't have this weapon already - if(!pPlayer->HasItem(this)) - { - // If weapon was placed by mapper - if(this->GetIsPersistent()) - { - // Create a new weapon and give it to the player - pPlayer->GiveNamedItem(STRING(this->pev->classname)); - - this->DestroyItem(); - } - else - { - theAddedToPlayer = CBasePlayerWeapon::AddToPlayer(pPlayer); - if(theAddedToPlayer) - { - // Make sure it's not set for expiration - SetThink(NULL); - } - } - } - } -#endif - - return theAddedToPlayer; -} - -BOOL AvHBasePlayerWeapon::Deploy() -{ - // tankefugl: 0000938 - // removed deploy sounds for all weapons, leaving the sounds to the models - // char* theDeploySound = this->GetDeploySound(); - // if(theDeploySound) - // { - // EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, this->GetDeploySound(), this->GetDeploySoundVolume(), ATTN_NORM); - //} - // :tankefugl - - char* theAnimExt = this->GetAnimationExtension(); - - BOOL theSuccess = DefaultDeploy(this->GetActiveViewModel(), this->GetPlayerModel(), this->GetDeployAnimation(), theAnimExt); - - // Set a player animatiom here? - //this->m_pPlayer->SetAnimation(PLAYER_ANIM(iAnim)); - - // Set deploy time - this->m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + this->GetDeployTime(); - - this->mTimeOfLastPrime = -1; - this->mWeaponPrimeStarted = false; - - return theSuccess; -} - -BOOL AvHBasePlayerWeapon::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal, int body) -{ - if (!CanDeploy( )) - return FALSE; - - m_pPlayer->TabulateAmmo(); - - // This was causing a crash from hl_weapons.cpp only when connected to a dedicated server and switching weapons - //#ifdef AVH_SERVER - //m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); - //#endif - -#ifdef AVH_SERVER - m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); - m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); -#else - gEngfuncs.CL_LoadModel( szViewModel, &m_pPlayer->pev->viewmodel ); - gEngfuncs.CL_LoadModel( szWeaponModel, &m_pPlayer->pev->weaponmodel ); -#endif - - strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); - //SendWeaponAnim( iAnim, skiplocal, body ); - this->SendWeaponAnim(iAnim); - - // Set the player animation as well - //this->m_pPlayer->SetAnimation(PLAYER_ANIM(iAnim)); - - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + this->GetDeployTime(); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + this->GetDeployTime() + kDeployIdleInterval; - - return TRUE; -} - -BOOL AvHBasePlayerWeapon::DefaultReload( int iClipSize, int iAnim, float fDelay, int body) -{ - // tankefugl: 0000996 - if (m_fInReload == TRUE) - return TRUE; - // :tankefugl - - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) - return FALSE; - - // Don't reload while we're resupplying - if(this->mTimeOfLastResupply > 0) - { - return FALSE; - } - - int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); - - if (j == 0) - return FALSE; - - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay; - - //!!UNDONE -- reload sound goes here !!! - //SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 ); - this->SendWeaponAnim(iAnim); - - // Send reload to all players. Reloads are initiated server-side, so send down to local client as well - this->m_pPlayer->pev->weaponanim = iAnim; - this->PlaybackEvent(this->mWeaponAnimationEvent, iAnim, FEV_RELIABLE); - - // Player model reload animation - this->m_pPlayer->SetAnimation(PLAYER_RELOAD); - - m_fInReload = TRUE; - - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + kDeployIdleInterval; - - return TRUE; -} - - - -char* AvHBasePlayerWeapon::GetActiveViewModel() const -{ - return this->GetViewModel(); -} - -//BOOL AvHBasePlayerWeapon::Deploy(char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, float inNextAttackTime, int skiplocal) -//{ -// BOOL theSuccess = FALSE; -// -// if(CanDeploy()) -// { -// this->m_pPlayer->TabulateAmmo(); -// this->m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); -// this->m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); -// strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); -// SendWeaponAnim( iAnim, skiplocal ); -// -// this->m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + inNextAttackTime; -// this->m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0; -// -// theSuccess = TRUE; -// } -// -// return theSuccess; -//} - -char* AvHBasePlayerWeapon::GetAnimationExtension() const -{ - char* theAnimExt = NULL; - - AvHWeaponID theWeaponID = (AvHWeaponID)this->m_iId; - switch(theWeaponID) - { - case AVH_WEAPON_BITE: - case AVH_WEAPON_SPIT: - case AVH_WEAPON_SPIKE: - case AVH_WEAPON_BITE2: - case AVH_WEAPON_SWIPE: - case AVH_WEAPON_CLAWS: - theAnimExt = "ability1"; - break; - - case AVH_WEAPON_PARASITE: - case AVH_WEAPON_HEALINGSPRAY: - case AVH_WEAPON_SPORES: - case AVH_WEAPON_BLINK: - case AVH_WEAPON_STOMP: - theAnimExt = "ability2"; - break; - - case AVH_ABILITY_LEAP: - case AVH_WEAPON_BILEBOMB: - case AVH_WEAPON_UMBRA: - case AVH_WEAPON_METABOLIZE: - case AVH_WEAPON_DEVOUR: - theAnimExt = "ability3"; - break; - - case AVH_WEAPON_DIVINEWIND: - case AVH_WEAPON_WEBSPINNER: - case AVH_WEAPON_PRIMALSCREAM: - case AVH_WEAPON_ACIDROCKET: - case AVH_ABILITY_CHARGE: - theAnimExt = "ability4"; - break; - - case AVH_WEAPON_KNIFE: - theAnimExt = "knife"; - break; - case AVH_WEAPON_PISTOL: - theAnimExt = "pistol"; - break; - case AVH_WEAPON_MG: - theAnimExt = "lmg"; - break; - case AVH_WEAPON_SONIC: - theAnimExt = "shotgun"; - break; - case AVH_WEAPON_HMG: - theAnimExt = "hmg"; - break; - case AVH_WEAPON_WELDER: - theAnimExt = "welder"; - break; - case AVH_WEAPON_MINE: - theAnimExt = "tripmine"; - break; - case AVH_WEAPON_GRENADE_GUN: - theAnimExt = "grenadegun"; - break; - case AVH_WEAPON_GRENADE: - theAnimExt = "handgrenade"; - break; - default: - ASSERT(false); - break; - } - return theAnimExt; -} - -int AvHBasePlayerWeapon::GetClipSize() const -{ - ItemInfo theItemInfo; - this->GetItemInfo(&theItemInfo); - int theClipSize = theItemInfo.iMaxClip; - - return theClipSize; -} - -int AvHBasePlayerWeapon::GetPrimaryAmmoAmount() const -{ - int theAmmo = 0; - - if(this->m_pPlayer && (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)) - { - theAmmo = this->m_pPlayer->m_rgAmmo[this->m_iPrimaryAmmoType]; - } - - return theAmmo; -} - -int AvHBasePlayerWeapon::GetDamageType() const -{ - return NS_DMG_NORMAL; -} - -char* AvHBasePlayerWeapon::GetDeploySound() const -{ - return NULL; -} - -float AvHBasePlayerWeapon::GetDeploySoundVolume() const -{ - return 1.0f; -} - -int AvHBasePlayerWeapon::GetEmptyShootAnimation() const -{ - return kShootEmptyAnimation; -} - -void AvHBasePlayerWeapon::GetEventOrigin(Vector& outOrigin) const -{ - VectorCopy(this->m_pPlayer->pev->origin, outOrigin); -} - -void AvHBasePlayerWeapon::GetEventAngles(Vector& outAngles) const -{ - outAngles = this->pev->v_angle + this->pev->punchangle; -} - -bool AvHBasePlayerWeapon::GetFiresUnderwater() const -{ - return false; -} - -bool AvHBasePlayerWeapon::GetIsFiring() const -{ - return this->mAttackButtonDownLastFrame; -} - -bool AvHBasePlayerWeapon::GetHasMuzzleFlash() const -{ - return false; -} - -bool AvHBasePlayerWeapon::GetIsCapableOfFiring() const -{ - return !this->UsesAmmo() || (this->m_iClip > 0); -} - - -int AvHBasePlayerWeapon::GetDeployAnimation() const -{ - return kDeployAnimation; -} - -int AvHBasePlayerWeapon::GetIdleAnimation() const -{ - int iAnim; - int theRandomNum = UTIL_SharedRandomLong(this->m_pPlayer->random_seed, 0, 1); - - switch(theRandomNum) - { - case 0: - iAnim = kIdleAnimationOne; - break; - - default: - case 1: - iAnim = kIdleAnimationTwo; - break; - } - - return iAnim; -} - -Vector AvHBasePlayerWeapon::GetProjectileSpread() const -{ - return VECTOR_CONE_0DEGREES; -} - -float AvHBasePlayerWeapon::ComputeAttackInterval() const -{ - return this->GetRateOfFire(); -} - -float AvHBasePlayerWeapon::GetRateOfFire() const -{ - return 1; -} - -int AvHBasePlayerWeapon::GetShootAnimation() const -{ - return kShootAnimation; -} - -int AvHBasePlayerWeapon::GetShotsInClip() const -{ - int theShotsInClip = -1; - - if(this->UsesAmmo()) - { - theShotsInClip = this->m_iClip; - } - - return theShotsInClip; -} - -bool AvHBasePlayerWeapon::GetIsDroppable() const -{ - return true; -} - -bool AvHBasePlayerWeapon::GetIsPlayerMoving() const -{ - bool thePlayerIsMoving = false; - -// float kMovingThresholdVelocity = 100; -// float theCurrentVelocity = 0; -// -// #ifdef AVH_SERVER -// theCurrentVelocity = this->m_pPlayer->pev->velocity.Length(); -// #endif -// -// #ifdef AVH_CLIENT -// cl_entity_t* theLocalPlayer = gEngfuncs.GetLocalPlayer(); -// if(theLocalPlayer) -// { -// theCurrentVelocity = theLocalPlayer->curstate.velocity.Length(); -// } -// #endif -// -// if(theCurrentVelocity > kMovingThresholdVelocity) -// { -// thePlayerIsMoving = true; -// } - - return thePlayerIsMoving; -} - -int AvHBasePlayerWeapon::GetMaxClipsCouldReceive() -{ - int theMaxClips = 0; - - ItemInfo theItemInfo; - this->GetItemInfo(&theItemInfo); - int theClipSize = theItemInfo.iMaxClip; - - if(theClipSize > 0 && this->UsesAmmo()) - { - theMaxClips = ((theItemInfo.iMaxAmmo1 - this->m_iClip)/theClipSize) + 1; - } - - return theMaxClips; -} - -AvHWeaponID AvHBasePlayerWeapon::GetPreviousWeaponID() const -{ - AvHWeaponID theID = AVH_WEAPON_NONE; - - // Look at most recently used weapon and see if we can transition from it - AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pPlayer->m_pLastItem); - if(theWeapon) - { - theID = (AvHWeaponID)(theWeapon->m_iId); - } - - return theID; -} - - -float AvHBasePlayerWeapon::GetRange() const -{ - return this->mRange; -} - -vec3_t AvHBasePlayerWeapon::GetWorldBarrelPoint() const -{ - ASSERT(this->m_pPlayer); - - Vector vecAiming = gpGlobals->v_forward; - Vector theWorldBarrelPoint = this->m_pPlayer->GetGunPosition() + vecAiming*this->GetBarrelLength(); - - return theWorldBarrelPoint; - -} - -char* AvHBasePlayerWeapon::GetPlayerModel() const -{ - return kNullModel; -} - -char* AvHBasePlayerWeapon::GetPrimeSound() const -{ - return NULL; -} - -float AvHBasePlayerWeapon::GetPrimeSoundVolume() const -{ - return 1.0f; -} - -char* AvHBasePlayerWeapon::GetViewModel() const -{ - return kNullModel; -} - -char* AvHBasePlayerWeapon::GetWorldModel() const -{ - return kNullModel; -} - -void AvHBasePlayerWeapon::Holster( int skiplocal) -{ - CBasePlayerWeapon::Holster(skiplocal); - - #ifdef AVH_SERVER - this->mInOverwatch = false; - #endif -} - -float AvHBasePlayerWeapon::GetTimePassedThisTick() const -{ - float theClientTimePassedThisTick = (pmove->cmd.msec/1000.0f); - return theClientTimePassedThisTick; -} - -void AvHBasePlayerWeapon::ItemPostFrame( void ) -{ - CBasePlayerWeapon::ItemPostFrame(); - - float theClientTimePassedThisTick = this->GetTimePassedThisTick(); - - this->m_flNextPrimaryAttack -= theClientTimePassedThisTick; - this->m_flTimeWeaponIdle -= theClientTimePassedThisTick; - this->mTimeOfLastResupply -= theClientTimePassedThisTick; - this->mTimeOfLastPrime -= theClientTimePassedThisTick; -} - -void AvHBasePlayerWeapon::Precache(void) -{ - CBasePlayerWeapon::Precache(); - - char* theDeploySound = this->GetDeploySound(); - if(theDeploySound) - { - PRECACHE_UNMODIFIED_SOUND(theDeploySound); - } - char* thePrimeSound = this->GetPrimeSound(); - if(thePrimeSound) - { - PRECACHE_UNMODIFIED_SOUND(thePrimeSound); - } - char* thePlayerModel = this->GetPlayerModel(); - if(thePlayerModel) - { - PRECACHE_UNMODIFIED_MODEL(thePlayerModel); - } - char* theViewModel = this->GetViewModel(); - if(theViewModel) - { - PRECACHE_UNMODIFIED_MODEL(theViewModel); - } - char* theWorldModel = this->GetWorldModel(); - if(theWorldModel) - { - PRECACHE_UNMODIFIED_MODEL(theWorldModel); - } - - this->mWeaponAnimationEvent = PRECACHE_EVENT(1, kWeaponAnimationEvent); -} - -bool AvHBasePlayerWeapon::ProcessValidAttack(void) -{ - bool theAttackIsValid = false; - -//#ifdef AVH_CLIENT -// char sz[ 128 ]; -// sprintf(sz, "during check valid, clip is %d\n", this->m_iClip); -// gEngfuncs.pfnCenterPrint( sz ); -// -// //gEngfuncs.Con_Printf("during idle, clip is %d\n", sz); -//#endif - -//#ifdef AVH_CLIENT -// if(gHUD.GetInTopDownMode()) -// { -// return false; -// } -//#endif -// -//#ifdef AVH_SERVER -// AvHPlayer* thePlayer = dynamic_cast(this->m_pPlayer); -// if(thePlayer->GetIsInTopDownMode()) -// { -// return false; -// } -//#endif - - // Only shoot if deployed and enabled (iuser3 is 0 when disabled, 1 when enabled ) - - // puzl: 497 call GetEnabledState instead of testing directly - int enabledState=this->GetEnabledState(); - - if(this->m_pPlayer->pev->viewmodel && ( enabledState == 1)) - { - // don't fire underwater - if((this->m_pPlayer->pev->waterlevel == 3) && !this->GetFiresUnderwater()) - { - this->PlayEmptySound(); - - //this->m_flNextPrimaryAttack = gpGlobals->time + this->mROF; - this->m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + this->ComputeAttackInterval(); - } - else if(this->UsesAmmo()) - { - ASSERT(this->m_iPrimaryAmmoType >= 0); - if(this->m_iClip > 0) - { - if(this->m_flNextPrimaryAttack <= 0) - { - if(!this->GetMustPressTriggerForEachShot() || (!this->mAttackButtonDownLastFrame)) - { - theAttackIsValid = true; - } - } - } - else - { - // Definitely not firing this pull of the trigger - theAttackIsValid = false; - - BOOL theHasAmmo = 0; - - if ( pszAmmo1() ) - { - theHasAmmo |= (this->m_pPlayer->m_rgAmmo[this->m_iPrimaryAmmoType] != 0); - } - if ( pszAmmo2() ) - { - theHasAmmo |= (this->m_pPlayer->m_rgAmmo[this->m_iSecondaryAmmoType] != 0); - } - if (this->m_iClip > 0) - { - theHasAmmo |= 1; - } - - if(theHasAmmo) - { - // Trigger reload - this->Reload(); - } - else - { - this->PlayEmptySound(); - - this->SendWeaponAnim(this->GetEmptyShootAnimation()); - - //this->m_pPlayer->SetAnimation(PLAYER_ATTACK1); - } - } - } - else - { - theAttackIsValid = true; - } - } - - return theAttackIsValid; - -} - -bool AvHBasePlayerWeapon::GetIsGunPositionValid() const -{ - return true; -} - -void AvHBasePlayerWeapon::DeductCostForShot(void) -{ - this->m_iClip--; - - // On a successful attack, decloak the player if needed - #ifdef AVH_SERVER - AvHPlayer* thePlayer = dynamic_cast(this->m_pPlayer); - if(thePlayer) - { - thePlayer->TriggerUncloak(); - } - - int theResourceCost = this->GetResourceCost(); - if(theResourceCost > 0) - { - float theNewResources = (thePlayer->GetResources(false) - theResourceCost); - theNewResources = max(theNewResources, 0.0f); - thePlayer->SetResources(theNewResources); - } - - #endif -} - -float AvHBasePlayerWeapon::GetReloadTime(void) const -{ - // TODO: Allow weapons to have different reload times - return 1.5f; -} - -int AvHBasePlayerWeapon::GetReloadAnimation() const -{ - return kReloadAnimation; -} - -void AvHBasePlayerWeapon::PlaybackEvent(unsigned short inEvent, int inIparam2, int inFlags) const -{ - unsigned short theEvent = inEvent; - if(theEvent != 0) - { - // Playback event, sending along enough data so client knows who triggered this event! - int theWeaponIndex = 0; - AvHUser3 theUser3 = AVH_USER3_NONE; - int theUpgrades = 0; - - // When predicting weapons, play the event locally, then tell everyone else but us to play it back later - int flags = inFlags; - edict_t* theEdict; - - // Pass player random seed to event, so it chooses the right direction for spread - int theRandomNumber = this->m_pPlayer->random_seed; - -#if defined( AVH_CLIENT ) - theUser3 = gHUD.GetHUDUser3(); - theUpgrades = gHUD.GetHUDUpgrades(); - theEdict = NULL; -#else - theUser3 = dynamic_cast(this->m_pPlayer)->GetUser3(); - theUpgrades = this->m_pPlayer->pev->iuser4; - theEdict = this->m_pPlayer->edict(); -#endif - - // For bullet spread - //theRandomNumber = UTIL_SharedRandomLong(this->m_pPlayer->random_seed, 1, kBulletSpreadGranularity*kBulletSpreadGranularity); - - // When in overwatch, the weapon is fired on the server, so the client firing the weapon won't be firing it locally first -#if defined(AVH_SERVER) - if(this->mInOverwatch) - { - flags = 0; - } -#endif - - // Allow weapon to specify some parameters, so they are available on both client and server - Vector theEventOrigin; - this->GetEventOrigin(theEventOrigin); - - Vector theEventAngles; - this->GetEventAngles(theEventAngles); - - float theVolume = AvHPlayerUpgrade::GetSilenceVolumeLevel(theUser3, theUpgrades); - - //( int flags, const edict_t *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); - PLAYBACK_EVENT_FULL(flags, this->m_pPlayer->edict(), theEvent, 0, (float *)&theEventOrigin, (float *)&theEventAngles, theVolume, 0.0, theRandomNumber, inIparam2, 0, 0 ); - } -} - -void AvHBasePlayerWeapon::SetAnimationAndSound(void) -{ - this->m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; - this->m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; - - // player "shoot" animation - //SendWeaponAnim(this->GetShootAnimation()); - - this->m_pPlayer->SetAnimation(PLAYER_ATTACK1); - - if(this->GetHasMuzzleFlash()) - { - this->m_pPlayer->pev->effects = (int)(this->m_pPlayer->pev->effects) | EF_MUZZLEFLASH; - } -} - -void AvHBasePlayerWeapon::FireProjectiles(void) -{ - Vector vecSrc = this->m_pPlayer->GetGunPosition(); - Vector vecAiming = this->m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); - - // Fire the bullets and apply damage - int theRange = this->mRange; - float theDamage = this->mDamage; - - int theTracerFreq; - float theDamageMultiplier; - AvHPlayerUpgrade::GetWeaponUpgrade(this->m_pPlayer->pev->iuser3, this->m_pPlayer->pev->iuser4, &theDamageMultiplier, &theTracerFreq); - theDamage *= theDamageMultiplier; - - Vector theSpread = this->GetProjectileSpread(); - this->m_pPlayer->FireBulletsPlayer(1, vecSrc, vecAiming, theSpread, theRange, BULLET_PLAYER_MP5, theTracerFreq, theDamage, this->m_pPlayer->pev, this->m_pPlayer->random_seed); - - this->mWeaponPrimeStarted = false; -} - -int AvHBasePlayerWeapon::GetPrimeAnimation() const -{ - return -1; -} - -float AvHBasePlayerWeapon::GetWeaponPrimeTime() const -{ - return -1.0f; -} - -void AvHBasePlayerWeapon::PrimeWeapon() -{ - float thePrimeTime = this->GetWeaponPrimeTime(); - if(thePrimeTime > 0.0f) - { - if(!this->GetIsWeaponPriming()) - { - char* thePrimeSound = this->GetPrimeSound(); - if(thePrimeSound) - { - EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, thePrimeSound, this->GetPrimeSoundVolume(), ATTN_NORM); - } - - this->PlaybackEvent(this->mWeaponAnimationEvent, this->GetPrimeAnimation()); - - this->mTimeOfLastPrime = UTIL_WeaponTimeBase() + thePrimeTime; - - this->mWeaponPrimeStarted = true; - } - } -} - -BOOL AvHBasePlayerWeapon::GetIsWeaponPrimed() const -{ - return ((this->GetWeaponPrimeTime() > 0.0f) && this->mWeaponPrimeStarted && (UTIL_WeaponTimeBase() > this->mTimeOfLastPrime)); -} - -BOOL AvHBasePlayerWeapon::GetIsWeaponPriming() const -{ - return ((this->GetWeaponPrimeTime() > 0.0f) && this->mWeaponPrimeStarted && (UTIL_WeaponTimeBase() < this->mTimeOfLastPrime)); -} - -void AvHBasePlayerWeapon::SetNextAttack(void) -{ -// this->m_flNextPrimaryAttack += this->mROF; -// -// if(this->m_flNextPrimaryAttack < 0) -// this->m_flNextPrimaryAttack = this->mROF; - - float theRateOfFire = this->ComputeAttackInterval(); - - this->m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + theRateOfFire; - - if(this->m_flNextPrimaryAttack < 0) - { - this->m_flNextPrimaryAttack = theRateOfFire; - } - - this->SetNextIdle(); -} - - -void AvHBasePlayerWeapon::PrimaryAttack(void) -{ - if (this->ProcessValidAttack()) - { - - if (!this->mAttackButtonDownLastFrame) - { - this->PlaybackEvent(this->mStartEvent); - this->mAttackButtonDownLastFrame = true; - } - - this->PlaybackEvent(this->mEvent, this->GetShootAnimation()); - this->SetAnimationAndSound(); - - // If player is too close to a wall, don't actually fire the projectile - if(this->GetIsGunPositionValid()) - { - this->FireProjectiles(); - } - else - { - #ifdef DEBUG - #ifdef AVH_SERVER - char theMessage[256]; - sprintf(theMessage, "Gun position is not valid, skipping call to FireProjectiles.\n"); - ALERT(at_console, theMessage); - #endif - #endif - } - - this->DeductCostForShot(); - this->SetNextAttack(); - - } -} - -void AvHBasePlayerWeapon::Reload(void) -{ - // Move clip sounds out - int theReloadAnimation = this->GetReloadAnimation(); - float theReloadTime = this->GetReloadTime(); - int theClipSize = this->GetClipSize(); - - this->DefaultReload(theClipSize, theReloadAnimation, theReloadTime); - - // Don't idle for a bit - this->SetNextIdle(); -} - -bool AvHBasePlayerWeapon::GetCanBeResupplied() const -{ - bool theCanBeResupplied = false; - - if(this->UsesAmmo()) - { - ItemInfo theItemInfo; - this->GetItemInfo(&theItemInfo); - - int theCurrentPrimary = this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]; - int theMaxPrimary = theItemInfo.iMaxAmmo1; - - // Add some to primary store - if(theCurrentPrimary < theMaxPrimary) - { - theCanBeResupplied = true; - } - } - - return theCanBeResupplied; -} - -bool AvHBasePlayerWeapon::Resupply() -{ - bool theResupplied = false; - - if(this->GetCanBeResupplied()) - { - ItemInfo theItemInfo; - this->GetItemInfo(&theItemInfo); - - // Get amount to add - int theMaxClip = theItemInfo.iMaxClip; - - // Add half the clip each time, rounding up (roughly 3 seconds per clip () - int theAmountToAdd = max((theMaxClip+1)/2, 1); - - int theCurrentPrimary = this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]; - int theMaxPrimary = theItemInfo.iMaxAmmo1; - - // Add ammo - this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = min(theCurrentPrimary + theAmountToAdd, theMaxPrimary); - - const float theDelay = 1.0f; - //bugfix - don't let resupply shorten reload time - this->m_pPlayer->m_flNextAttack = max(this->m_pPlayer->m_flNextAttack,UTIL_WeaponTimeBase() + theDelay); - this->mTimeOfLastResupply = UTIL_WeaponTimeBase() + theDelay; - } - - return theResupplied; -} - -void AvHBasePlayerWeapon::SendWeaponAnim(int inAnimation, int skiplocal, int body) -{ - if(inAnimation >= 0) - { - this->m_pPlayer->pev->weaponanim = inAnimation; - - this->PlaybackEvent(this->mWeaponAnimationEvent, inAnimation); - } -} - -void AvHBasePlayerWeapon::SecondaryAttack(void) -{ -} - -void AvHBasePlayerWeapon::Spawn() -{ - CBasePlayerWeapon::Spawn(); - - this->pev->solid = SOLID_BBOX; - this->pev->movetype = MOVETYPE_TOSS; - UTIL_SetOrigin(this->pev, this->pev->origin); - UTIL_SetSize(this->pev, kMarineItemMinSize, kMarineItemMaxSize); - - this->pev->iuser3 = AVH_USER3_MARINEITEM; - - #ifdef AVH_SERVER - if(!this->GetIsPersistent()) - { - this->mLifetime = AvHSUGetWeaponStayTime(); - } - #endif -} - -bool AvHBasePlayerWeapon::GetEnabledState() const -{ - bool result=false; -#ifdef AVH_SERVER - result= (this->m_iEnabled == 1); -#else - // puzl: 497 client now uses the enabled state in the appropriate WEAPON - ItemInfo theItemInfo; - this->GetItemInfo(&theItemInfo); - WEAPON *pWeapon = gWR.GetWeapon( theItemInfo.iId ); - if ( pWeapon != 0 ) { - result=(pWeapon->iEnabled == 1); - } -#endif - return result; -} - -#ifdef AVH_SERVER -void AvHBasePlayerWeapon::VirtualMaterialize(void) -{ - int theLifetime = this->GetGroundLifetime(); - - if(theLifetime >= 0) - { - SetThink(&AvHBasePlayerWeapon::DestroyItem); - this->pev->nextthink = gpGlobals->time + theLifetime; - } -} - -int AvHBasePlayerWeapon::GetGroundLifetime() const -{ - return this->mLifetime; -} - -// -1 means never expire -void AvHBasePlayerWeapon::SetGroundLifetime(int inGroundLifetime) -{ - this->mLifetime = inGroundLifetime; -} - -int AvHBasePlayerWeapon::GetResourceCost() const -{ - return 0; -} - -void AvHBasePlayerWeapon::VirtualDestroyItem(void) -{ - if(this->GetIsPersistent()) - { - // Make this weapon invisible until map reset - this->pev->effects |= EF_NODRAW; - this->pev->solid = SOLID_NOT; - this->pev->takedamage = DAMAGE_NO; - SetThink(NULL); - } - else - { - CBasePlayerItem::VirtualDestroyItem(); - } -} - -void AvHBasePlayerWeapon::ResetEntity() -{ - CBasePlayerWeapon::ResetEntity(); - - this->pev->effects = 0; - this->pev->solid = SOLID_BBOX; - this->pev->movetype = MOVETYPE_TOSS; - - this->pev->takedamage = DAMAGE_YES; - - this->VirtualMaterialize(); -} - -void AvHBasePlayerWeapon::SetOverwatchState(bool inState) -{ - this->mInOverwatch = inState; -} - -void AvHBasePlayerWeapon::UpdateInventoryEnabledState(int inNumActiveHives) -{ - // Process here - int theEnabledState = 1; - - ItemInfo theItemInfo; - if(this->GetItemInfo(&theItemInfo) != 0) - { - int theWeaponFlags = theItemInfo.iFlags; - AvHPlayer* thePlayer = dynamic_cast(this->m_pPlayer); - ASSERT(thePlayer); - - // If we don't have the hives required, or we're ensnared - if (/*thePlayer->GetIsTemporarilyInvulnerable() ||*/ - !thePlayer->GetIsAbleToAct() || - ((inNumActiveHives < 1) && (theWeaponFlags & ONE_HIVE_REQUIRED)) || - ((inNumActiveHives < 2) && (theWeaponFlags & TWO_HIVES_REQUIRED)) || - ((inNumActiveHives < 3) && (theWeaponFlags & THREE_HIVES_REQUIRED)) || - (this->GetResourceCost() > thePlayer->GetResources(false)) ) - { - // Disable it - theEnabledState = 0; - } - } - - // puzl: 497 save the state for when we send the CurWeapon message - this->m_iEnabled = theEnabledState; -} - -void AvHBasePlayerWeapon::KeyValue(KeyValueData* pkvd) -{ - // Any entity placed by the mapper is persistent - this->SetPersistent(); - - if(FStrEq(pkvd->szKeyName, "teamchoice")) - { - this->pev->team = (AvHTeamNumber)(atoi(pkvd->szValue)); - pkvd->fHandled = TRUE; - } - else if(FStrEq(pkvd->szKeyName, "angles")) - { - // TODO: Insert code here - //pkvd->fHandled = TRUE; - } - else if(FStrEq(pkvd->szKeyName, "lifetime")) - { - this->mLifetime = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBasePlayerWeapon::KeyValue(pkvd); - } -} - -// 0 means never expire, -1 means no value was set so use default -int AvHBasePlayerWeapon::GetLifetime() const -{ - int theLifetime = this->mLifetime; - - if(theLifetime < 0) - { - theLifetime = AvHSUGetWeaponStayTime(); - } - - return theLifetime; -} - -bool AvHBasePlayerWeapon::GetIsPersistent() const -{ - return this->mIsPersistent; -} - -void AvHBasePlayerWeapon::SetPersistent() -{ - this->mIsPersistent = true; -} -#endif - -void AvHBasePlayerWeapon::SetNextIdle(void) -{ - float theRandomIdle = UTIL_SharedRandomFloat(this->m_pPlayer->random_seed, 10, 15); - - this->m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + theRandomIdle; - - // Just added these next two lines 8/8/01 (trying to fix prediction wackiness) - if(this->m_flTimeWeaponIdle < 0) - { - this->m_flTimeWeaponIdle = theRandomIdle; - } -} - -BOOL AvHBasePlayerWeapon::UseDecrement( void ) -{ - // If we're predicting, return true - return TRUE; -} - - -void AvHBasePlayerWeapon::WeaponIdle(void) -{ -//#ifdef AVH_CLIENT -// char sz[ 128 ]; -// sprintf(sz, "during idle, clip is %d\n", this->m_iClip); -// gEngfuncs.pfnCenterPrint( sz ); -// -// //gEngfuncs.Con_Printf("during idle, clip is %d\n", sz); -//#endif - - if(this->mAttackButtonDownLastFrame) - { - this->PlaybackEvent(this->mEndEvent); - this->mAttackButtonDownLastFrame = false; - } - - ResetEmptySound(); - - this->m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); - - if(this->m_flTimeWeaponIdle <= UTIL_WeaponTimeBase()) - { - this->SendWeaponAnim(this->GetIdleAnimation()); - - this->m_pPlayer->SetAnimation(PLAYER_IDLE); - - this->SetNextIdle(); - } -} - -bool AvHBasePlayerWeapon::UsesAmmo(void) const -{ - return true; -} +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHBasePlayerWeapon.cpp$ +// $Date: 2002/11/22 21:28:15 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHBasePlayerWeapon.cpp,v $ +// Revision 1.38 2002/11/22 21:28:15 Flayra +// - mp_consistency changes +// +// Revision 1.37 2002/11/03 04:47:23 Flayra +// - Moved weapon expiring into .dll out of .cfg +// +// Revision 1.36 2002/10/24 21:22:10 Flayra +// - Fixes muzzle-flash showing when firing an empty weapon +// +// Revision 1.35 2002/10/17 17:34:14 Flayra +// - Part 2 of persistent weapons fix (found with Grendel) +// +// Revision 1.34 2002/10/16 20:51:17 Flayra +// - Fixed problem where acid projectile hit player +// +// Revision 1.33 2002/10/03 18:39:24 Flayra +// - Added heavy view models +// +// Revision 1.32 2002/09/23 22:10:46 Flayra +// - Weapons now stick around the way they should (forever when dropped by commander, weapon stay time when dropped by player) +// +// Revision 1.31 2002/08/16 02:33:12 Flayra +// - Added damage types +// +// Revision 1.30 2002/07/24 19:11:45 Flayra +// - Linux issues +// +// Revision 1.29 2002/07/24 18:45:40 Flayra +// - Linux and scripting changes +// +// Revision 1.28 2002/07/08 16:47:31 Flayra +// - Reworked bullet firing to add random spread (bug #236), temporarily hacked shotty player animation, removed old adrenaline, don't allow using weapons when invulnerable +// +// Revision 1.27 2002/07/01 21:17:13 Flayra +// - Removed outdated adrenaline concept, made ROF generic for primal scream +// +// Revision 1.26 2002/06/25 17:41:13 Flayra +// - Reworking for correct player animations and new enable/disable state +// +// Revision 1.25 2002/06/10 19:50:37 Flayra +// - First-pass at level 1 animated view model (different anims when running and walking) +// +// Revision 1.24 2002/06/03 16:29:53 Flayra +// - Added resupply (from arsenal), better animation support (for both view model and player model) +// +// Revision 1.23 2002/05/28 17:37:33 Flayra +// - Max entities fix, added animation empty fire +// +// Revision 1.22 2002/05/23 02:34:00 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifdef AVH_CLIENT +#include "cl_dll/wrect.h" +#include "cl_dll/cl_dll.h" +#include "cl_dll/hud.h" +#include "cl_dll/cl_util.h" +#include "cl_dll/ammo.h" +#include "cl_dll/ammohistory.h" +extern int g_runfuncs; +#endif + +#include "util/nowarnings.h" +#include "mod/AvHBasePlayerWeapon.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHPlayer.h" +#include "common/usercmd.h" +#include "pm_shared/pm_defs.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHMarineWeapons.h" + +#ifdef AVH_SERVER +#include "mod/AvHServerUtil.h" +#include "mod/AvHGamerules.h" + +extern int gWelderConstEventID; +#endif + +#ifdef AVH_CLIENT +#include "cl_dll/com_weapons.h" +#endif + +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHSharedUtil.h" +#include "types.h" +#include "common/vector_util.h" +#include "util/MathUtil.h" + +// extern "C" this guy because delcared in pm_shared.c, not pm_shared.cpp +extern playermove_t* pmove; +extern cvar_t weaponstay; + +Vector UTIL_GetRandomSpreadDir(unsigned int inSeed, int inShotNumber, const Vector& inBaseDirection, const Vector& inRight, const Vector& inUp, const Vector& inSpread) +{ + // Use player's random seed. + // get circular gaussian spread + float x = UTIL_SharedRandomFloat( inSeed + inShotNumber, -0.5, 0.5 ) + UTIL_SharedRandomFloat( inSeed + ( 1 + inShotNumber ) , -0.5, 0.5 ); + float y = UTIL_SharedRandomFloat( inSeed + ( 2 + inShotNumber ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( inSeed + ( 3 + inShotNumber ), -0.5, 0.5 ); + float z = x * x + y * y; + + Vector theRandomDir = inBaseDirection + x * inSpread.x * inRight + y * inSpread.y * inUp; + + return theRandomDir; +} + +// test +Vector UTIL_GetRandomSpreadDirFrom(unsigned int inSeed, int inShotNumber, const Vector& inBaseDirection, const Vector& inRight, const Vector& inUp, const Vector& inSpread, const Vector& inFromSpread) +{ + // Use player's random seed. + // get circular gaussian spread + float x = UTIL_SharedRandomFloat( inSeed + inShotNumber, -0.5, 0.5 ) + UTIL_SharedRandomFloat( inSeed + ( 1 + inShotNumber ) , -0.5, 0.5 ); + float y = UTIL_SharedRandomFloat( inSeed + ( 2 + inShotNumber ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( inSeed + ( 3 + inShotNumber ), -0.5, 0.5 ); + float z = x * x + y * y; + float xdir = x / fabs(x); + float ydir = y / fabs(y); + + Vector theRandomDir = inBaseDirection + inFromSpread.x * inRight * xdir + x * inSpread.x * inRight + inFromSpread.y * inUp * ydir + y * inSpread.y * inUp; + + return theRandomDir; +} + +AvHBasePlayerWeapon::AvHBasePlayerWeapon() +{ + // reasonable defaults for everything else + this->mEvent = 0; + this->mWeaponAnimationEvent = 0; + this->mStartEvent = 0; + this->mEndEvent = 0; + this->mRange = 8012; + this->mDamage = 10; + this->mAttackButtonDownLastFrame = false; + this->mTimeOfLastResupply = -1; + this->mTimeOfLastPrime = -1; + this->mWeaponPrimeStarted = false; + +#ifdef AVH_SERVER + this->mInOverwatch = false; + this->mIsPersistent = false; + this->mLifetime = -1; +#endif +} +void AvHBasePlayerWeapon::PrintWeaponToClient(CBaseEntity *theAvHPlayer) { + char msg[1024]; + ItemInfo theItemInfo; + this->GetItemInfo(&theItemInfo); + sprintf(msg, "%s iuser3=%d\tenabled = %d\n", theItemInfo.pszName, this->pev->iuser3, this->m_iEnabled); + ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, msg); +} + +int AvHBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer ) +{ + // Can we predict weapon pick-ups? I bet we can. + int theAddedToPlayer = 0; + +#ifdef AVH_SERVER + AvHPlayer* inPlayer = dynamic_cast(pPlayer); + ASSERT(inPlayer != NULL); + + if(this->GetAllowedForUser3(inPlayer->GetUser3())) + { + // Make sure player doesn't have this weapon already + if(!pPlayer->HasItem(this)) + { + // If weapon was placed by mapper + if(this->GetIsPersistent()) + { + // Create a new weapon and give it to the player + pPlayer->GiveNamedItem(STRING(this->pev->classname)); + + this->DestroyItem(); + } + else + { + theAddedToPlayer = CBasePlayerWeapon::AddToPlayer(pPlayer); + if(theAddedToPlayer) + { + // Make sure it's not set for expiration + SetThink(NULL); + } + } + } + } +#endif + + return theAddedToPlayer; +} + +BOOL AvHBasePlayerWeapon::Deploy() +{ + // : 0000938 + // removed deploy sounds for all weapons, leaving the sounds to the models + // char* theDeploySound = this->GetDeploySound(); + // if(theDeploySound) + // { + // EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, this->GetDeploySound(), this->GetDeploySoundVolume(), ATTN_NORM); + //} + // : + + char* theAnimExt = this->GetAnimationExtension(); + + BOOL theSuccess = DefaultDeploy(this->GetActiveViewModel(), this->GetPlayerModel(), this->GetDeployAnimation(), theAnimExt); + + // Set a player animatiom here? + //this->m_pPlayer->SetAnimation(PLAYER_ANIM(iAnim)); + + // Set deploy time + this->m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + this->GetDeployTime(); + + this->mTimeOfLastPrime = -1; + this->mWeaponPrimeStarted = false; + + return theSuccess; +} + +BOOL AvHBasePlayerWeapon::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal, int body) +{ + if (!CanDeploy( )) + return FALSE; + + m_pPlayer->TabulateAmmo(); + + // This was causing a crash from hl_weapons.cpp only when connected to a dedicated server and switching weapons + //#ifdef AVH_SERVER + //m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); + //#endif + +#ifdef AVH_SERVER + m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); + m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); +#else + gEngfuncs.CL_LoadModel( szViewModel, &m_pPlayer->pev->viewmodel ); + gEngfuncs.CL_LoadModel( szWeaponModel, &m_pPlayer->pev->weaponmodel ); +#endif + + strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); + //SendWeaponAnim( iAnim, skiplocal, body ); + this->SendWeaponAnim(iAnim); + + // Set the player animation as well + //this->m_pPlayer->SetAnimation(PLAYER_ANIM(iAnim)); + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + this->GetDeployTime(); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + this->GetDeployTime() + kDeployIdleInterval; + + return TRUE; +} + +BOOL AvHBasePlayerWeapon::DefaultReload( int iClipSize, int iAnim, float fDelay, int body) +{ + // : 0000996 + if (m_fInReload == TRUE) + return TRUE; + // : + + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + return FALSE; + + // Don't reload while we're resupplying + if(this->mTimeOfLastResupply > 0) + { + return FALSE; + } + + int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); + + if (j == 0) + return FALSE; + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay; + + //!!UNDONE -- reload sound goes here !!! + //SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 ); + this->SendWeaponAnim(iAnim); + + // Send reload to all players. Reloads are initiated server-side, so send down to local client as well + this->m_pPlayer->pev->weaponanim = iAnim; + this->PlaybackEvent(this->mWeaponAnimationEvent, iAnim, FEV_RELIABLE); + + // Player model reload animation + this->m_pPlayer->SetAnimation(PLAYER_RELOAD); + + m_fInReload = TRUE; + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + kDeployIdleInterval; + + return TRUE; +} + + + +char* AvHBasePlayerWeapon::GetActiveViewModel() const +{ + return this->GetViewModel(); +} + +//BOOL AvHBasePlayerWeapon::Deploy(char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, float inNextAttackTime, int skiplocal) +//{ +// BOOL theSuccess = FALSE; +// +// if(CanDeploy()) +// { +// this->m_pPlayer->TabulateAmmo(); +// this->m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); +// this->m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); +// strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); +// SendWeaponAnim( iAnim, skiplocal ); +// +// this->m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + inNextAttackTime; +// this->m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0; +// +// theSuccess = TRUE; +// } +// +// return theSuccess; +//} + +char* AvHBasePlayerWeapon::GetAnimationExtension() const +{ + char* theAnimExt = NULL; + + AvHWeaponID theWeaponID = (AvHWeaponID)this->m_iId; + switch(theWeaponID) + { + case AVH_WEAPON_BITE: + case AVH_WEAPON_SPIT: + case AVH_WEAPON_SPIKE: + case AVH_WEAPON_BITE2: + case AVH_WEAPON_SWIPE: + case AVH_WEAPON_CLAWS: + theAnimExt = "ability1"; + break; + + case AVH_WEAPON_PARASITE: + case AVH_WEAPON_HEALINGSPRAY: + case AVH_WEAPON_SPORES: + case AVH_WEAPON_BLINK: + case AVH_WEAPON_STOMP: + theAnimExt = "ability2"; + break; + + case AVH_ABILITY_LEAP: + case AVH_WEAPON_BILEBOMB: + case AVH_WEAPON_UMBRA: + case AVH_WEAPON_METABOLIZE: + case AVH_WEAPON_DEVOUR: + theAnimExt = "ability3"; + break; + + case AVH_WEAPON_DIVINEWIND: + case AVH_WEAPON_WEBSPINNER: + case AVH_WEAPON_PRIMALSCREAM: + case AVH_WEAPON_ACIDROCKET: + case AVH_ABILITY_CHARGE: + theAnimExt = "ability4"; + break; + + case AVH_WEAPON_KNIFE: + theAnimExt = "knife"; + break; + case AVH_WEAPON_PISTOL: + theAnimExt = "pistol"; + break; + case AVH_WEAPON_MG: + theAnimExt = "lmg"; + break; + case AVH_WEAPON_SONIC: + theAnimExt = "shotgun"; + break; + case AVH_WEAPON_HMG: + theAnimExt = "hmg"; + break; + case AVH_WEAPON_WELDER: + theAnimExt = "welder"; + break; + case AVH_WEAPON_MINE: + theAnimExt = "tripmine"; + break; + case AVH_WEAPON_GRENADE_GUN: + theAnimExt = "grenadegun"; + break; + case AVH_WEAPON_GRENADE: + theAnimExt = "handgrenade"; + break; + default: + ASSERT(false); + break; + } + return theAnimExt; +} + +int AvHBasePlayerWeapon::GetClipSize() const +{ + ItemInfo theItemInfo; + this->GetItemInfo(&theItemInfo); + int theClipSize = theItemInfo.iMaxClip; + + return theClipSize; +} + +int AvHBasePlayerWeapon::GetPrimaryAmmoAmount() const +{ + int theAmmo = 0; + + if(this->m_pPlayer && (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)) + { + theAmmo = this->m_pPlayer->m_rgAmmo[this->m_iPrimaryAmmoType]; + } + + return theAmmo; +} + +int AvHBasePlayerWeapon::GetDamageType() const +{ + return NS_DMG_NORMAL; +} + +char* AvHBasePlayerWeapon::GetDeploySound() const +{ + return NULL; +} + +float AvHBasePlayerWeapon::GetDeploySoundVolume() const +{ + return 1.0f; +} + +int AvHBasePlayerWeapon::GetEmptyShootAnimation() const +{ + return kShootEmptyAnimation; +} + +void AvHBasePlayerWeapon::GetEventOrigin(Vector& outOrigin) const +{ + VectorCopy(this->m_pPlayer->pev->origin, outOrigin); +} + +void AvHBasePlayerWeapon::GetEventAngles(Vector& outAngles) const +{ + outAngles = this->pev->v_angle + this->pev->punchangle; +} + +bool AvHBasePlayerWeapon::GetFiresUnderwater() const +{ + return false; +} + +bool AvHBasePlayerWeapon::GetIsFiring() const +{ + return this->mAttackButtonDownLastFrame; +} + +bool AvHBasePlayerWeapon::GetHasMuzzleFlash() const +{ + return false; +} + +bool AvHBasePlayerWeapon::GetIsCapableOfFiring() const +{ + return !this->UsesAmmo() || (this->m_iClip > 0); +} + + +int AvHBasePlayerWeapon::GetDeployAnimation() const +{ + return kDeployAnimation; +} + +int AvHBasePlayerWeapon::GetIdleAnimation() const +{ + int iAnim; + int theRandomNum = UTIL_SharedRandomLong(this->m_pPlayer->random_seed, 0, 1); + + switch(theRandomNum) + { + case 0: + iAnim = kIdleAnimationOne; + break; + + default: + case 1: + iAnim = kIdleAnimationTwo; + break; + } + + return iAnim; +} + +Vector AvHBasePlayerWeapon::GetProjectileSpread() const +{ + return VECTOR_CONE_0DEGREES; +} + +float AvHBasePlayerWeapon::ComputeAttackInterval() const +{ + return this->GetRateOfFire(); +} + +float AvHBasePlayerWeapon::GetRateOfFire() const +{ + return 1; +} + +int AvHBasePlayerWeapon::GetShootAnimation() const +{ + return kShootAnimation; +} + +int AvHBasePlayerWeapon::GetShotsInClip() const +{ + int theShotsInClip = -1; + + if(this->UsesAmmo()) + { + theShotsInClip = this->m_iClip; + } + + return theShotsInClip; +} + +bool AvHBasePlayerWeapon::GetIsDroppable() const +{ + return true; +} + +bool AvHBasePlayerWeapon::GetIsPlayerMoving() const +{ + bool thePlayerIsMoving = false; + +// float kMovingThresholdVelocity = 100; +// float theCurrentVelocity = 0; +// +// #ifdef AVH_SERVER +// theCurrentVelocity = this->m_pPlayer->pev->velocity.Length(); +// #endif +// +// #ifdef AVH_CLIENT +// cl_entity_t* theLocalPlayer = gEngfuncs.GetLocalPlayer(); +// if(theLocalPlayer) +// { +// theCurrentVelocity = theLocalPlayer->curstate.velocity.Length(); +// } +// #endif +// +// if(theCurrentVelocity > kMovingThresholdVelocity) +// { +// thePlayerIsMoving = true; +// } + + return thePlayerIsMoving; +} + +int AvHBasePlayerWeapon::GetMaxClipsCouldReceive() +{ + int theMaxClips = 0; + + ItemInfo theItemInfo; + this->GetItemInfo(&theItemInfo); + int theClipSize = theItemInfo.iMaxClip; + + if(theClipSize > 0 && this->UsesAmmo()) + { + theMaxClips = ((theItemInfo.iMaxAmmo1 - this->m_iClip)/theClipSize) + 1; + } + + return theMaxClips; +} + +AvHWeaponID AvHBasePlayerWeapon::GetPreviousWeaponID() const +{ + AvHWeaponID theID = AVH_WEAPON_NONE; + + // Look at most recently used weapon and see if we can transition from it + AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pPlayer->m_pLastItem); + if(theWeapon) + { + theID = (AvHWeaponID)(theWeapon->m_iId); + } + + return theID; +} + + +float AvHBasePlayerWeapon::GetRange() const +{ + return this->mRange; +} + +vec3_t AvHBasePlayerWeapon::GetWorldBarrelPoint() const +{ + ASSERT(this->m_pPlayer); + + Vector vecAiming = gpGlobals->v_forward; + Vector theWorldBarrelPoint = this->m_pPlayer->GetGunPosition() + vecAiming*this->GetBarrelLength(); + + return theWorldBarrelPoint; + +} + +char* AvHBasePlayerWeapon::GetPlayerModel() const +{ + return kNullModel; +} + +char* AvHBasePlayerWeapon::GetPrimeSound() const +{ + return NULL; +} + +float AvHBasePlayerWeapon::GetPrimeSoundVolume() const +{ + return 1.0f; +} + +char* AvHBasePlayerWeapon::GetViewModel() const +{ + return kNullModel; +} + +char* AvHBasePlayerWeapon::GetWorldModel() const +{ + return kNullModel; +} + +void AvHBasePlayerWeapon::Holster( int skiplocal) +{ + CBasePlayerWeapon::Holster(skiplocal); + + #ifdef AVH_SERVER + this->mInOverwatch = false; + #endif +} + +float AvHBasePlayerWeapon::GetTimePassedThisTick() const +{ + float theClientTimePassedThisTick = (pmove->cmd.msec/1000.0f); + return theClientTimePassedThisTick; +} + +void AvHBasePlayerWeapon::ItemPostFrame( void ) +{ + CBasePlayerWeapon::ItemPostFrame(); + + float theClientTimePassedThisTick = this->GetTimePassedThisTick(); + + this->m_flNextPrimaryAttack -= theClientTimePassedThisTick; + this->m_flTimeWeaponIdle -= theClientTimePassedThisTick; + this->mTimeOfLastResupply -= theClientTimePassedThisTick; + this->mTimeOfLastPrime -= theClientTimePassedThisTick; +} + +void AvHBasePlayerWeapon::Precache(void) +{ + CBasePlayerWeapon::Precache(); + + char* theDeploySound = this->GetDeploySound(); + if(theDeploySound) + { + PRECACHE_UNMODIFIED_SOUND(theDeploySound); + } + char* thePrimeSound = this->GetPrimeSound(); + if(thePrimeSound) + { + PRECACHE_UNMODIFIED_SOUND(thePrimeSound); + } + char* thePlayerModel = this->GetPlayerModel(); + if(thePlayerModel) + { + PRECACHE_UNMODIFIED_MODEL(thePlayerModel); + } + char* theViewModel = this->GetViewModel(); + if(theViewModel) + { + PRECACHE_UNMODIFIED_MODEL(theViewModel); + } + char* theWorldModel = this->GetWorldModel(); + if(theWorldModel) + { + PRECACHE_UNMODIFIED_MODEL(theWorldModel); + } + + this->mWeaponAnimationEvent = PRECACHE_EVENT(1, kWeaponAnimationEvent); +} + +bool AvHBasePlayerWeapon::ProcessValidAttack(void) +{ + bool theAttackIsValid = false; + +//#ifdef AVH_CLIENT +// char sz[ 128 ]; +// sprintf(sz, "during check valid, clip is %d\n", this->m_iClip); +// gEngfuncs.pfnCenterPrint( sz ); +// +// //gEngfuncs.Con_Printf("during idle, clip is %d\n", sz); +//#endif + +//#ifdef AVH_CLIENT +// if(gHUD.GetInTopDownMode()) +// { +// return false; +// } +//#endif +// +//#ifdef AVH_SERVER +// AvHPlayer* thePlayer = dynamic_cast(this->m_pPlayer); +// if(thePlayer->GetIsInTopDownMode()) +// { +// return false; +// } +//#endif + + // Only shoot if deployed and enabled (iuser3 is 0 when disabled, 1 when enabled ) + + // : 497 call GetEnabledState instead of testing directly + int enabledState=this->GetEnabledState(); + + if(this->m_pPlayer->pev->viewmodel && ( enabledState == 1)) + { + // don't fire underwater + if((this->m_pPlayer->pev->waterlevel == 3) && !this->GetFiresUnderwater()) + { + this->PlayEmptySound(); + + //this->m_flNextPrimaryAttack = gpGlobals->time + this->mROF; + this->m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + this->ComputeAttackInterval(); + } + else if(this->UsesAmmo()) + { + ASSERT(this->m_iPrimaryAmmoType >= 0); + if(this->m_iClip > 0) + { + if(this->m_flNextPrimaryAttack <= 0) + { + if(!this->GetMustPressTriggerForEachShot() || (!this->mAttackButtonDownLastFrame)) + { + theAttackIsValid = true; + } + } + } + else + { + // Definitely not firing this pull of the trigger + theAttackIsValid = false; + + BOOL theHasAmmo = 0; + + if ( pszAmmo1() ) + { + theHasAmmo |= (this->m_pPlayer->m_rgAmmo[this->m_iPrimaryAmmoType] != 0); + } + if ( pszAmmo2() ) + { + theHasAmmo |= (this->m_pPlayer->m_rgAmmo[this->m_iSecondaryAmmoType] != 0); + } + if (this->m_iClip > 0) + { + theHasAmmo |= 1; + } + + if(theHasAmmo) + { + // Trigger reload + this->Reload(); + } + else + { + this->PlayEmptySound(); + + this->SendWeaponAnim(this->GetEmptyShootAnimation()); + + //this->m_pPlayer->SetAnimation(PLAYER_ATTACK1); + } + } + } + else + { + theAttackIsValid = true; + } + } + + return theAttackIsValid; + +} + +bool AvHBasePlayerWeapon::GetIsGunPositionValid() const +{ + return true; +} + +void AvHBasePlayerWeapon::DeductCostForShot(void) +{ + this->m_iClip--; + + // On a successful attack, decloak the player if needed + #ifdef AVH_SERVER + AvHPlayer* thePlayer = dynamic_cast(this->m_pPlayer); + if(thePlayer) + { + thePlayer->TriggerUncloak(); + } + + int theResourceCost = this->GetResourceCost(); + if(theResourceCost > 0) + { + float theNewResources = (thePlayer->GetResources(false) - theResourceCost); + theNewResources = max(theNewResources, 0.0f); + thePlayer->SetResources(theNewResources); + } + + #endif +} + +float AvHBasePlayerWeapon::GetReloadTime(void) const +{ + // TODO: Allow weapons to have different reload times + return 1.5f; +} + +int AvHBasePlayerWeapon::GetReloadAnimation() const +{ + return kReloadAnimation; +} + +void AvHBasePlayerWeapon::PlaybackEvent(unsigned short inEvent, int inIparam2, int inFlags) const +{ + unsigned short theEvent = inEvent; + if(theEvent != 0) + { + // Playback event, sending along enough data so client knows who triggered this event! + int theWeaponIndex = 0; + AvHUser3 theUser3 = AVH_USER3_NONE; + int theUpgrades = 0; + + // When predicting weapons, play the event locally, then tell everyone else but us to play it back later + int flags = inFlags; + edict_t* theEdict; + + // Pass player random seed to event, so it chooses the right direction for spread + int theRandomNumber = this->m_pPlayer->random_seed; + +#if defined( AVH_CLIENT ) + theUser3 = gHUD.GetHUDUser3(); + theUpgrades = gHUD.GetHUDUpgrades(); + theEdict = NULL; +#else + theUser3 = dynamic_cast(this->m_pPlayer)->GetUser3(); + theUpgrades = this->m_pPlayer->pev->iuser4; + theEdict = this->m_pPlayer->edict(); +#endif + + // For bullet spread + //theRandomNumber = UTIL_SharedRandomLong(this->m_pPlayer->random_seed, 1, kBulletSpreadGranularity*kBulletSpreadGranularity); + + // When in overwatch, the weapon is fired on the server, so the client firing the weapon won't be firing it locally first +#if defined(AVH_SERVER) + if(this->mInOverwatch) + { + flags = 0; + } +#endif + + // Allow weapon to specify some parameters, so they are available on both client and server + Vector theEventOrigin; + this->GetEventOrigin(theEventOrigin); + + Vector theEventAngles; + this->GetEventAngles(theEventAngles); + + float theVolume = AvHPlayerUpgrade::GetSilenceVolumeLevel(theUser3, theUpgrades); + + //( int flags, const edict_t *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); + PLAYBACK_EVENT_FULL(flags, this->m_pPlayer->edict(), theEvent, 0, (float *)&theEventOrigin, (float *)&theEventAngles, theVolume, 0.0, theRandomNumber, inIparam2, 0, 0 ); + } +} + +void AvHBasePlayerWeapon::SetAnimationAndSound(void) +{ + this->m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; + this->m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; + + // player "shoot" animation + //SendWeaponAnim(this->GetShootAnimation()); + + this->m_pPlayer->SetAnimation(PLAYER_ATTACK1); + + if(this->GetHasMuzzleFlash()) + { + this->m_pPlayer->pev->effects = (int)(this->m_pPlayer->pev->effects) | EF_MUZZLEFLASH; + } +} + +void AvHBasePlayerWeapon::FireProjectiles(void) +{ + Vector vecSrc = this->m_pPlayer->GetGunPosition(); + Vector vecAiming = this->m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); + + // Fire the bullets and apply damage + int theRange = this->mRange; + float theDamage = this->mDamage; + + int theTracerFreq; + float theDamageMultiplier; + AvHPlayerUpgrade::GetWeaponUpgrade(this->m_pPlayer->pev->iuser3, this->m_pPlayer->pev->iuser4, &theDamageMultiplier, &theTracerFreq); + theDamage *= theDamageMultiplier; + + Vector theSpread = this->GetProjectileSpread(); + this->m_pPlayer->FireBulletsPlayer(1, vecSrc, vecAiming, theSpread, theRange, BULLET_PLAYER_MP5, theTracerFreq, theDamage, this->m_pPlayer->pev, this->m_pPlayer->random_seed); + + this->mWeaponPrimeStarted = false; +} + +int AvHBasePlayerWeapon::GetPrimeAnimation() const +{ + return -1; +} + +float AvHBasePlayerWeapon::GetWeaponPrimeTime() const +{ + return -1.0f; +} + +void AvHBasePlayerWeapon::PrimeWeapon() +{ + float thePrimeTime = this->GetWeaponPrimeTime(); + if(thePrimeTime > 0.0f) + { + if(!this->GetIsWeaponPriming()) + { + char* thePrimeSound = this->GetPrimeSound(); + if(thePrimeSound) + { + EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, thePrimeSound, this->GetPrimeSoundVolume(), ATTN_NORM); + } + + this->PlaybackEvent(this->mWeaponAnimationEvent, this->GetPrimeAnimation()); + + this->mTimeOfLastPrime = UTIL_WeaponTimeBase() + thePrimeTime; + + this->mWeaponPrimeStarted = true; + } + } +} + +BOOL AvHBasePlayerWeapon::GetIsWeaponPrimed() const +{ + return ((this->GetWeaponPrimeTime() > 0.0f) && this->mWeaponPrimeStarted && (UTIL_WeaponTimeBase() > this->mTimeOfLastPrime)); +} + +BOOL AvHBasePlayerWeapon::GetIsWeaponPriming() const +{ + return ((this->GetWeaponPrimeTime() > 0.0f) && this->mWeaponPrimeStarted && (UTIL_WeaponTimeBase() < this->mTimeOfLastPrime)); +} + +void AvHBasePlayerWeapon::SetNextAttack(void) +{ +// this->m_flNextPrimaryAttack += this->mROF; +// +// if(this->m_flNextPrimaryAttack < 0) +// this->m_flNextPrimaryAttack = this->mROF; + + float theRateOfFire = this->ComputeAttackInterval(); + + this->m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + theRateOfFire; + + if(this->m_flNextPrimaryAttack < 0) + { + this->m_flNextPrimaryAttack = theRateOfFire; + } + + this->SetNextIdle(); +} + + +void AvHBasePlayerWeapon::PrimaryAttack(void) +{ + if (this->ProcessValidAttack()) + { + + if (!this->mAttackButtonDownLastFrame) + { + this->PlaybackEvent(this->mStartEvent); + this->mAttackButtonDownLastFrame = true; + } + + this->PlaybackEvent(this->mEvent, this->GetShootAnimation()); + this->SetAnimationAndSound(); + + // If player is too close to a wall, don't actually fire the projectile + if(this->GetIsGunPositionValid()) + { + this->FireProjectiles(); + } + else + { + #ifdef DEBUG + #ifdef AVH_SERVER + char theMessage[256]; + sprintf(theMessage, "Gun position is not valid, skipping call to FireProjectiles.\n"); + ALERT(at_console, theMessage); + #endif + #endif + } + + this->DeductCostForShot(); + this->SetNextAttack(); + + } +} + +void AvHBasePlayerWeapon::Reload(void) +{ + // Move clip sounds out + int theReloadAnimation = this->GetReloadAnimation(); + float theReloadTime = this->GetReloadTime(); + int theClipSize = this->GetClipSize(); + + this->DefaultReload(theClipSize, theReloadAnimation, theReloadTime); + + // Don't idle for a bit + this->SetNextIdle(); +} + +bool AvHBasePlayerWeapon::GetCanBeResupplied() const +{ + bool theCanBeResupplied = false; + + if(this->UsesAmmo()) + { + ItemInfo theItemInfo; + this->GetItemInfo(&theItemInfo); + + int theCurrentPrimary = this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]; + int theMaxPrimary = theItemInfo.iMaxAmmo1; + + // Add some to primary store + if(theCurrentPrimary < theMaxPrimary) + { + theCanBeResupplied = true; + } + } + + return theCanBeResupplied; +} + +bool AvHBasePlayerWeapon::Resupply() +{ + bool theResupplied = false; + + if(this->GetCanBeResupplied()) + { + ItemInfo theItemInfo; + this->GetItemInfo(&theItemInfo); + + // Get amount to add + int theMaxClip = theItemInfo.iMaxClip; + + // Add half the clip each time, rounding up (roughly 3 seconds per clip () + int theAmountToAdd = max((theMaxClip+1)/2, 1); + + int theCurrentPrimary = this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]; + int theMaxPrimary = theItemInfo.iMaxAmmo1; + + // Add ammo + this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = min(theCurrentPrimary + theAmountToAdd, theMaxPrimary); + + const float theDelay = 1.0f; + //bugfix - don't let resupply shorten reload time + this->m_pPlayer->m_flNextAttack = max(this->m_pPlayer->m_flNextAttack,UTIL_WeaponTimeBase() + theDelay); + this->mTimeOfLastResupply = UTIL_WeaponTimeBase() + theDelay; + } + + return theResupplied; +} + +void AvHBasePlayerWeapon::SendWeaponAnim(int inAnimation, int skiplocal, int body) +{ + if(inAnimation >= 0) + { + this->m_pPlayer->pev->weaponanim = inAnimation; + + this->PlaybackEvent(this->mWeaponAnimationEvent, inAnimation); + } +} + +void AvHBasePlayerWeapon::SecondaryAttack(void) +{ +} + +void AvHBasePlayerWeapon::Spawn() +{ + CBasePlayerWeapon::Spawn(); + + this->pev->solid = SOLID_BBOX; + this->pev->movetype = MOVETYPE_TOSS; + UTIL_SetOrigin(this->pev, this->pev->origin); + UTIL_SetSize(this->pev, kMarineItemMinSize, kMarineItemMaxSize); + + this->pev->iuser3 = AVH_USER3_MARINEITEM; + + #ifdef AVH_SERVER + if(!this->GetIsPersistent()) + { + this->mLifetime = AvHSUGetWeaponStayTime(); + } + #endif +} + +bool AvHBasePlayerWeapon::GetEnabledState() const +{ + bool result=false; +#ifdef AVH_SERVER + result= (this->m_iEnabled == 1); +#else + // : 497 client now uses the enabled state in the appropriate WEAPON + ItemInfo theItemInfo; + this->GetItemInfo(&theItemInfo); + WEAPON *pWeapon = gWR.GetWeapon( theItemInfo.iId ); + if ( pWeapon != 0 ) { + result=(pWeapon->iEnabled == 1); + } +#endif + return result; +} + +#ifdef AVH_SERVER +void AvHBasePlayerWeapon::VirtualMaterialize(void) +{ + int theLifetime = this->GetGroundLifetime(); + + if(theLifetime >= 0) + { + SetThink(&AvHBasePlayerWeapon::DestroyItem); + this->pev->nextthink = gpGlobals->time + theLifetime; + } +} + +int AvHBasePlayerWeapon::GetGroundLifetime() const +{ + return this->mLifetime; +} + +// -1 means never expire +void AvHBasePlayerWeapon::SetGroundLifetime(int inGroundLifetime) +{ + this->mLifetime = inGroundLifetime; +} + +int AvHBasePlayerWeapon::GetResourceCost() const +{ + return 0; +} + +void AvHBasePlayerWeapon::VirtualDestroyItem(void) +{ + if(this->GetIsPersistent()) + { + // Make this weapon invisible until map reset + this->pev->effects |= EF_NODRAW; + this->pev->solid = SOLID_NOT; + this->pev->takedamage = DAMAGE_NO; + SetThink(NULL); + } + else + { + CBasePlayerItem::VirtualDestroyItem(); + } +} + +void AvHBasePlayerWeapon::ResetEntity() +{ + CBasePlayerWeapon::ResetEntity(); + + this->pev->effects = 0; + this->pev->solid = SOLID_BBOX; + this->pev->movetype = MOVETYPE_TOSS; + + this->pev->takedamage = DAMAGE_YES; + + this->VirtualMaterialize(); +} + +void AvHBasePlayerWeapon::SetOverwatchState(bool inState) +{ + this->mInOverwatch = inState; +} + +void AvHBasePlayerWeapon::UpdateInventoryEnabledState(int inNumActiveHives) +{ + // Process here + int theEnabledState = 1; + + ItemInfo theItemInfo; + if(this->GetItemInfo(&theItemInfo) != 0) + { + int theWeaponFlags = theItemInfo.iFlags; + AvHPlayer* thePlayer = dynamic_cast(this->m_pPlayer); + ASSERT(thePlayer); + + // If we don't have the hives required, or we're ensnared + if (/*thePlayer->GetIsTemporarilyInvulnerable() ||*/ + !thePlayer->GetIsAbleToAct() || + ((inNumActiveHives < 1) && (theWeaponFlags & ONE_HIVE_REQUIRED)) || + ((inNumActiveHives < 2) && (theWeaponFlags & TWO_HIVES_REQUIRED)) || + ((inNumActiveHives < 3) && (theWeaponFlags & THREE_HIVES_REQUIRED)) || + (this->GetResourceCost() > thePlayer->GetResources(false)) ) + { + // Disable it + theEnabledState = 0; + } + } + + // : 497 save the state for when we send the CurWeapon message + this->m_iEnabled = theEnabledState; +} + +void AvHBasePlayerWeapon::KeyValue(KeyValueData* pkvd) +{ + // Any entity placed by the mapper is persistent + this->SetPersistent(); + + if(FStrEq(pkvd->szKeyName, "teamchoice")) + { + this->pev->team = (AvHTeamNumber)(atoi(pkvd->szValue)); + pkvd->fHandled = TRUE; + } + else if(FStrEq(pkvd->szKeyName, "angles")) + { + // TODO: Insert code here + //pkvd->fHandled = TRUE; + } + else if(FStrEq(pkvd->szKeyName, "lifetime")) + { + this->mLifetime = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBasePlayerWeapon::KeyValue(pkvd); + } +} + +// 0 means never expire, -1 means no value was set so use default +int AvHBasePlayerWeapon::GetLifetime() const +{ + int theLifetime = this->mLifetime; + + if(theLifetime < 0) + { + theLifetime = AvHSUGetWeaponStayTime(); + } + + return theLifetime; +} + +bool AvHBasePlayerWeapon::GetIsPersistent() const +{ + return this->mIsPersistent; +} + +void AvHBasePlayerWeapon::SetPersistent() +{ + this->mIsPersistent = true; +} +#endif + +void AvHBasePlayerWeapon::SetNextIdle(void) +{ + float theRandomIdle = UTIL_SharedRandomFloat(this->m_pPlayer->random_seed, 10, 15); + + this->m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + theRandomIdle; + + // Just added these next two lines 8/8/01 (trying to fix prediction wackiness) + if(this->m_flTimeWeaponIdle < 0) + { + this->m_flTimeWeaponIdle = theRandomIdle; + } +} + +BOOL AvHBasePlayerWeapon::UseDecrement( void ) +{ + // If we're predicting, return true + return TRUE; +} + + +void AvHBasePlayerWeapon::WeaponIdle(void) +{ +//#ifdef AVH_CLIENT +// char sz[ 128 ]; +// sprintf(sz, "during idle, clip is %d\n", this->m_iClip); +// gEngfuncs.pfnCenterPrint( sz ); +// +// //gEngfuncs.Con_Printf("during idle, clip is %d\n", sz); +//#endif + + if(this->mAttackButtonDownLastFrame) + { + this->PlaybackEvent(this->mEndEvent); + this->mAttackButtonDownLastFrame = false; + } + + ResetEmptySound(); + + this->m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); + + if(this->m_flTimeWeaponIdle <= UTIL_WeaponTimeBase()) + { + this->SendWeaponAnim(this->GetIdleAnimation()); + + this->m_pPlayer->SetAnimation(PLAYER_IDLE); + + this->SetNextIdle(); + } +} + +bool AvHBasePlayerWeapon::UsesAmmo(void) const +{ + return true; +} diff --git a/main/source/mod/AvHBasePlayerWeapon.h b/main/source/mod/AvHBasePlayerWeapon.h index 2c055c9..20dc602 100644 --- a/main/source/mod/AvHBasePlayerWeapon.h +++ b/main/source/mod/AvHBasePlayerWeapon.h @@ -72,6 +72,7 @@ const int kShootEmptyAnimation = 4; const int kDeployAnimation = 5; Vector UTIL_GetRandomSpreadDir(unsigned int inSeed, int inShotNumber, const Vector& inBaseDirection, const Vector& inRight, const Vector& inUp, const Vector& inSpread); +Vector UTIL_GetRandomSpreadDirFrom(unsigned int inSeed, int inShotNumber, const Vector& inBaseDirection, const Vector& inRight, const Vector& inUp, const Vector& inSpread, const Vector& inFromSpread); class AvHBasePlayerWeapon : public CBasePlayerWeapon { diff --git a/main/source/mod/AvHBlink.cpp b/main/source/mod/AvHBlink.cpp index 21572c8..b337407 100644 --- a/main/source/mod/AvHBlink.cpp +++ b/main/source/mod/AvHBlink.cpp @@ -13,6 +13,7 @@ extern playermove_t* pmove; #include "cl_dll/hud.h" #include "mod/AvHHud.h" extern int g_runfuncs; +void IN_Attack2Down(); #endif #include "mod/AvHAlienAbilities.h" @@ -102,6 +103,13 @@ AvHMessageID AvHBlinkGun::GetAbilityImpulse() const return ALIEN_ABILITY_BLINK; } +void AvHBlinkGun::SecondaryAttack() +{ +#ifdef AVH_CLIENT + //this->FireProjectiles(); +#endif +} + void AvHBlinkGun::FireProjectiles(void) { #ifdef AVH_CLIENT @@ -117,12 +125,12 @@ void AvHBlinkGun::FireProjectiles(void) thePlayer->TriggerUncloak(); } #endif - if(this->mTimeOfNextBlinkEvent <= 0) - { - const float kEventDelay = 2.0f; - this->PlaybackEvent(this->mBlinkSuccessEvent, this->GetShootAnimation()); - this->mTimeOfNextBlinkEvent = UTIL_WeaponTimeBase() + kEventDelay; - } +// if(this->mTimeOfNextBlinkEvent <= 0) +// { +// const float kEventDelay = 2.0f; +// this->PlaybackEvent(this->mBlinkSuccessEvent, this->GetShootAnimation()); +// this->mTimeOfNextBlinkEvent = UTIL_WeaponTimeBase() + kEventDelay; +// } } bool AvHBlinkGun::GetMustPressTriggerForEachShot() const diff --git a/main/source/mod/AvHBuildable.cpp b/main/source/mod/AvHBuildable.cpp index f5c88a1..61f2767 100644 --- a/main/source/mod/AvHBuildable.cpp +++ b/main/source/mod/AvHBuildable.cpp @@ -82,6 +82,8 @@ void AvHBuildable::TriggerRemoveTech() const if(theTeam) { theTeam->TriggerRemoveTech(this->mTechID); + if ( this->mTechID == TECH_ADVANCED_ARMORY ) + theTeam->TriggerRemoveTech(TECH_ARMORY); } } diff --git a/main/source/mod/AvHClientVariables.h b/main/source/mod/AvHClientVariables.h index 118e1ef..6107bec 100644 --- a/main/source/mod/AvHClientVariables.h +++ b/main/source/mod/AvHClientVariables.h @@ -1,59 +1,63 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHClientVariables.h $ -// $Date: 2002/08/02 22:02:02 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHClientVariables.h,v $ -// Revision 1.6 2002/08/02 22:02:02 Flayra -// - Renamed centerid variable (because it tells info about everything, not just players) -// -// Revision 1.5 2002/05/23 02:34:00 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifndef AVH_CLIENTVARIABLES_H -#define AVH_CLIENTVARIABLES_H - -#include "common/cvardef.h" - -extern cvar_t* cl_cmhotkeys; -extern cvar_t* cl_highdetail; -extern cvar_t* cl_musicenabled; -extern cvar_t* cl_musicdelay; -extern cvar_t* cl_musicvolume; -extern cvar_t* cl_particleinfo; -extern cvar_t* cl_quickselecttime; -extern cvar_t* cl_musicdir; - -// Variables -#define kvAutoHelp "cl_autohelp" -// puzl: 1064 The cl var that controls the display of labelled minimaps -#define kvLabelMaps "cl_labelmaps" -// :puzl -// tankefugl: 0001070 - enables forced gamma ramp loading -#define kvGammaRamp "cl_gammaramp" -// :tankefugl -#define kvCMHotKeys "cl_cmhotkeys" -#define kvForceDefaultFOV "cl_forcedefaultfov" -#define kvCenterEntityID "cl_centerentityid" -#define kvHighDetail "cl_highdetail" -#define kvCMHotkeys "cl_cmhotkeys" -#define kvMusicEnabled "cl_musicenabled" -#define kvMusicDirectory "cl_musicdirectory" -#define kvMusicDelay "cl_musicdelay" -#define kvMusicVolume "cl_musicvolume" -#define kvParticleInfo "cl_particleinfo" -#define kvQuickSelectTime "cl_quickselecttime" - -#define kvDynamicLights "cl_dynamiclights" -#define kvBuildMessages "cl_buildmessages" - -#endif +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHClientVariables.h $ +// $Date: 2002/08/02 22:02:02 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHClientVariables.h,v $ +// Revision 1.6 2002/08/02 22:02:02 Flayra +// - Renamed centerid variable (because it tells info about everything, not just players) +// +// Revision 1.5 2002/05/23 02:34:00 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifndef AVH_CLIENTVARIABLES_H +#define AVH_CLIENTVARIABLES_H + +#include "common/cvardef.h" + +extern cvar_t* cl_cmhotkeys; +extern cvar_t* cl_highdetail; +extern cvar_t* cl_musicenabled; +extern cvar_t* cl_musicdelay; +extern cvar_t* cl_musicvolume; +extern cvar_t* cl_particleinfo; +extern cvar_t* cl_quickselecttime; +extern cvar_t* cl_musicdir; + +// Variables +#define kvAutoHelp "cl_autohelp" +// : 1064 The cl var that controls the display of labelled minimaps +#define kvLabelMaps "cl_labelmaps" +// : +// : 0001070 - enables forced gamma ramp loading +#define kvGammaRamp "cl_gammaramp" +#define kvLabelHivesight "cl_labelhivesight" +#define kvCustomCrosshair "cl_customcrosshair" +#define kvHudMapZoom "cl_hudmapzoom" + +// : +#define kvCMHotKeys "cl_cmhotkeys" +#define kvForceDefaultFOV "cl_forcedefaultfov" +#define kvCenterEntityID "cl_centerentityid" +#define kvHighDetail "cl_highdetail" +#define kvCMHotkeys "cl_cmhotkeys" +#define kvMusicEnabled "cl_musicenabled" +#define kvMusicDirectory "cl_musicdirectory" +#define kvMusicDelay "cl_musicdelay" +#define kvMusicVolume "cl_musicvolume" +#define kvParticleInfo "cl_particleinfo" +#define kvQuickSelectTime "cl_quickselecttime" + +#define kvDynamicLights "cl_dynamiclights" +#define kvBuildMessages "cl_buildmessages" + +#endif diff --git a/main/source/mod/AvHCloakable.cpp b/main/source/mod/AvHCloakable.cpp index a3c2366..228ded4 100644 --- a/main/source/mod/AvHCloakable.cpp +++ b/main/source/mod/AvHCloakable.cpp @@ -1,139 +1,140 @@ -#include "mod/AvHCloakable.h" -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "util/Balance.h" - -AvHCloakable::AvHCloakable() -{ - this->Init(); -} - -void AvHCloakable::Init() -{ - this->mCurrentSpeed = 0.0f; - this->mMaxSpeed = 0.0f; - this->mMaxWalkSpeed = 0.0f; - this->mTimeOfLastCloak = -1; - this->mTimeOfLastUncloak = -1; - this->mOpacity = 1.0f; - this->mTimeOfLastUpdate = 0; -} - -bool AvHCloakable::GetCanCloak() const -{ - bool theCanCloak = ((this->mTimeOfLastUncloak == -1) || (this->GetTime() > this->mTimeOfLastUncloak + this->GetUncloakTime())); - - if(!theCanCloak) - { - int a = 0; - } - - return theCanCloak; -} - -float AvHCloakable::GetCloakTime() const -{ - return BALANCE_VAR(kCloakTime); -} - -bool AvHCloakable::GetIsCloaked() const -{ - return (this->mOpacity < 0.1f); -} - -bool AvHCloakable::GetIsPartiallyCloaked() const -{ - return (this->mOpacity < 0.6f); -} - -float AvHCloakable::GetUncloakTime() const -{ - return BALANCE_VAR(kUncloakTime); -} - -void AvHCloakable::ResetCloaking() -{ - this->Init(); -} - -void AvHCloakable::Update() -{ - float theCurrentTime = this->GetTime(); - - float theTimePassed = theCurrentTime - this->mTimeOfLastUpdate; - - float theOldOpacity=this->mOpacity; - if((this->mTimeOfLastCloak != -1) || (this->mTimeOfLastUncloak != -1)) - { - if( this->mTimeOfLastCloak > this->mTimeOfLastUncloak ) - { - // Cloaking - this->mOpacity -= theTimePassed/this->GetCloakTime(); - if ( this->mOpacity < 0.45f && this->mCurrentSpeed > this->mMaxWalkSpeed ) - { - float theExtraSpeed = max(0.0f, this->mCurrentSpeed - this->mMaxWalkSpeed); - float theSpeedRange = max(0.0f, this->mMaxSpeed - this->mMaxWalkSpeed); - float thePercent=theExtraSpeed/theSpeedRange; - this->mOpacity=0.30f * thePercent; - if ( this->mCurrentSpeed > this->mMaxSpeed ) { - //ALERT(at_console, "exceeded the speed limit %f\n", this->mCurrentSpeed); - this->mOpacity=0.30f + 0.30f * ((this->mCurrentSpeed - this->mMaxSpeed) / this->mMaxSpeed / 2.0f ); - } - this->mOpacity = min(max(0.0f, this->mOpacity), 0.45f); - } - } - else - { - // Uncloaking - this->mOpacity += theTimePassed/this->GetUncloakTime(); - } - - this->mOpacity = min(max(0.0f, this->mOpacity), 1.0f); - } - this->mTimeOfLastUpdate = theCurrentTime; -} - -float AvHCloakable::GetOpacity() const -{ - return this->mOpacity; -} - -void AvHCloakable::SetSpeeds(float inCurrentSpeed, float inMaxSpeed, float inMaxWalkSpeed) -{ - this->mCurrentSpeed=inCurrentSpeed; - this->mMaxSpeed=inMaxSpeed; - this->mMaxWalkSpeed=inMaxWalkSpeed; -} - -void AvHCloakable::Cloak(bool inNoFade) -{ - // puzl: 864 - if ( (this->GetTime() > this->mTimeOfLastUncloak + BALANCE_VAR(kRecloakTime)) || inNoFade ) { - if(this->GetCanCloak()) - { - this->mTimeOfLastCloak = this->GetTime(); - this->mTimeOfLastUncloak = -1; - - if(inNoFade) - { - this->mOpacity = 0.0f; - } - } - } -} - -void AvHCloakable::Uncloak(bool inNoFade) -{ - this->mTimeOfLastUncloak = this->GetTime(); - this->mTimeOfLastCloak = -1; - - if(inNoFade) - { - this->mOpacity = 1.0f; - } -} - -float AvHCloakable::GetTime() const -{ - return gpGlobals->time; -} +#include "mod/AvHCloakable.h" +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "util/Balance.h" + +AvHCloakable::AvHCloakable() +{ + this->Init(); +} + +void AvHCloakable::Init() +{ + this->mCurrentSpeed = 0.0f; + this->mMaxSpeed = 0.0f; + this->mMaxWalkSpeed = 0.0f; + this->mTimeOfLastCloak = -1; + this->mTimeOfLastUncloak = -1; + this->mOpacity = 1.0f; + this->mTimeOfLastUpdate = 0; +} + +bool AvHCloakable::GetCanCloak() const +{ + bool theCanCloak = ((this->mTimeOfLastUncloak == -1) || (this->GetTime() > this->mTimeOfLastUncloak + this->GetUncloakTime())); + + if(!theCanCloak) + { + int a = 0; + } + + return theCanCloak; +} + +float AvHCloakable::GetCloakTime() const +{ + return BALANCE_VAR(kCloakTime); +} + +bool AvHCloakable::GetIsCloaked() const +{ + return (this->mOpacity < 0.1f); +} + +bool AvHCloakable::GetIsPartiallyCloaked() const +{ + return (this->mOpacity < 0.6f); +} + +float AvHCloakable::GetUncloakTime() const +{ + return BALANCE_VAR(kUncloakTime); +} + +void AvHCloakable::ResetCloaking() +{ + this->Init(); +} + +void AvHCloakable::Update() +{ + float theCurrentTime = this->GetTime(); + + float theTimePassed = theCurrentTime - this->mTimeOfLastUpdate; + + if((this->mTimeOfLastCloak != -1) || (this->mTimeOfLastUncloak != -1)) + { + float newOpacity=this->mOpacity; + if( this->mTimeOfLastCloak > this->mTimeOfLastUncloak ) + { + // Cloaking + newOpacity -= theTimePassed/this->GetCloakTime(); + // Adjust for movement + if ( this->mOpacity < 0.45f && this->mCurrentSpeed > this->mMaxWalkSpeed ) { + newOpacity=this->mOpacity; + if ( this->mCurrentSpeed > this->mMaxSpeed ) { + newOpacity += theTimePassed / this->GetUncloakTime(); + } + else { + newOpacity += theTimePassed/this->GetUncloakTime() / 2.0f; + + } + newOpacity = min(max(0.0f, newOpacity), 0.45f); + } + } + else + { + // Uncloaking + newOpacity += theTimePassed/this->GetUncloakTime(); + } + + + this->mOpacity = min(max(0.0f, newOpacity), 1.0f); + } + this->mTimeOfLastUpdate = theCurrentTime; +} + +float AvHCloakable::GetOpacity() const +{ + return this->mOpacity; +} + +void AvHCloakable::SetSpeeds(float inCurrentSpeed, float inMaxSpeed, float inMaxWalkSpeed) +{ + this->mCurrentSpeed=inCurrentSpeed; + this->mMaxSpeed=inMaxSpeed; + this->mMaxWalkSpeed=inMaxWalkSpeed; +} + +void AvHCloakable::Cloak(bool inNoFade) +{ + // : 864 + if ( (this->GetTime() > this->mTimeOfLastUncloak + BALANCE_VAR(kRecloakTime)) || inNoFade ) { + if(this->GetCanCloak()) + { + this->mTimeOfLastCloak = this->GetTime(); + this->mTimeOfLastUncloak = -1; + + if(inNoFade) + { + this->mOpacity = 0.0f; + } + } + } +} + +void AvHCloakable::Uncloak(bool inNoFade) +{ + this->mTimeOfLastUncloak = this->GetTime(); + this->mTimeOfLastCloak = -1; + + if(inNoFade) + { + this->mOpacity = 1.0f; + } +} + +float AvHCloakable::GetTime() const +{ + return gpGlobals->time; +} diff --git a/main/source/mod/AvHCombat.cpp b/main/source/mod/AvHCombat.cpp index 6aa3dbf..208ba6c 100644 --- a/main/source/mod/AvHCombat.cpp +++ b/main/source/mod/AvHCombat.cpp @@ -1,706 +1,709 @@ -#include "types.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHSharedUtil.h" - -extern int gLevelUpEventID; - -bool AvHPlayer::GiveCombatModeUpgrade(AvHMessageID inMessageID, bool inInstantaneous) -{ - bool theSuccess = false; - bool theEffectivePlayerClassChanged = false; - - // Process every upgrade here - AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; - int thePlayerLevel = this->GetExperienceLevel(); - float theHealthPercentage = this->pev->health/AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, thePlayerLevel); - float theArmorPercentage = this->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); - bool thePreserveHealthArmorPercentage = true; - CBasePlayerItem* theCreatedItem = NULL; - - // Marine upgrades - if(this->GetIsMarine()) - { - Vector thePlayerMinSize, thePlayerMaxSize; - this->GetSize(thePlayerMinSize, thePlayerMaxSize); - - Vector thePlayersFeet = this->pev->origin; - thePlayersFeet.z += thePlayerMinSize.z; - - switch(inMessageID) - { - case BUILD_SHOTGUN: - case BUILD_GRENADE_GUN: - case BUILD_HMG: - case BUILD_WELDER: - case BUILD_MINES: - case BUILD_JETPACK: - case BUILD_HEAVY: - //voogru: spawn the weapon in the middle of nowhere to prevent anyone else from getting it. - theCreatedItem = dynamic_cast(AvHSUBuildTechForPlayer(inMessageID, Vector(9999,9999,9999), this)); - - ASSERT(theCreatedItem); - - if((inMessageID == BUILD_JETPACK) || (inMessageID == BUILD_HEAVY)) - { - theEffectivePlayerClassChanged = true; - } - - theSuccess = true; - break; - - case BUILD_SCAN: - AvHSUBuildTechForPlayer(inMessageID, thePlayersFeet, this); - theSuccess = true; - break; - -// case RESEARCH_DISTRESSBEACON: -// AvHSUResearchStarted(this, inMessageID); -// AvHSUResearchComplete(this, inMessageID); -// theSuccess = true; -// break; - - // Give upgrades - case RESEARCH_ARMOR_ONE: - this->GiveTeamUpgrade(RESEARCH_ARMOR_ONE); - theSuccess = true; - break; - - case RESEARCH_ARMOR_TWO: - this->GiveTeamUpgrade(RESEARCH_ARMOR_ONE); - this->GiveTeamUpgrade(RESEARCH_ARMOR_TWO); - theSuccess = true; - break; - - case RESEARCH_ARMOR_THREE: - this->GiveTeamUpgrade(RESEARCH_ARMOR_ONE); - this->GiveTeamUpgrade(RESEARCH_ARMOR_TWO); - this->GiveTeamUpgrade(RESEARCH_ARMOR_THREE); - theSuccess = true; - break; - - case RESEARCH_WEAPONS_ONE: - this->GiveTeamUpgrade(RESEARCH_WEAPONS_ONE); - theSuccess = true; - break; - - case RESEARCH_WEAPONS_TWO: - this->GiveTeamUpgrade(RESEARCH_WEAPONS_ONE); - this->GiveTeamUpgrade(RESEARCH_WEAPONS_TWO); - theSuccess = true; - break; - - case RESEARCH_WEAPONS_THREE: - this->GiveTeamUpgrade(RESEARCH_WEAPONS_ONE); - this->GiveTeamUpgrade(RESEARCH_WEAPONS_TWO); - this->GiveTeamUpgrade(RESEARCH_WEAPONS_THREE); - theSuccess = true; - break; - - case RESEARCH_GRENADES: - this->GiveNamedItem(kwsGrenade); - // NOTE: Fall through below - - case RESEARCH_MOTIONTRACK: - this->GiveTeamUpgrade(inMessageID); - theSuccess = true; - break; - - //case BUILD_AMMO: - //case BUILD_HEALTH: - case BUILD_RESUPPLY: - // Find all friendly players nearby (right now this just resupplies current player) - // Give them health and ammo, equal to # of current level - AvHSUResupplyFriendliesInRange(1, this); - thePreserveHealthArmorPercentage = false; - theSuccess = true; - - // Add new tech node to allow us to buy it again - //AvHTechNode theTechNode(BUILD_RESUPPLY, TECH_FOUR_LEVEL_TWO, TECH_FOUR_LEVEL_TWO, TECH_NULL, 0, 0, true); - //this->mCombatNodes.AddTechNode(theTechNode); - break; - - case BUILD_CAT: - // Don't give out cat-packs every time - AvHCatalyst::GiveCatalyst(this); - theSuccess = true; - break; - } - } - else if(this->GetIsAlien()) - { - theSuccess = this->ExecuteAlienMorphMessage(inMessageID, inInstantaneous); - thePreserveHealthArmorPercentage = false; - } - - if(thePreserveHealthArmorPercentage) - { - this->pev->health = theHealthPercentage*AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, thePlayerLevel); - this->pev->armorvalue = theArmorPercentage*AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); - } - - if(theEffectivePlayerClassChanged) - { - this->EffectivePlayerClassChanged(); - } - - return theSuccess; -} - -float AvHPlayer::GetExperience() const -{ - return this->mExperience; -} - -int AvHPlayer::GetExperienceLevelsSpent() const -{ - return this->mExperienceLevelsSpent; -} - -void AvHPlayer::SetExperienceLevelsSpent(int inSpentLevels) -{ - this->mExperienceLevelsSpent = inSpentLevels; -} - -void AvHPlayer::AwardExperienceForObjective(float inHealthChange, AvHMessageID inMessageID) -{ - bool theAwardExperience = false; - - if(GetGameRules()->GetIsCombatMode()) - { - switch(inMessageID) - { - case ALIEN_BUILD_HIVE: - case BUILD_COMMANDSTATION: - theAwardExperience = true; - break; - } - } - - if(theAwardExperience) - { - int theMaxHealth = GetGameRules()->GetBaseHealthForMessageID(inMessageID); - float thePercentageOfHealth = inHealthChange/theMaxHealth; - int theCombatObjectiveExperienceScalar = BALANCE_VAR(kCombatObjectiveExperienceScalar); - float theExperienceGained = thePercentageOfHealth*theCombatObjectiveExperienceScalar; - this->SetExperience(this->GetExperience() + theExperienceGained); - } -} - -void AvHPlayer::SetExperience(float inExperience) -{ - if(GetGameRules()->GetIsCombatMode()) - { - int theCurrentLevel = AvHPlayerUpgrade::GetPlayerLevel(this->GetExperience()); - - this->mExperience = inExperience; - - // Update server player data in case we get disconnected - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); - if(theServerPlayerData) - { - theServerPlayerData->SetExperience(this->mExperience); - } - } - - int theNewLevel = AvHPlayerUpgrade::GetPlayerLevel(this->GetExperience()); - - if(theCurrentLevel != theNewLevel) - { - int theIsMarine = this->GetIsMarine(); - PLAYBACK_EVENT_FULL(0, this->edict(), gLevelUpEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, theIsMarine, 0, 0, 0 ); - - // Give player health and armor back on level-up, to allow more soloing, heroics, and reduce dependence on hives/resupply - AvHUser3 theUser3 = AvHUser3(this->pev->iuser3); - float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, theCurrentLevel); - float theHealthPercentage = this->pev->health/theMaxHealth; - - float theLevelUpHealthPercentage = BALANCE_VAR(kCombatLevelupHealthIncreasePercent)/100.0f; - theHealthPercentage = min(theHealthPercentage + theLevelUpHealthPercentage, 1.0f); - this->pev->health = theHealthPercentage*theMaxHealth; - - float theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); - float theArmorPercentage = this->pev->armorvalue/theMaxArmor; - - float theLevelUpArmorPercentage = BALANCE_VAR(kCombatLevelupArmorIncreasePercent)/100.0f; - theArmorPercentage = min(theArmorPercentage + theLevelUpArmorPercentage, 1.0f); - this->pev->armorvalue = theArmorPercentage*theMaxArmor; - - // Unlock tiers as player levels up - // if(theNewLevel >= 4) - // { - // this->mCombatNodes.SetResearchDone(COMBAT_TIER2_UNLOCK); - // } - // if(theNewLevel >= 7) - // { - // this->mCombatNodes.SetResearchDone(COMBAT_TIER3_UNLOCK); - // } - } - } -} - -int AvHPlayer::GetExperienceLevel() const -{ - int theLevel = 1; - - if(GetGameRules()->GetIsCombatMode()) - { - theLevel = AvHPlayerUpgrade::GetPlayerLevel(this->GetExperience()); - } - - return theLevel; -} - -AvHTechTree& AvHPlayer::GetCombatNodes() -{ - return this->mCombatNodes; -} - -MessageIDListType& AvHPlayer::GetPurchasedCombatUpgrades() -{ - return this->mPurchasedCombatUpgrades; -} - -void AvHPlayer::SetCombatNodes(const AvHTechTree& inTechNodes) -{ - this->mCombatNodes = inTechNodes; -} - -void AvHPlayer::GiveCombatUpgradesOnSpawn() -{ - // Save off previously-spent upgrades and respend them - MessageIDListType theUpgrades = this->mGiveCombatUpgrades; - - // Need to run through these in order - for(MessageIDListType::iterator theIter = theUpgrades.begin(); theIter != theUpgrades.end(); theIter++) - { - AvHMessageID theCurrentCombatUpgrade = *theIter; - if(theCurrentCombatUpgrade != MESSAGE_NULL) - { - this->GiveCombatModeUpgrade(theCurrentCombatUpgrade, true); - } - } -} - -void AvHPlayer::PurchaseCombatUpgrade(AvHMessageID inMessageID) -{ - this->mPurchasedCombatUpgrades.push_back(inMessageID); - //this->mExperienceLevelsSpent++; - - // Remove any upgrades that this prempts - bool theOneShot = false; - switch(inMessageID) - { - case BUILD_AMMO: - case BUILD_HEALTH: - case BUILD_RESUPPLY: - case BUILD_CAT: - case BUILD_SCAN: - //case BUILD_MINES: - //case RESEARCH_DISTRESSBEACON: - theOneShot = true; - break; - } - - // Don't add it as a permanent upgrade to get every time we spawn - if(!theOneShot) - { - this->mGiveCombatUpgrades.push_back(inMessageID); - } - - this->RemoveCombatUpgradesPremptedBy(inMessageID); -} - -void AvHPlayer::RemoveCombatUpgrade(AvHMessageID inMessageID) -{ - MessageIDListType::iterator theIter = std::find(this->mGiveCombatUpgrades.begin(), this->mGiveCombatUpgrades.end(), inMessageID); - if(theIter != this->mGiveCombatUpgrades.end()) - { - this->mGiveCombatUpgrades.erase(theIter); - - // Take away the upgrade - this->GiveTeamUpgrade(inMessageID, false); - } -} - -void AvHPlayer::RemoveCombatUpgradesPremptedBy(AvHMessageID inMessageID) -{ - switch(inMessageID) - { - case BUILD_JETPACK: - this->RemoveCombatUpgrade(BUILD_HEAVY); - break; - case BUILD_HEAVY: - this->RemoveCombatUpgrade(BUILD_JETPACK); - break; - case BUILD_HMG: - this->RemoveCombatUpgrade(BUILD_SHOTGUN); - this->RemoveCombatUpgrade(BUILD_GRENADE_GUN); - break; - case BUILD_GRENADE_GUN: - this->RemoveCombatUpgrade(BUILD_SHOTGUN); - this->RemoveCombatUpgrade(BUILD_HMG); - break; - case ALIEN_LIFEFORM_TWO: - this->RemoveCombatUpgrade(ALIEN_LIFEFORM_ONE); - break; - case ALIEN_LIFEFORM_THREE: - this->RemoveCombatUpgrade(ALIEN_LIFEFORM_TWO); - break; - case ALIEN_LIFEFORM_FOUR: - this->RemoveCombatUpgrade(ALIEN_LIFEFORM_THREE); - break; - case ALIEN_LIFEFORM_FIVE: - this->RemoveCombatUpgrade(ALIEN_LIFEFORM_FOUR); - break; - - //case BUILD_WELDER - // this->RemoveCombatUpgrade(BUILD_MINES); - // break; - //case BUILD_MINES: - // this->RemoveCombatUpgrade(BUILD_WELDER); - // break; - } -} - -void AvHPlayer::ProcessCombatDeath() -{ - // Unspend lifeform (return to skulk, but get the levels spent on lifeform back) - if(this->GetIsAlien()) - { - AvHMessageID theLifeformID = MESSAGE_NULL; - AvHUser3 theUser3 = this->GetUser3(false); - if(theUser3 == AVH_USER3_ALIEN_EMBRYO) - { - // Use the last lifeform we were, unless we're evolving into a new one (assumes only one lifeform change per life) - theUser3 = this->GetPreviousUser3(false); - switch(this->mEvolution) - { - case ALIEN_LIFEFORM_TWO: - theUser3 = AVH_USER3_ALIEN_PLAYER2; - break; - case ALIEN_LIFEFORM_THREE: - theUser3 = AVH_USER3_ALIEN_PLAYER3; - break; - case ALIEN_LIFEFORM_FOUR: - theUser3 = AVH_USER3_ALIEN_PLAYER4; - break; - case ALIEN_LIFEFORM_FIVE: - theUser3 = AVH_USER3_ALIEN_PLAYER5; - break; - } - } - - AvHSHUUser3ToMessageID(theUser3, theLifeformID); - ASSERT(theLifeformID != MESSAGE_NULL); - - int theLifeformCost = GetGameRules()->GetCostForMessageID(theLifeformID); - this->SetExperienceLevelsSpent(this->GetExperienceLevelsSpent() - theLifeformCost); - - this->RemoveCombatUpgrade(theLifeformID); - - // Make all lifeforms chooseable again - this->SetLifeformCombatNodesAvailable(true); - } -} - -void AvHPlayer::SetLifeformCombatNodesAvailable(bool inAvailable) -{ - MessageIDListType theLifeformList; - theLifeformList.push_back(ALIEN_LIFEFORM_ONE); - theLifeformList.push_back(ALIEN_LIFEFORM_TWO); - theLifeformList.push_back(ALIEN_LIFEFORM_THREE); - theLifeformList.push_back(ALIEN_LIFEFORM_FOUR); - theLifeformList.push_back(ALIEN_LIFEFORM_FIVE); - - for(MessageIDListType::iterator theIter = theLifeformList.begin(); theIter != theLifeformList.end(); theIter++) - { - AvHMessageID theLifeformID = *theIter; - this->mCombatNodes.SetIsResearchable(theLifeformID, inAvailable); - this->mCombatNodes.SetResearchDone(theLifeformID, !inAvailable); - } -} - -bool AvHPlayer::ExecuteCombatMessage(AvHMessageID inMessageID, bool& outIsAvailable, bool inForce) -{ - bool theMessageExecuted = false; - - // Only explicitly deny messages in the tech tree, let the others fall back to NS-mode handling - if(this->mCombatNodes.GetIsMessageInTechTree(inMessageID)) - { - if(this->mCombatNodes.GetIsMessageAvailable(inMessageID) || inForce) - { - if(this->GiveCombatModeUpgrade(inMessageID)) - { - theMessageExecuted = true; - } - } - else - { - // Explicitly deny - outIsAvailable = false; - } - } - - return theMessageExecuted; -} - -bool AvHPlayer::GetHasCombatModeUpgrade(AvHMessageID inMessageID) const -{ - bool theHasUpgrade = false; - - MessageIDListType::const_iterator theIter = std::find(this->mPurchasedCombatUpgrades.begin(), this->mPurchasedCombatUpgrades.end(), inMessageID); - if(theIter != this->mPurchasedCombatUpgrades.end()) - { - theHasUpgrade = true; - } - - return theHasUpgrade; -} - -void AvHPlayer::InternalCombatThink() -{ - // Only update every so often - if(GetGameRules()->GetIsCombatMode()) - { - // Save our data in case we get kicked off - AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); - if(theServerPlayerData) - { - theServerPlayerData->SetCombatNodes(this->mCombatNodes); - theServerPlayerData->SetPurchasedCombatUpgrades(this->mPurchasedCombatUpgrades); - theServerPlayerData->SetExperienceLevelsSpent(this->mExperienceLevelsSpent); - } - - // If it's time for an update - float theCurrentTime = gpGlobals->time; - const float theCombatThinkInterval = BALANCE_VAR(kCombatThinkInterval); - - // Give support from a fake commander - if(this->GetIsMarine() && this->GetIsRelevant() && !this->GetIsBeingDigested()) - { - if(this->mTimeOfLastCombatThink == 0 || (theCurrentTime > (this->mTimeOfLastCombatThink + theCombatThinkInterval))) - { - // Only allow one upgrade per think - bool theSuccess = false; - - // Does player have resupply upgrade? - if(this->GetHasCombatModeUpgrade(BUILD_RESUPPLY)) - { - // Do they need it? - float theHealthPercentage = this->pev->health/AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - // float theAmmoPercentage = this->GetCurrentWeaponAmmoPercentage(); // changed to fix #542 - bool theAmmoResupply = this->GetShouldResupplyAmmo(); - - if((theHealthPercentage < BALANCE_VAR(kCombatResupplyHealthPercentage)) || theAmmoResupply) //(theAmmoPercentage < BALANCE_VAR(kCombatResupplyAmmoPercentage))) - { - // Resupply player - this->GiveCombatModeUpgrade(BUILD_RESUPPLY); - theSuccess = true; - } - } - - // Does player have resupply upgrade? - if(this->GetHasCombatModeUpgrade(BUILD_CAT)) - { - // Catalyst player after he gets a kill - if(this->pev->frags > this->mSavedCombatFrags) - { - //if(RANDOM_LONG(0, 1) == 0) - //{ - AvHCatalyst::GiveCatalyst(this); - theSuccess = true; - //} - } - } - this->mSavedCombatFrags = this->pev->frags; - - // Does player have scan upgrade? - if(!theSuccess && this->GetHasCombatModeUpgrade(BUILD_SCAN) && !this->GetIsBeingDigested()) - { - // Needed if there is a cloaked enemy nearby - bool theCloakedEnemyNearby = false; - - // Look in sphere for cloakables - CBaseEntity* theSphereEntity = NULL; - while ((theSphereEntity = UTIL_FindEntityInSphere(theSphereEntity, this->pev->origin, BALANCE_VAR(kScanRadius))) != NULL) - { - if(!AvHSUGetIsExternalClassName(STRING(theSphereEntity->pev->classname))) - { - AvHCloakable* theCloakable = dynamic_cast(theSphereEntity); - if(theCloakable && theCloakable->GetIsPartiallyCloaked() && (theSphereEntity->pev->team != this->pev->team)) - { - //if(this->GetIsEntityInSight(theSphereEntity)) - //{ - theCloakedEnemyNearby = true; - break; - //} - } - } - } - - // Lucky enough to receive? - if(theCloakedEnemyNearby /*&& (RANDOM_LONG(0, 1) == 0)*/) - { - // Scan - this->GiveCombatModeUpgrade(BUILD_SCAN); - theSuccess = true; - } - } - -// // Does player have distress beacon? -// if(!theSuccess && this->GetHasCombatModeUpgrade(RESEARCH_DISTRESSBEACON)) -// { -// // Needed? -// int theNumTeammatesWaitingToSpawn = 0; -// FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*); -// if(theEntity->GetTeam() == this->GetTeam()) -// { -// if(theEntity->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) -// { -// theNumTeammatesWaitingToSpawn++; -// } -// } -// END_FOR_ALL_ENTITIES(kAvHPlayerClassName); -// -// // Lucky enough? -// int theNumPlayersOnTeam = 0; -// AvHTeam* theTeamPointer = this->GetTeamPointer(); -// if(theTeamPointer) -// { -// theNumPlayersOnTeam = theTeamPointer->GetPlayerCount(); -// } -// -// float theDeadPercentage = (float)theNumTeammatesWaitingToSpawn/theNumPlayersOnTeam; -// if(theDeadPercentage > BALANCE_VAR(kCombatDistressBeaconDeadPercentage)) -// { -// // Distress! -// this->GiveCombatModeUpgrade(RESEARCH_DISTRESSBEACON); -// theSuccess = true; -// } -// } - - // Update time - this->mTimeOfLastCombatThink = theCurrentTime; - } - } - else if(this->GetIsAlien() && this->GetIsRelevant()) - { - // Give aliens experience slowly over time - if(this->mTimeOfLastCombatThink == 0 || (theCurrentTime > (this->mTimeOfLastCombatThink + theCombatThinkInterval))) - { - float theExperienceRate = BALANCE_VAR(kCombatExperienceAlienGrowthRate); - float theExperienceGained = theCombatThinkInterval*theExperienceRate; - this->SetExperience(this->GetExperience() + theExperienceGained); - - // Update time - this->mTimeOfLastCombatThink = theCurrentTime; - } - } - } -} - - -void AvHTeam::InitializeCombatTechNodes() -{ - // If team is marine - if(this->mTeamType == AVH_CLASS_TYPE_MARINE) - { - this->AddTechNode(RESEARCH_WEAPONS_ONE, TECH_ONE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(RESEARCH_WEAPONS_TWO, TECH_ONE_LEVEL_TWO, TECH_ONE_LEVEL_ONE, TECH_NULL, false); - this->AddTechNode(RESEARCH_WEAPONS_THREE, TECH_NULL, TECH_ONE_LEVEL_TWO, TECH_NULL, false); - this->AddTechNode(BUILD_SHOTGUN, TECH_ONE_LEVEL_THREE, TECH_ONE_LEVEL_ONE, TECH_NULL, false); - this->AddTechNode(BUILD_GRENADE_GUN, TECH_NULL, TECH_ONE_LEVEL_THREE, TECH_NULL, false); - this->AddTechNode(BUILD_HMG, TECH_NULL, TECH_ONE_LEVEL_THREE, TECH_NULL, false); - - this->AddTechNode(RESEARCH_ARMOR_ONE, TECH_TWO_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(RESEARCH_ARMOR_TWO, TECH_TWO_LEVEL_TWO, TECH_TWO_LEVEL_ONE, TECH_NULL, false); - this->AddTechNode(RESEARCH_ARMOR_THREE, TECH_NULL, TECH_TWO_LEVEL_TWO, TECH_NULL, false); - this->AddTechNode(BUILD_JETPACK, TECH_NULL, TECH_TWO_LEVEL_TWO, TECH_NULL, false); - this->AddTechNode(BUILD_HEAVY, TECH_NULL, TECH_TWO_LEVEL_TWO, TECH_NULL, false); - - this->AddTechNode(BUILD_SCAN, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); - this->AddTechNode(RESEARCH_MOTIONTRACK, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - //this->AddTechNode(RESEARCH_DISTRESSBEACON, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, true, false); - - this->AddTechNode(BUILD_RESUPPLY, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); - this->AddTechNode(BUILD_CAT, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); - - this->AddTechNode(BUILD_WELDER, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); - this->AddTechNode(BUILD_MINES, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); - this->AddTechNode(RESEARCH_GRENADES, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); - } - else - { - - // Deny skulks so that player can't "re-evolve" to skulks. - this->AddTechNode(ALIEN_LIFEFORM_ONE, TECH_NULL, TECH_PLAYER_UNAVAILABLE, TECH_NULL, false); - - this->AddTechNode(ALIEN_LIFEFORM_TWO, TECH_ONE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_LIFEFORM_THREE, TECH_ONE_LEVEL_TWO, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_LIFEFORM_FOUR, TECH_ONE_LEVEL_THREE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_LIFEFORM_FIVE, TECH_ONE_LEVEL_FOUR, TECH_NULL, TECH_NULL, false); - - this->AddTechNode(ALIEN_HIVE_TWO_UNLOCK, TECH_TWO_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_HIVE_THREE_UNLOCK, TECH_THREE_LEVEL_TWO, TECH_TWO_LEVEL_ONE, TECH_NULL, false); - - this->AddTechNode(ALIEN_EVOLUTION_ONE, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_EVOLUTION_TWO, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_EVOLUTION_THREE, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - - this->AddTechNode(ALIEN_EVOLUTION_TEN, TECH_FOUR_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_EVOLUTION_TWELVE, TECH_FOUR_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_EVOLUTION_ELEVEN, TECH_FOUR_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - - this->AddTechNode(ALIEN_EVOLUTION_SEVEN, TECH_FIVE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_EVOLUTION_EIGHT, TECH_FIVE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - this->AddTechNode(ALIEN_EVOLUTION_NINE, TECH_FIVE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); - } -} - -void AvHGamerules::AwardExperience(AvHPlayer* inPlayer, int inTargetLevel, bool inAwardFriendliesInRange) -{ - PlayerListType thePlayerList; - thePlayerList.push_back(inPlayer); - - if(inAwardFriendliesInRange) - { - // Award experience to player, and any other players nearby - int theExperienceRadius = BALANCE_VAR(kCombatFriendlyNearbyRange); - - // Make list of players to split it between. If a player is at full experience, extra is wasted. - CBaseEntity* theEntity = NULL; - while ((theEntity = UTIL_FindEntityInSphere(theEntity, inPlayer->pev->origin, theExperienceRadius)) != NULL) - { - const char* theClassName = STRING(theEntity->pev->classname); - if(!AvHSUGetIsExternalClassName(theClassName)) - { - AvHPlayer* thePlayer = dynamic_cast(theEntity); - if(thePlayer && (thePlayer != inPlayer) && (thePlayer->pev->team == inPlayer->pev->team) && thePlayer->GetIsRelevant() && !thePlayer->GetIsBeingDigested()) - { - thePlayerList.push_back(thePlayer); - } - } - } - } - - ASSERT(thePlayerList.size() > 0); - - float theExperienceFactor = GetGameRules()->GetIsIronMan() ? BALANCE_VAR(kCombatIronManExperienceScalar) : 1.0f; - - int theExperienceToAward = BALANCE_VAR(kCombatExperienceBaseAward) + inTargetLevel*BALANCE_VAR(kCombatExperienceLevelAward); - - float theExperienceForEach = (theExperienceToAward/(float)thePlayerList.size() + BALANCE_VAR(kCombatExperienceCrowdAward))*theExperienceFactor; - - for(PlayerListType::iterator thePlayerIter = thePlayerList.begin(); thePlayerIter != thePlayerList.end(); thePlayerIter++) - { - AvHPlayer* theCurrentPlayer = (*thePlayerIter); - theCurrentPlayer->SetExperience(theCurrentPlayer->GetExperience() + theExperienceForEach); - } -} +#include "types.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHSharedUtil.h" + +extern int gLevelUpEventID; + +bool AvHPlayer::GiveCombatModeUpgrade(AvHMessageID inMessageID, bool inInstantaneous) +{ + bool theSuccess = false; + bool theEffectivePlayerClassChanged = false; + + // Process every upgrade here + AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; + int thePlayerLevel = this->GetExperienceLevel(); + float theHealthPercentage = this->pev->health/AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, thePlayerLevel); + float theArmorPercentage = this->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); + bool thePreserveHealthArmorPercentage = true; + CBasePlayerItem* theCreatedItem = NULL; + + // Marine upgrades + if(this->GetIsMarine()) + { + Vector thePlayerMinSize, thePlayerMaxSize; + this->GetSize(thePlayerMinSize, thePlayerMaxSize); + + Vector thePlayersFeet = this->pev->origin; + thePlayersFeet.z += thePlayerMinSize.z; + + switch(inMessageID) + { + case BUILD_SHOTGUN: + case BUILD_GRENADE_GUN: + case BUILD_HMG: + case BUILD_WELDER: + case BUILD_MINES: + case BUILD_JETPACK: + case BUILD_HEAVY: + //: spawn the weapon in the middle of nowhere to prevent anyone else from getting it. + theCreatedItem = dynamic_cast(AvHSUBuildTechForPlayer(inMessageID, Vector(9999,9999,9999), this)); + + ASSERT(theCreatedItem); + + if((inMessageID == BUILD_JETPACK) || (inMessageID == BUILD_HEAVY) || (inMessageID == BUILD_HMG) || + (inMessageID == BUILD_SHOTGUN) || (inMessageID == BUILD_GRENADE_GUN)) + { + theEffectivePlayerClassChanged = true; + } + + theSuccess = true; + break; + + case BUILD_SCAN: + AvHSUBuildTechForPlayer(inMessageID, thePlayersFeet, this); + theSuccess = true; + break; + +// case RESEARCH_DISTRESSBEACON: +// AvHSUResearchStarted(this, inMessageID); +// AvHSUResearchComplete(this, inMessageID); +// theSuccess = true; +// break; + + // Give upgrades + case RESEARCH_ARMOR_ONE: + this->GiveTeamUpgrade(RESEARCH_ARMOR_ONE); + theSuccess = true; + break; + + case RESEARCH_ARMOR_TWO: + this->GiveTeamUpgrade(RESEARCH_ARMOR_ONE); + this->GiveTeamUpgrade(RESEARCH_ARMOR_TWO); + theSuccess = true; + break; + + case RESEARCH_ARMOR_THREE: + this->GiveTeamUpgrade(RESEARCH_ARMOR_ONE); + this->GiveTeamUpgrade(RESEARCH_ARMOR_TWO); + this->GiveTeamUpgrade(RESEARCH_ARMOR_THREE); + theSuccess = true; + break; + + case RESEARCH_WEAPONS_ONE: + this->GiveTeamUpgrade(RESEARCH_WEAPONS_ONE); + theSuccess = true; + break; + + case RESEARCH_WEAPONS_TWO: + this->GiveTeamUpgrade(RESEARCH_WEAPONS_ONE); + this->GiveTeamUpgrade(RESEARCH_WEAPONS_TWO); + theSuccess = true; + break; + + case RESEARCH_WEAPONS_THREE: + this->GiveTeamUpgrade(RESEARCH_WEAPONS_ONE); + this->GiveTeamUpgrade(RESEARCH_WEAPONS_TWO); + this->GiveTeamUpgrade(RESEARCH_WEAPONS_THREE); + theSuccess = true; + break; + + case RESEARCH_GRENADES: + this->GiveNamedItem(kwsGrenade); + // NOTE: Fall through below + + case RESEARCH_MOTIONTRACK: + this->GiveTeamUpgrade(inMessageID); + theSuccess = true; + break; + + //case BUILD_AMMO: + //case BUILD_HEALTH: + case BUILD_RESUPPLY: + // Find all friendly players nearby (right now this just resupplies current player) + // Give them health and ammo, equal to # of current level + AvHSUResupplyFriendliesInRange(1, this); + thePreserveHealthArmorPercentage = false; + theSuccess = true; + + // Add new tech node to allow us to buy it again + //AvHTechNode theTechNode(BUILD_RESUPPLY, TECH_FOUR_LEVEL_TWO, TECH_FOUR_LEVEL_TWO, TECH_NULL, 0, 0, true); + //this->mCombatNodes.AddTechNode(theTechNode); + break; + + case BUILD_CAT: + // Don't give out cat-packs every time + AvHCatalyst::GiveCatalyst(this); + theSuccess = true; + break; + } + } + else if(this->GetIsAlien()) + { + theSuccess = this->ExecuteAlienMorphMessage(inMessageID, inInstantaneous); + thePreserveHealthArmorPercentage = false; + } + + if(thePreserveHealthArmorPercentage) + { + this->pev->health = theHealthPercentage*AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, thePlayerLevel); + this->pev->armorvalue = theArmorPercentage*AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); + } + + if(theEffectivePlayerClassChanged) + { + this->EffectivePlayerClassChanged(); + } + + return theSuccess; +} + +float AvHPlayer::GetExperience() const +{ + return this->mExperience; +} + +int AvHPlayer::GetExperienceLevelsSpent() const +{ + return this->mExperienceLevelsSpent; +} + +void AvHPlayer::SetExperienceLevelsSpent(int inSpentLevels) +{ + this->mExperienceLevelsSpent = inSpentLevels; +} + +void AvHPlayer::AwardExperienceForObjective(float inHealthChange, AvHMessageID inMessageID) +{ + bool theAwardExperience = false; + + if(GetGameRules()->GetIsCombatMode()) + { + switch(inMessageID) + { + case ALIEN_BUILD_HIVE: + case BUILD_COMMANDSTATION: + theAwardExperience = true; + break; + } + } + + if(theAwardExperience) + { + int theMaxHealth = GetGameRules()->GetBaseHealthForMessageID(inMessageID); + float thePercentageOfHealth = inHealthChange/theMaxHealth; + int theCombatObjectiveExperienceScalar = BALANCE_VAR(kCombatObjectiveExperienceScalar); + float theExperienceGained = thePercentageOfHealth*theCombatObjectiveExperienceScalar; + this->SetExperience(this->GetExperience() + theExperienceGained); + } +} + +void AvHPlayer::SetExperience(float inExperience) +{ + if(GetGameRules()->GetIsCombatMode()) + { + int theCurrentLevel = AvHPlayerUpgrade::GetPlayerLevel(this->GetExperience()); + + this->mExperience = inExperience; + + // Update server player data in case we get disconnected + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); + if(theServerPlayerData) + { + theServerPlayerData->SetExperience(this->mExperience); + } + } + + int theNewLevel = AvHPlayerUpgrade::GetPlayerLevel(this->GetExperience()); + + if(theCurrentLevel != theNewLevel) + { + int theIsMarine = this->GetIsMarine(); + PLAYBACK_EVENT_FULL(0, this->edict(), gLevelUpEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, theIsMarine, 0, 0, 0 ); + + this->EffectivePlayerClassChanged(); + + // Give player health and armor back on level-up, to allow more soloing, heroics, and reduce dependence on hives/resupply + AvHUser3 theUser3 = AvHUser3(this->pev->iuser3); + float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, theCurrentLevel); + float theHealthPercentage = this->pev->health/theMaxHealth; + + float theLevelUpHealthPercentage = BALANCE_VAR(kCombatLevelupHealthIncreasePercent)/100.0f; + theHealthPercentage = min(theHealthPercentage + theLevelUpHealthPercentage, 1.0f); + this->pev->health = theHealthPercentage*theMaxHealth; + + float theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); + float theArmorPercentage = this->pev->armorvalue/theMaxArmor; + + float theLevelUpArmorPercentage = BALANCE_VAR(kCombatLevelupArmorIncreasePercent)/100.0f; + theArmorPercentage = min(theArmorPercentage + theLevelUpArmorPercentage, 1.0f); + this->pev->armorvalue = theArmorPercentage*theMaxArmor; + + // Unlock tiers as player levels up + // if(theNewLevel >= 4) + // { + // this->mCombatNodes.SetResearchDone(COMBAT_TIER2_UNLOCK); + // } + // if(theNewLevel >= 7) + // { + // this->mCombatNodes.SetResearchDone(COMBAT_TIER3_UNLOCK); + // } + } + } +} + +int AvHPlayer::GetExperienceLevel() const +{ + int theLevel = 1; + + if(GetGameRules()->GetIsCombatMode()) + { + theLevel = AvHPlayerUpgrade::GetPlayerLevel(this->GetExperience()); + } + + return theLevel; +} + +AvHTechTree& AvHPlayer::GetCombatNodes() +{ + return this->mCombatNodes; +} + +MessageIDListType& AvHPlayer::GetPurchasedCombatUpgrades() +{ + return this->mPurchasedCombatUpgrades; +} + +void AvHPlayer::SetCombatNodes(const AvHTechTree& inTechNodes) +{ + this->mCombatNodes = inTechNodes; +} + +void AvHPlayer::GiveCombatUpgradesOnSpawn() +{ + // Save off previously-spent upgrades and respend them + MessageIDListType theUpgrades = this->mGiveCombatUpgrades; + + // Need to run through these in order + for(MessageIDListType::iterator theIter = theUpgrades.begin(); theIter != theUpgrades.end(); theIter++) + { + AvHMessageID theCurrentCombatUpgrade = *theIter; + if(theCurrentCombatUpgrade != MESSAGE_NULL) + { + this->GiveCombatModeUpgrade(theCurrentCombatUpgrade, true); + } + } +} + +void AvHPlayer::PurchaseCombatUpgrade(AvHMessageID inMessageID) +{ + this->mPurchasedCombatUpgrades.push_back(inMessageID); + //this->mExperienceLevelsSpent++; + + // Remove any upgrades that this prempts + bool theOneShot = false; + switch(inMessageID) + { + case BUILD_AMMO: + case BUILD_HEALTH: + case BUILD_RESUPPLY: + case BUILD_CAT: + case BUILD_SCAN: + //case BUILD_MINES: + //case RESEARCH_DISTRESSBEACON: + theOneShot = true; + break; + } + + // Don't add it as a permanent upgrade to get every time we spawn + if(!theOneShot) + { + this->mGiveCombatUpgrades.push_back(inMessageID); + } + + this->RemoveCombatUpgradesPremptedBy(inMessageID); +} + +void AvHPlayer::RemoveCombatUpgrade(AvHMessageID inMessageID) +{ + MessageIDListType::iterator theIter = std::find(this->mGiveCombatUpgrades.begin(), this->mGiveCombatUpgrades.end(), inMessageID); + if(theIter != this->mGiveCombatUpgrades.end()) + { + this->mGiveCombatUpgrades.erase(theIter); + + // Take away the upgrade + this->GiveTeamUpgrade(inMessageID, false); + } +} + +void AvHPlayer::RemoveCombatUpgradesPremptedBy(AvHMessageID inMessageID) +{ + switch(inMessageID) + { + case BUILD_JETPACK: + this->RemoveCombatUpgrade(BUILD_HEAVY); + break; + case BUILD_HEAVY: + this->RemoveCombatUpgrade(BUILD_JETPACK); + break; + case BUILD_HMG: + this->RemoveCombatUpgrade(BUILD_SHOTGUN); + this->RemoveCombatUpgrade(BUILD_GRENADE_GUN); + break; + case BUILD_GRENADE_GUN: + this->RemoveCombatUpgrade(BUILD_SHOTGUN); + this->RemoveCombatUpgrade(BUILD_HMG); + break; + case ALIEN_LIFEFORM_TWO: + this->RemoveCombatUpgrade(ALIEN_LIFEFORM_ONE); + break; + case ALIEN_LIFEFORM_THREE: + this->RemoveCombatUpgrade(ALIEN_LIFEFORM_TWO); + break; + case ALIEN_LIFEFORM_FOUR: + this->RemoveCombatUpgrade(ALIEN_LIFEFORM_THREE); + break; + case ALIEN_LIFEFORM_FIVE: + this->RemoveCombatUpgrade(ALIEN_LIFEFORM_FOUR); + break; + + //case BUILD_WELDER + // this->RemoveCombatUpgrade(BUILD_MINES); + // break; + //case BUILD_MINES: + // this->RemoveCombatUpgrade(BUILD_WELDER); + // break; + } +} + +void AvHPlayer::ProcessCombatDeath() +{ + // Unspend lifeform (return to skulk, but get the levels spent on lifeform back) + if(this->GetIsAlien()) + { + AvHMessageID theLifeformID = MESSAGE_NULL; + AvHUser3 theUser3 = this->GetUser3(false); + if(theUser3 == AVH_USER3_ALIEN_EMBRYO) + { + // Use the last lifeform we were, unless we're evolving into a new one (assumes only one lifeform change per life) + theUser3 = this->GetPreviousUser3(false); + switch(this->mEvolution) + { + case ALIEN_LIFEFORM_TWO: + theUser3 = AVH_USER3_ALIEN_PLAYER2; + break; + case ALIEN_LIFEFORM_THREE: + theUser3 = AVH_USER3_ALIEN_PLAYER3; + break; + case ALIEN_LIFEFORM_FOUR: + theUser3 = AVH_USER3_ALIEN_PLAYER4; + break; + case ALIEN_LIFEFORM_FIVE: + theUser3 = AVH_USER3_ALIEN_PLAYER5; + break; + } + } + + AvHSHUUser3ToMessageID(theUser3, theLifeformID); + ASSERT(theLifeformID != MESSAGE_NULL); + + int theLifeformCost = GetGameRules()->GetCostForMessageID(theLifeformID); + this->SetExperienceLevelsSpent(this->GetExperienceLevelsSpent() - theLifeformCost); + + this->RemoveCombatUpgrade(theLifeformID); + + // Make all lifeforms chooseable again + this->SetLifeformCombatNodesAvailable(true); + } +} + +void AvHPlayer::SetLifeformCombatNodesAvailable(bool inAvailable) +{ + MessageIDListType theLifeformList; + theLifeformList.push_back(ALIEN_LIFEFORM_ONE); + theLifeformList.push_back(ALIEN_LIFEFORM_TWO); + theLifeformList.push_back(ALIEN_LIFEFORM_THREE); + theLifeformList.push_back(ALIEN_LIFEFORM_FOUR); + theLifeformList.push_back(ALIEN_LIFEFORM_FIVE); + + for(MessageIDListType::iterator theIter = theLifeformList.begin(); theIter != theLifeformList.end(); theIter++) + { + AvHMessageID theLifeformID = *theIter; + this->mCombatNodes.SetIsResearchable(theLifeformID, inAvailable); + this->mCombatNodes.SetResearchDone(theLifeformID, !inAvailable); + } +} + +bool AvHPlayer::ExecuteCombatMessage(AvHMessageID inMessageID, bool& outIsAvailable, bool inForce) +{ + bool theMessageExecuted = false; + + // Only explicitly deny messages in the tech tree, let the others fall back to NS-mode handling + if(this->mCombatNodes.GetIsMessageInTechTree(inMessageID)) + { + if(this->mCombatNodes.GetIsMessageAvailable(inMessageID) || inForce) + { + if(this->GiveCombatModeUpgrade(inMessageID)) + { + theMessageExecuted = true; + } + } + else + { + // Explicitly deny + outIsAvailable = false; + } + } + + return theMessageExecuted; +} + +bool AvHPlayer::GetHasCombatModeUpgrade(AvHMessageID inMessageID) const +{ + bool theHasUpgrade = false; + + MessageIDListType::const_iterator theIter = std::find(this->mPurchasedCombatUpgrades.begin(), this->mPurchasedCombatUpgrades.end(), inMessageID); + if(theIter != this->mPurchasedCombatUpgrades.end()) + { + theHasUpgrade = true; + } + + return theHasUpgrade; +} + +void AvHPlayer::InternalCombatThink() +{ + // Only update every so often + if(GetGameRules()->GetIsCombatMode()) + { + // Save our data in case we get kicked off + AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); + if(theServerPlayerData) + { + theServerPlayerData->SetCombatNodes(this->mCombatNodes); + theServerPlayerData->SetPurchasedCombatUpgrades(this->mPurchasedCombatUpgrades); + theServerPlayerData->SetExperienceLevelsSpent(this->mExperienceLevelsSpent); + } + + // If it's time for an update + float theCurrentTime = gpGlobals->time; + const float theCombatThinkInterval = BALANCE_VAR(kCombatThinkInterval); + + // Give support from a fake commander + if(this->GetIsMarine() && this->GetIsRelevant() && !this->GetIsBeingDigested()) + { + if(this->mTimeOfLastCombatThink == 0 || (theCurrentTime > (this->mTimeOfLastCombatThink + theCombatThinkInterval))) + { + // Only allow one upgrade per think + bool theSuccess = false; + + // Does player have resupply upgrade? + if(this->GetHasCombatModeUpgrade(BUILD_RESUPPLY)) + { + // Do they need it? + float theHealthPercentage = this->pev->health/AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + // float theAmmoPercentage = this->GetCurrentWeaponAmmoPercentage(); // changed to fix #542 + bool theAmmoResupply = this->GetShouldResupplyAmmo(); + + if((theHealthPercentage < BALANCE_VAR(kCombatResupplyHealthPercentage)) || theAmmoResupply) //(theAmmoPercentage < BALANCE_VAR(kCombatResupplyAmmoPercentage))) + { + // Resupply player + this->GiveCombatModeUpgrade(BUILD_RESUPPLY); + theSuccess = true; + } + } + + // Does player have resupply upgrade? + if(this->GetHasCombatModeUpgrade(BUILD_CAT)) + { + // Catalyst player after he gets a kill + if(this->pev->frags > this->mSavedCombatFrags) + { + //if(RANDOM_LONG(0, 1) == 0) + //{ + AvHCatalyst::GiveCatalyst(this); + theSuccess = true; + //} + } + } + this->mSavedCombatFrags = this->pev->frags; + + // Does player have scan upgrade? + if(!theSuccess && this->GetHasCombatModeUpgrade(BUILD_SCAN) && !this->GetIsBeingDigested()) + { + // Needed if there is a cloaked enemy nearby + bool theCloakedEnemyNearby = false; + + // Look in sphere for cloakables + CBaseEntity* theSphereEntity = NULL; + while ((theSphereEntity = UTIL_FindEntityInSphere(theSphereEntity, this->pev->origin, BALANCE_VAR(kScanRadius))) != NULL) + { + if(!AvHSUGetIsExternalClassName(STRING(theSphereEntity->pev->classname))) + { + AvHCloakable* theCloakable = dynamic_cast(theSphereEntity); + if(theCloakable && theCloakable->GetIsPartiallyCloaked() && (theSphereEntity->pev->team != this->pev->team)) + { + //if(this->GetIsEntityInSight(theSphereEntity)) + //{ + theCloakedEnemyNearby = true; + break; + //} + } + } + } + + // Lucky enough to receive? + if(theCloakedEnemyNearby /*&& (RANDOM_LONG(0, 1) == 0)*/) + { + // Scan + this->GiveCombatModeUpgrade(BUILD_SCAN); + theSuccess = true; + } + } + +// // Does player have distress beacon? +// if(!theSuccess && this->GetHasCombatModeUpgrade(RESEARCH_DISTRESSBEACON)) +// { +// // Needed? +// int theNumTeammatesWaitingToSpawn = 0; +// FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*); +// if(theEntity->GetTeam() == this->GetTeam()) +// { +// if(theEntity->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) +// { +// theNumTeammatesWaitingToSpawn++; +// } +// } +// END_FOR_ALL_ENTITIES(kAvHPlayerClassName); +// +// // Lucky enough? +// int theNumPlayersOnTeam = 0; +// AvHTeam* theTeamPointer = this->GetTeamPointer(); +// if(theTeamPointer) +// { +// theNumPlayersOnTeam = theTeamPointer->GetPlayerCount(); +// } +// +// float theDeadPercentage = (float)theNumTeammatesWaitingToSpawn/theNumPlayersOnTeam; +// if(theDeadPercentage > BALANCE_VAR(kCombatDistressBeaconDeadPercentage)) +// { +// // Distress! +// this->GiveCombatModeUpgrade(RESEARCH_DISTRESSBEACON); +// theSuccess = true; +// } +// } + + // Update time + this->mTimeOfLastCombatThink = theCurrentTime; + } + } + else if(this->GetIsAlien() && this->GetIsRelevant()) + { + // Give aliens experience slowly over time + if(this->mTimeOfLastCombatThink == 0 || (theCurrentTime > (this->mTimeOfLastCombatThink + theCombatThinkInterval))) + { + float theExperienceRate = BALANCE_VAR(kCombatExperienceAlienGrowthRate); + float theExperienceGained = theCombatThinkInterval*theExperienceRate; + this->SetExperience(this->GetExperience() + theExperienceGained); + + // Update time + this->mTimeOfLastCombatThink = theCurrentTime; + } + } + } +} + + +void AvHTeam::InitializeCombatTechNodes() +{ + // If team is marine + if(this->mTeamType == AVH_CLASS_TYPE_MARINE) + { + this->AddTechNode(RESEARCH_WEAPONS_ONE, TECH_ONE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(RESEARCH_WEAPONS_TWO, TECH_ONE_LEVEL_TWO, TECH_ONE_LEVEL_ONE, TECH_NULL, false); + this->AddTechNode(RESEARCH_WEAPONS_THREE, TECH_NULL, TECH_ONE_LEVEL_TWO, TECH_NULL, false); + this->AddTechNode(BUILD_SHOTGUN, TECH_ONE_LEVEL_THREE, TECH_ONE_LEVEL_ONE, TECH_NULL, false); + this->AddTechNode(BUILD_GRENADE_GUN, TECH_NULL, TECH_ONE_LEVEL_THREE, TECH_NULL, false); + this->AddTechNode(BUILD_HMG, TECH_NULL, TECH_ONE_LEVEL_THREE, TECH_NULL, false); + + this->AddTechNode(RESEARCH_ARMOR_ONE, TECH_TWO_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(RESEARCH_ARMOR_TWO, TECH_TWO_LEVEL_TWO, TECH_TWO_LEVEL_ONE, TECH_NULL, false); + this->AddTechNode(RESEARCH_ARMOR_THREE, TECH_NULL, TECH_TWO_LEVEL_TWO, TECH_NULL, false); + this->AddTechNode(BUILD_JETPACK, TECH_NULL, TECH_TWO_LEVEL_TWO, TECH_NULL, false); + this->AddTechNode(BUILD_HEAVY, TECH_NULL, TECH_TWO_LEVEL_TWO, TECH_NULL, false); + + this->AddTechNode(BUILD_SCAN, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); + this->AddTechNode(RESEARCH_MOTIONTRACK, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + //this->AddTechNode(RESEARCH_DISTRESSBEACON, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, true, false); + + this->AddTechNode(BUILD_RESUPPLY, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); + this->AddTechNode(BUILD_CAT, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); + + this->AddTechNode(BUILD_WELDER, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); + this->AddTechNode(BUILD_MINES, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); + this->AddTechNode(RESEARCH_GRENADES, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false, false); + } + else + { + + // Deny skulks so that player can't "re-evolve" to skulks. + this->AddTechNode(ALIEN_LIFEFORM_ONE, TECH_NULL, TECH_PLAYER_UNAVAILABLE, TECH_NULL, false); + + this->AddTechNode(ALIEN_LIFEFORM_TWO, TECH_ONE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_LIFEFORM_THREE, TECH_ONE_LEVEL_TWO, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_LIFEFORM_FOUR, TECH_ONE_LEVEL_THREE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_LIFEFORM_FIVE, TECH_ONE_LEVEL_FOUR, TECH_NULL, TECH_NULL, false); + + this->AddTechNode(ALIEN_HIVE_TWO_UNLOCK, TECH_TWO_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_HIVE_THREE_UNLOCK, TECH_THREE_LEVEL_TWO, TECH_TWO_LEVEL_ONE, TECH_NULL, false); + + this->AddTechNode(ALIEN_EVOLUTION_ONE, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_EVOLUTION_TWO, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_EVOLUTION_THREE, TECH_THREE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + + this->AddTechNode(ALIEN_EVOLUTION_TEN, TECH_FOUR_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_EVOLUTION_TWELVE, TECH_FOUR_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_EVOLUTION_ELEVEN, TECH_FOUR_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + + this->AddTechNode(ALIEN_EVOLUTION_SEVEN, TECH_FIVE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_EVOLUTION_EIGHT, TECH_FIVE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + this->AddTechNode(ALIEN_EVOLUTION_NINE, TECH_FIVE_LEVEL_ONE, TECH_NULL, TECH_NULL, false); + } +} + +void AvHGamerules::AwardExperience(AvHPlayer* inPlayer, int inTargetLevel, bool inAwardFriendliesInRange) +{ + PlayerListType thePlayerList; + thePlayerList.push_back(inPlayer); + + if(inAwardFriendliesInRange) + { + // Award experience to player, and any other players nearby + int theExperienceRadius = BALANCE_VAR(kCombatFriendlyNearbyRange); + + // Make list of players to split it between. If a player is at full experience, extra is wasted. + CBaseEntity* theEntity = NULL; + while ((theEntity = UTIL_FindEntityInSphere(theEntity, inPlayer->pev->origin, theExperienceRadius)) != NULL) + { + const char* theClassName = STRING(theEntity->pev->classname); + if(!AvHSUGetIsExternalClassName(theClassName)) + { + AvHPlayer* thePlayer = dynamic_cast(theEntity); + if(thePlayer && (thePlayer != inPlayer) && (thePlayer->pev->team == inPlayer->pev->team) && thePlayer->GetIsRelevant() && !thePlayer->GetIsBeingDigested()) + { + thePlayerList.push_back(thePlayer); + } + } + } + } + + ASSERT(thePlayerList.size() > 0); + + float theExperienceFactor = GetGameRules()->GetIsIronMan() ? BALANCE_VAR(kCombatIronManExperienceScalar) : 1.0f; + + int theExperienceToAward = BALANCE_VAR(kCombatExperienceBaseAward) + inTargetLevel*BALANCE_VAR(kCombatExperienceLevelAward); + + float theExperienceForEach = (theExperienceToAward/(float)thePlayerList.size() + BALANCE_VAR(kCombatExperienceCrowdAward))*theExperienceFactor; + + for(PlayerListType::iterator thePlayerIter = thePlayerList.begin(); thePlayerIter != thePlayerList.end(); thePlayerIter++) + { + AvHPlayer* theCurrentPlayer = (*thePlayerIter); + theCurrentPlayer->SetExperience(theCurrentPlayer->GetExperience() + theExperienceForEach); + } +} diff --git a/main/source/mod/AvHCommandConstants.h b/main/source/mod/AvHCommandConstants.h index 7900670..b0a0608 100644 --- a/main/source/mod/AvHCommandConstants.h +++ b/main/source/mod/AvHCommandConstants.h @@ -19,6 +19,7 @@ // Commands //#define kcBuyReinforcements "buyreinforcements" #define kcReadyRoom "readyroom" +#define kcMenuReadyRoom "menureadyroom" #define kcJoinTeamOne "jointeamone" #define kcJoinTeamTwo "jointeamtwo" #define kcJoinTeamThree "jointeamthree" @@ -82,6 +83,7 @@ #define kcOrderSelf "orderself" #define kcParasite "parasite" #define kcKillAll "killall" +#define kcAuth "auth" #define kcStun "stun" #define kcBoxes "boxes" #define kcOverflow "overflow" diff --git a/main/source/mod/AvHConsoleCommands.cpp b/main/source/mod/AvHConsoleCommands.cpp index 10fba8c..eca274e 100644 --- a/main/source/mod/AvHConsoleCommands.cpp +++ b/main/source/mod/AvHConsoleCommands.cpp @@ -1,1626 +1,1641 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHConsoleCommands.cpp$ -// $Date: 2002/11/22 21:28:16 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHConsoleCommands.cpp,v $ -// Revision 1.31 2002/11/22 21:28:16 Flayra -// - mp_consistency changes -// -// Revision 1.30 2002/11/15 04:46:53 Flayra -// - Utility command -// -// Revision 1.29 2002/11/03 04:49:56 Flayra -// - Team balance fixes -// -// Revision 1.28 2002/10/28 20:33:55 Flayra -// - Added confirmation to auth command -// -// Revision 1.27 2002/10/24 21:22:40 Flayra -// - Commented out adjust score cheat for release (even with cheats on, this is abusive) -// -// Revision 1.26 2002/10/20 16:45:19 Flayra -// - Linux update -// -// Revision 1.25 2002/10/20 16:35:46 Flayra -// - Added hack code to create a fake client to test profiling -// -// Revision 1.24 2002/10/18 22:17:12 Flayra -// - Added redeem cheat -// -// Revision 1.23 2002/10/16 00:51:33 Flayra -// - Added auth command and setauth cheat -// - Require cheats for some dev commands (oops) -// -// Revision 1.22 2002/10/03 18:41:35 Flayra -// - Allow setskin to work without chets, for testing -// -// Revision 1.21 2002/09/23 22:11:46 Flayra -// - Regular update -// -// Revision 1.20 2002/09/09 19:49:25 Flayra -// - Added deathmessage cheat -// -// Revision 1.19 2002/08/16 02:33:32 Flayra -// - Added endgame cheat to end game for either team (to test endgame music) -// -// Revision 1.18 2002/08/09 00:56:11 Flayra -// - Added "adjustscore" cheat for testing new scoreboard -// -// Revision 1.17 2002/08/02 22:01:27 Flayra -// - Added some new cheats, refactored cheat names, changes for new alert system -// -// Revision 1.16 2002/07/10 14:39:24 Flayra -// - Added "overflow" cheat for debugging -// -// Revision 1.15 2002/07/08 16:49:34 Flayra -// - Unhacked this to fix some random bug -// -// Revision 1.14 2002/06/03 16:41:19 Flayra -// - Added "invul" cheat -// -// Revision 1.13 2002/05/23 02:34:00 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "util/nowarnings.h" -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "dlls/cbase.h" -#include "dlls/player.h" -#include "dlls/weapons.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHConstants.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHEntities.h" -#include "mod/AvHParticleTemplateServer.h" -#include "mod/AvHPlayer.h" -#include "dlls/client.h" -#include "dlls/game.h" -#include "dlls/util.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHMessage.h" -#include "mod/AvHSoundListManager.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHTitles.h" -#include "mod/AvHParticleSystemManager.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHMovementUtil.h" -#include "mod/AvHSharedUtil.h" -#include "textrep/TRFactory.h" -#include "mod/AvHEntities.h" -#include "mod/AvHScriptManager.h" -#include "mod/AvHCommandConstants.h" -#include "mod/AvHPlayerUpgrade.h" -#include -#include "game_shared/voice_gamemgr.h" -#include "mod/AvHNetworkMessages.h" -#include "mod/AvHNexusServer.h" - -extern AvHParticleTemplateListServer gParticleTemplateList; -extern CVoiceGameMgr g_VoiceGameMgr; -extern int gCommanderPointsAwardedEventID; -extern cvar_t allow_spectators; -extern cvar_t avh_tournamentmode; -extern int kNumEntsProcessedForPlayerOne; - -#ifdef WIN32 -// this is the LINK_ENTITY_TO_CLASS function that creates a player (bot) -HINSTANCE h_Library = NULL; -typedef void (*LINK_ENTITY_FUNC)(entvars_t *); - -void player( entvars_t *pev ) -{ - if(!h_Library) - { - string libName = string(getModDirectory()) + "\\dlls\\ns.dll"; - h_Library = LoadLibrary(libName.c_str()); - } - - static LINK_ENTITY_FUNC otherClassName = NULL; - if (otherClassName == NULL) - otherClassName = (LINK_ENTITY_FUNC)GetProcAddress(h_Library, "player"); - if (otherClassName != NULL) - { - (*otherClassName)(pev); - } -} -#endif - -//------------------------------------------------------------------ -// begin future MapUtils.h / MapUtils.cpp -//------------------------------------------------------------------ -typedef std::pair EntityInfoNodeType; -typedef std::map EntityInfoMapType; -typedef std::vector EntityInfoVectorType; - -bool EntityInfoSortFunction(const EntityInfoNodeType& left,const EntityInfoNodeType& right) -{ - if(left.second > right.second) - { return true; } - if(left.first.compare(right.first) < 0) - { return true; } - return false; -} - -bool EntityInfoShouldCount(const string& inClassName) -{ - bool theShouldCount = true; - if(inClassName.empty()) // unassigned player slot - { - theShouldCount = false; - } - else if(inClassName.compare(0,7,"weapon_") == 0) - { - theShouldCount = false; - } - else if(inClassName.compare("bodyque") == 0) - { - theShouldCount = false; - } - else if(inClassName.compare("player") == 0) - { - theShouldCount = false; - } - return theShouldCount; -} - -EntityInfoVectorType EntityInfoGetVector(void) -{ - EntityInfoMapType theMap; - const char* theClassName; - string theClassNameString; - - for(int index = 0; index < gpGlobals->maxEntities; ++index) - { - edict_t* theEdict = INDEXENT(index); - if(!FNullEnt(theEdict)) - { - theClassName = STRING(theEdict->v.classname); - if(theClassName) - { - ++theMap[string(theClassName)]; - } - } - } - - EntityInfoVectorType theVector; - - EntityInfoMapType::iterator end = theMap.end(); - for(EntityInfoMapType::iterator current = theMap.begin(); current != end; ++current) - { - if(EntityInfoShouldCount(current->first)) - { - theVector.push_back(EntityInfoNodeType(current->first,current->second)); - } - } - - //sort it - sort(theVector.begin(),theVector.end(),EntityInfoSortFunction); - - return theVector; -} - -int EntityInfoGetCount() -{ - int theCount = 0; - EntityInfoVectorType theVector = EntityInfoGetVector(); - EntityInfoVectorType::iterator end = theVector.end(); - for(EntityInfoVectorType::iterator current = theVector.begin(); current != end; ++current) - { - theCount += current->second; - } - return theCount; -} -//------------------------------------------------------------------ -// end future MapUtils.h / MapUtils.cpp -//------------------------------------------------------------------ - - -void ReportPlayer(CBasePlayer* inPlayer, const char* inCommand) -{ - #ifdef AVH_PLAYTEST_BUILD - // Tell the server that this player executed x message - char* theMessage = UTIL_VarArgs("%s initiated command \"%s\"\n", STRING(inPlayer->pev->netname), inCommand); - UTIL_ClientPrintAll(HUD_PRINTNOTIFY, theMessage); - UTIL_LogPrintf(theMessage); - #endif -} - -BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd ) -{ -//adding Nexus TunnelToClient functionality up here... - if( strcmp( pcmd, "NexusData" ) == 0 ) - { - const char* arg1 = CMD_ARGV(1); - return AvHNexus::recv(pPlayer->pev,arg1,strlen(arg1)); - } -//non-Nexus signal handler down here... - - AvHPlayer* theAvHPlayer = dynamic_cast(pPlayer); - AvHTeam* theTeam = NULL; - bool theSuccess = false; - bool theIsDeveloper = false; - bool theIsGuide = false; - bool theIsServerOp = false; - bool theIsPlaytester = false; - bool theIsPlayerHelper = false; - bool theIsPlaytest = false; - bool theReportPlayer = false; - bool theIsDedicatedServer = false; - bool theIsDebug = false; - - #ifdef DEBUG - theIsDebug = true; - #endif - - if(theAvHPlayer) - { - theTeam = theAvHPlayer->GetTeamPointer(); - theIsDeveloper = theAvHPlayer->GetIsMember(PLAYERAUTH_DEVELOPER); - theIsGuide = theAvHPlayer->GetIsMember(PLAYERAUTH_GUIDE); - theIsPlaytester = theAvHPlayer->GetIsMember(PLAYERAUTH_PLAYTESTER); - theIsPlayerHelper = theIsDeveloper || theIsGuide || theIsPlaytester; - - #ifdef AVH_PLAYTEST_BUILD - theIsPlaytest = theIsPlaytester || theIsDeveloper; - #endif - } - else - { - theIsDedicatedServer = true; - } - - if( !theAvHPlayer || theAvHPlayer->GetIsMember(PLAYERAUTH_SERVEROP) ) - { - theIsServerOp = true; - } - - if(g_VoiceGameMgr.ClientCommand(pPlayer, pcmd)) - { - theSuccess = true; - } - else if ( FStrEq( pcmd, kcJoinTeamOne ) ) - { - if(theAvHPlayer) - { - this->AttemptToJoinTeam(theAvHPlayer, TEAM_ONE, true); - theSuccess = true; - } - } - else if ( FStrEq( pcmd, kcJoinTeamTwo ) ) - { - if(theAvHPlayer) - { - this->AttemptToJoinTeam(theAvHPlayer, TEAM_TWO, true); - theSuccess = true; - } - } - else if ( FStrEq( pcmd, kcJoinTeamThree ) ) - { - if(theAvHPlayer) - { - this->AttemptToJoinTeam(theAvHPlayer, TEAM_THREE, true); - theSuccess = true; - } - } - else if ( FStrEq( pcmd, kcJoinTeamFour ) ) - { - if(theAvHPlayer) - { - this->AttemptToJoinTeam(theAvHPlayer, TEAM_FOUR, true); - theSuccess = true; - } - } - else if ( FStrEq( pcmd, kcAutoAssign ) ) - { - if(theAvHPlayer) - { - this->AutoAssignPlayer(theAvHPlayer); - theSuccess = true; - } - } - else if ( FStrEq( pcmd, kcReadyRoom ) ) - { - if(theAvHPlayer) - { - if(!(theAvHPlayer->pev->flags & FL_FAKECLIENT)) - { - if(!theAvHPlayer->GetIsBeingDigested()) - { - // puzl: 984 - // Add a throttle on the readyroom key - const static int kReadyRoomThrottleTimeout=2.0f; - if ( (theAvHPlayer->GetTimeLastF4() == -1.0f) || - (gpGlobals->time > theAvHPlayer->GetTimeLastF4() + kReadyRoomThrottleTimeout) ) - { - theAvHPlayer->SendMessage(kReadyRoomThrottleMessage); - theAvHPlayer->SetTimeLastF4(gpGlobals->time); - } - else if ( gpGlobals->time < theAvHPlayer->GetTimeLastF4() + kReadyRoomThrottleTimeout ) - { - theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true); - } - } - theSuccess = true; - } - } - theSuccess = true; - } - else if (FStrEq(pcmd, kcStartGame)) - { - if(this->GetCheatsEnabled()) - { - this->SetGameStarted(true); - } - theSuccess = true; - } - else if(FStrEq(pcmd, kcSwitch)) - { - if(this->GetCheatsEnabled() || theIsPlaytest) - { - AvHTeamNumber thePlayerTeam = theAvHPlayer->GetTeam(); - if(thePlayerTeam != TEAM_IND) - { - ReportPlayer(theAvHPlayer, kcSwitch); - AvHTeamNumber teamA = GetGameRules()->GetTeamA()->GetTeamNumber(); - AvHTeamNumber teamB = GetGameRules()->GetTeamB()->GetTeamNumber(); - - // Get other team - AvHTeamNumber theOtherTeamNumber = (thePlayerTeam == teamA) ? teamB : teamA; - - // Remember current position - Vector theCurrentPosition = theAvHPlayer->pev->origin; - Vector theCurrentAngles = theAvHPlayer->pev->angles; - - // Switch teams - theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true); - this->AttemptToJoinTeam(theAvHPlayer, theOtherTeamNumber, false); - - // tankefugl: 0001010 - Boost the player 32 units up to avoid sticking in the ground - theCurrentPosition[2] += 32.0f; - - // Set our position again - theAvHPlayer->pev->origin = theCurrentPosition; - theAvHPlayer->pev->angles = theCurrentAngles; - theAvHPlayer->pev->fixangle = 1; - } - - theSuccess = true; - } - } - else if(FStrEq(pcmd, "givexp")) - { - if(this->GetCheatsEnabled()) - { - AvHPlayer* thePlayer = dynamic_cast(theAvHPlayer->GetSpectatingEntity()); - if(!thePlayer) - { - thePlayer = theAvHPlayer; - } - - if(thePlayer && thePlayer->GetIsRelevant()) - { - int theTargetLevel = 1; - - int theNumArgs = CMD_ARGC(); - if(theNumArgs == 2) - { - sscanf(CMD_ARGV(1), "%d", &theTargetLevel); - } - - GetGameRules()->AwardExperience(thePlayer, theTargetLevel); - - theSuccess = true; - } - } - } - else if(FStrEq(pcmd, kcRestartRound) || FStrEq(pcmd, kcRestart)) - { - if(!theAvHPlayer || theIsServerOp || theIsPlaytest || theIsDedicatedServer || theIsDebug || this->GetCheatsEnabled()) - { - if(theAvHPlayer) - { - ReportPlayer(theAvHPlayer, pcmd); - } - - // Reset game world - this->ResetGame(true); - } - theSuccess = true; - } - #ifdef AVH_PLAYTEST_BUILD - else if(FStrEq(pcmd, kcTournyMode)) - { - if(theIsPlaytest) - { - char theCommand[128]; - sprintf(theCommand, "%s %d\n", kvTournamentMode, !((int)avh_tournamentmode.value)); - SERVER_COMMAND(theCommand); - - theSuccess = true; - } - } - #endif -// puzl: 0001073 - #ifdef USE_OLDAUTH - else if(FStrEq(pcmd, "forceuplink")) - { - if(theIsDeveloper || theIsDedicatedServer) - { - this->InitializeAuthentication(); - UTIL_SayText("Initialized authentication.", theAvHPlayer); - } - theSuccess = true; - } - # endif - else if(FStrEq(pcmd, kcSetBalanceVar)) - { - if( theAvHPlayer && theAvHPlayer->GetIsAuthorized(AUTH_ACTION_ADJUST_BALANCE,0) ) - { - BalanceValueContainer* container = BalanceValueContainerFactory::get(BalanceValueContainerFactory::getDefaultFilename()); - int theNumArgs = CMD_ARGC(); - if( theNumArgs == 3 ) - { - string name(CMD_ARGV(1)); - string value(CMD_ARGV(2)); - if( value.at(0) == '"' ) - { - container->insert(name,value.substr(1,value.length()-1)); - } - else if( value.at(value.length()-1) == 'f' || value.find('.') != string::npos ) - { - float fvalue = (float)atof(value.c_str()); - container->insert(name,fvalue); - } - else - { - int ivalue = atoi(value.c_str()); - container->insert(name,ivalue); - } - } - } - - theSuccess = true; - } - else if(FStrEq(pcmd, kcNSChangeLevel)) - { - if(theIsServerOp || theIsPlaytest) - { - char theLevelName[1024]; - if(sscanf(CMD_ARGV(1), "%s", theLevelName) == 1) - { - ReportPlayer(theAvHPlayer, pcmd); - CHANGE_LEVEL(theLevelName, NULL ); - } - else - { - UTIL_SayText("Usage: changelevel ", theAvHPlayer); - } - } - else - { - UTIL_SayText("You're not authorized to changelevel.", theAvHPlayer); - } - - theSuccess = true; - } - else if (FStrEq(pcmd, kcTestEvent)) - { - if(theAvHPlayer && this->GetCheatsEnabled()) - { - vec3_t theOrigin = theAvHPlayer->pev->origin; - theOrigin.z -= 500; - int theFlags = 0;//FEV_GLOBAL;// | FEV_HOSTONLY; - PLAYBACK_EVENT_FULL(theFlags, theAvHPlayer->edict(), gCommanderPointsAwardedEventID, 0, theOrigin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcGetNumPlayers)) - { - if(theAvHPlayer && this->GetCheatsEnabled()) - { - int theNumPlayers = GetGameRules()->GetNumberOfPlayers(); - char theMessage[128]; - sprintf(theMessage, "Num players: %d\n", theNumPlayers); - UTIL_SayText(theMessage, theAvHPlayer); - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcAddCat)) - { - if(theAvHPlayer && (this->GetCheatsEnabled() || theIsPlaytest)) - { - AvHTeam* theTeam = theAvHPlayer->GetTeamPointer(); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN)) - { - ReportPlayer(theAvHPlayer, pcmd); - - int theCategory = 0; - sscanf(CMD_ARGV(1), "%d", &theCategory); - AvHAlienUpgradeCategory theUpgradeCategory = AvHAlienUpgradeCategory(theCategory); - - theTeam->AddTeamUpgrade(theUpgradeCategory); - - theSuccess = true; - } - } - } - else if (FStrEq(pcmd, kcSendMessage)) - { - if(theAvHPlayer) - { - if(this->GetCheatsEnabled()) - { - if(CMD_ARGC() >= 3) - { - char theTooltipText[512]; - sscanf(CMD_ARGV(1), "%s", theTooltipText); - - int theMode = 0; - sscanf(CMD_ARGV(2), "%d", &theMode); - - if(theMode == 0) - { - UTIL_ShowMessage(theTooltipText, theAvHPlayer); - } - else if(theMode == 1) - { - UTIL_Error(theAvHPlayer, theTooltipText); - } - else - { - bool theIsToolTip = (theMode == 2); - - theAvHPlayer->SendMessage(theTooltipText, (theIsToolTip)?TOOLTIP:NORMAL); - } - - theSuccess = true; - } - } - } - } - else if (FStrEq(pcmd, kcTooltip)) - { - if(theAvHPlayer && theIsPlayerHelper) - { - char thePlayerName[512]; - sscanf(CMD_ARGV(1), "%s", thePlayerName); - - // Read first word as target player - AvHPlayer* theTargetPlayer = NULL; - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - const char* thePlayerCStrName = STRING(theEntity->pev->netname); - if(!strcmp(thePlayerCStrName, thePlayerName)) - { - theTargetPlayer = theEntity; - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - if(theTargetPlayer) - { - string theToolTip; - theToolTip = string(STRING(theAvHPlayer->pev->netname)); - theToolTip += ":"; - - int theNumArgs = CMD_ARGC(); - - for(int i=0; i < theNumArgs - 2; i++) - { - theToolTip += string(" "); - char theTooltipText[512]; - sscanf(CMD_ARGV(i+2), "%s ", theTooltipText); - theToolTip += string(theTooltipText); - - // For some reason, some punctuation comes through as separate arguments - //if(strcmp(theTooltipText, ",")) - //{ - // theToolTip += string(" "); - //} - } - - theTargetPlayer->SendMessage(theToolTip.c_str(), TOOLTIP); - theSuccess = true; - } - else - { - char theErrorMessage[1024]; - sprintf(theErrorMessage, "Player \"%s\" not found. Usage: %s CaseSensitivePlayerName Message goes here.\n", thePlayerName, kcTooltip); - UTIL_SayText(theErrorMessage, theAvHPlayer); - } - } - } - else if (FStrEq(pcmd, kcRemoveCat)) - { - if(theAvHPlayer) - { - if(this->GetCheatsEnabled() || theIsPlaytest) - { - AvHTeam* theTeam = theAvHPlayer->GetTeamPointer(); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN)) - { - ReportPlayer(theAvHPlayer, pcmd); - - int theCategory = 0; - sscanf(CMD_ARGV(1), "%d", &theCategory); - AvHAlienUpgradeCategory theUpgradeCategory = AvHAlienUpgradeCategory(theCategory); - - theTeam->RemoveAlienUpgradeCategory(theUpgradeCategory); - - theSuccess = true; - } - } - } - } - else if (FStrEq(pcmd, kcEndGame)) - { - // Put this back in when doing larger scale playtesting - if(this->GetCheatsEnabled() || theIsServerOp || theIsPlaytest) - { - ReportPlayer(theAvHPlayer, pcmd); - - string theCheat = kcEndGame1; - - int theTeam = 0; - sscanf(CMD_ARGV(1), "%d", &theTeam); - - if(theTeam == 2) - { - theCheat = kcEndGame2; - } - - this->SetCheatEnabled(theCheat); - - //this->mVictoryTeam = theTeamNumber; - //this->mVictoryTime = gpGlobals->time; - ////this->mVictoryDraw = true; - } - theSuccess = true; - } - else if (FStrEq(pcmd, kcStartCommandMode)) - { - if(this->GetCheatsEnabled() && theAvHPlayer) - { - if( theAvHPlayer->GetTeamPointer(true)->GetTeamType() != AVH_CLASS_TYPE_MARINE ) - { - for( int counter = TEAM_ACTIVE_BEGIN; counter < TEAM_ACTIVE_END; ++counter ) - { - AvHTeam* team = GetGameRules()->GetTeam((AvHTeamNumber)counter); - if( team && team->GetTeamType() == AVH_CLASS_TYPE_MARINE ) - { - this->AttemptToJoinTeam( theAvHPlayer, (AvHTeamNumber)counter ); - } - } - } - - // Find position of command station - FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) - if( theEntity->GetTeamNumber() == theAvHPlayer->pev->team ) - { - Vector theCommandStationOrigin; - theCommandStationOrigin.x = (theEntity->pev->absmax.x + theEntity->pev->absmin.x)/2.0f; - theCommandStationOrigin.y = (theEntity->pev->absmax.y + theEntity->pev->absmin.y)/2.0f; - theCommandStationOrigin.z = (theEntity->pev->absmax.z + theEntity->pev->absmin.z)/2.0f; - - // Circle the station, trying points around it to see if there's room - for(int i = 0; i < 8; i++) - { - const int kDistance = 100; - Vector theOffset; - float theAngle = (i/(float)360)*2*M_PI; - theOffset.x = cos(theAngle)*kDistance; - theOffset.y = sin(theAngle)*kDistance; - theOffset.z = 20; - - Vector thePosition = theCommandStationOrigin + theOffset; - - if(AvHSUGetIsEnoughRoomForHull(thePosition, AvHMUGetHull(false, theAvHPlayer->pev->iuser3), NULL)) - { - // Teleport to this place - theAvHPlayer->pev->origin = thePosition; - theSuccess = true; - break; - } - } - } - END_FOR_ALL_ENTITIES(kwsTeamCommand) - } - } - else if (FStrEq(pcmd, kcHurt)) - { - if(this->GetCheatsEnabled()) - { - theAvHPlayer->TakeDamage(theAvHPlayer->pev, theAvHPlayer->pev, 100, DMG_GENERIC); - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcSetCullDistance)) - { - if(this->GetCheatsEnabled()) - { - float theCullDistance = 0.0f; - if(sscanf(CMD_ARGV(1), "%f", &theCullDistance) == 1) - { - this->mMapExtents.SetTopDownCullDistance(theCullDistance); - theSuccess = true; - } - } - } - else if (FStrEq(pcmd, kcSetGamma)) - { - if(this->GetCheatsEnabled()) - { - sscanf(CMD_ARGV(1), "%f", &this->mMapGamma); - } - theSuccess = true; - } - else if (FStrEq(pcmd, kcStopCommandMode)) - { - // Fixes problem where players can bind "stopcommandermode" and execute in ready room or maybe as spectator to get weapon - if(theAvHPlayer && theAvHPlayer->GetIsInTopDownMode()) - { - theAvHPlayer->SetUser3(AVH_USER3_MARINE_PLAYER); - - // Cheesy way to make sure player class change is sent to everyone - theAvHPlayer->EffectivePlayerClassChanged(); - - theSuccess = true; - } - else - { - int a = 0; - } - } - else if(FStrEq(pcmd, kcBigDig)) - { - if(this->GetCheatsEnabled()) - { - bool theNewState = !GetGameRules()->GetIsCheatEnabled(kcBigDig); - GetGameRules()->SetCheatEnabled(kcBigDig, theNewState); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcLowCost)) - { - if(this->GetCheatsEnabled()) - { - bool theNewState = !GetGameRules()->GetIsCheatEnabled(kcLowCost); - GetGameRules()->SetCheatEnabled(kcLowCost, theNewState); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcHighDamage)) - { - if(this->GetCheatsEnabled()) - { - GetGameRules()->SetCheatEnabled(kcHighDamage); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcHighTech)) - { - if(this->GetCheatsEnabled()) - { - GetGameRules()->SetCheatEnabled(kcHighTech); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcSlowResearch)) - { - if(this->GetCheatsEnabled()) - { - GetGameRules()->SetCheatEnabled(kcSlowResearch); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcDetectAll)) - { - if(this->GetCheatsEnabled()) - { - GetGameRules()->SetCheatEnabled(kcDetectAll); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcNumInfos)) - { - if(this->GetCheatsEnabled()) - { - int theNumInfos = this->mInfoLocations.size(); - - char theNumInfosString[128]; - sprintf(theNumInfosString, "Num infos: %d\n", theNumInfos); - UTIL_SayText(theNumInfosString, theAvHPlayer); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcNumSpawns)) - { - if(this->GetCheatsEnabled()) - { - int theNumSpawns = this->mSpawnList.size(); - - char theNumSpawnsString[128]; - sprintf(theNumSpawnsString, "Num spawns: %d\n", theNumSpawns); - UTIL_SayText(theNumSpawnsString, theAvHPlayer); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcEntityInfo) || FStrEq(pcmd, kcMapUtilEntityInfo)) - { - if(this->GetCheatsEnabled()) - { - EntityInfoVectorType theEntityInfo = EntityInfoGetVector(); - - ALERT(at_console, "------------------------------------------------------\n"); - ALERT(at_logged, "------------------------------------------------------\nEntityInfo Report:\n------------------------------------------------------\n"); - - char theEntityMessage[1024]; - EntityInfoVectorType::iterator end = theEntityInfo.end(); - for(EntityInfoVectorType::iterator current = theEntityInfo.begin(); current != end; ++current) - { - sprintf(theEntityMessage, "\t%3d \"%s\" entities\n", current->second, current->first.c_str()); - ALERT(at_console, theEntityMessage); - ALERT(at_logged, theEntityMessage); - } - - ALERT(at_console, "------------------------------------------------------\n"); - ALERT(at_logged, "------------------------------------------------------\n"); - - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcInvul)) - { - if(this->GetCheatsEnabled()) - { - if(theAvHPlayer->pev->takedamage == DAMAGE_AIM) - { - theAvHPlayer->pev->takedamage = DAMAGE_NO; - UTIL_SayText("Invul ON", theAvHPlayer); - } - else - { - theAvHPlayer->pev->takedamage = DAMAGE_AIM; - UTIL_SayText("Invul OFF", theAvHPlayer); - } - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcEditPS)) - { - if(this->GetCheatsEnabled() && theAvHPlayer) - { - const char* theName = CMD_ARGV(1); - uint32 theParticleIndex; - if(gParticleTemplateList.GetTemplateIndexWithName(theName, theParticleIndex)) - { - theAvHPlayer->SendMessage(kEditingParticleSystem, TOOLTIP); - NetMsg_EditPS( theAvHPlayer->pev, theParticleIndex ); - } - else - { - theAvHPlayer->SendMessage(kNoParticleSystem, TOOLTIP); - } - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcListPS)) - { - if(this->GetCheatsEnabled() && theAvHPlayer) - { - int theNumTemplates = gParticleTemplateList.GetNumberTemplates(); - - for(int i = 0; i < theNumTemplates; i++) - { - string theTemplateName(""); - const AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(i); - if(theTemplate) - { theTemplateName = theTemplate->GetName(); } - - theTemplateName += "\n"; - NetMsg_ListPS( theAvHPlayer->pev, theTemplateName ); - } - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcRedeem)) - { - if(this->GetCheatsEnabled()) - { - theAvHPlayer->Redeem(); - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcBuildMiniMap)) - { - if(this->GetCheatsEnabled() && theAvHPlayer) - { - const char* theCStrLevelName = STRING(gpGlobals->mapname); - if(theCStrLevelName && !FStrEq(theCStrLevelName, "")) - { - GetGameRules()->BuildMiniMap(theAvHPlayer); - theSuccess = true; - } - } - } - else if(FStrEq(pcmd, "votemap")) - { - if(theAvHPlayer) - { - // If map number passed - int theMapNumber = 0; - if(sscanf(CMD_ARGV(1), "%d", &theMapNumber) == 1) - { - // Add vote to this map. Server might change maps immediately. - GetGameRules()->VoteMap(theAvHPlayer->entindex(), theMapNumber); - } - else - { - // Print the list of maps and votes to the player - StringList theMapVoteList; - GetGameRules()->GetMapVoteStrings(theMapVoteList); - - for(StringList::iterator theIter = theMapVoteList.begin(); theIter != theMapVoteList.end(); theIter++) - { - ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, theIter->c_str()); - } - } - - theSuccess = true; - } - } - else if(FStrEq(pcmd, "weapondebug")) - { - // ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, theIter->c_str()); - if(theAvHPlayer) - { - ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, "weapondebug\n"); - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - theEntity->PrintWeaponListToClient(theAvHPlayer); - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - theSuccess = true; - } - } -#ifdef DEBUG - else if(FStrEq(pcmd, "catalyst")) - { - if(this->GetCheatsEnabled()) - { - if(AvHCatalyst::GiveCatalyst(theAvHPlayer)) - { - theSuccess = true; - } - } - } - else if(FStrEq(pcmd, "showmenu")) - { - short theSlots = 0x1f; - char theDisplayTime = -1; - bool theNeedMore = false; - char* theString = NULL; - - char theMenuText[1024]; - if(sscanf(CMD_ARGV(1), "%s", theMenuText) == 1) - { - NetMsg_ShowMenu( theAvHPlayer->pev, theSlots, theDisplayTime, theNeedMore ? 1 : 0, string(theMenuText) ); - } - - theSuccess = true; - } - else if(FStrEq(pcmd, "calcxp")) - { - if(theAvHPlayer) - { - char theString[512]; - sprintf(theString, "Experience for levels:\n"); - - for(int i=1; i <= 10; i++) - { - float theExperienceForLevel = AvHPlayerUpgrade::GetExperienceForLevel(i); - char theExpString[128]; - sprintf(theExpString, "\t%d/%d\n", i, (int)theExperienceForLevel); - strcat(theString, theExpString); - } - - ALERT(at_logged, theString); - theSuccess = true; - } - } - else if(FStrEq(pcmd, "calcspawn")) - { - if(theAvHPlayer) - { - char theString[512]; - sprintf(theString, "Spawn times (marines):\n"); - - char theResult[128]; - sprintf(theResult, "1v1 -> low: %f high: %f\n", AvHSUCalcCombatSpawnTime(AVH_CLASS_TYPE_MARINE, 1, 0, 1), AvHSUCalcCombatSpawnTime(AVH_CLASS_TYPE_MARINE, 1, 9, 1)); - strcat(theString, theResult); - - sprintf(theResult, "16v16 -> low: %f high: %f\n", AvHSUCalcCombatSpawnTime(AVH_CLASS_TYPE_MARINE, 16, 0, 1), AvHSUCalcCombatSpawnTime(AVH_CLASS_TYPE_MARINE, 16, 9, 1)); - strcat(theString, theResult); - - ALERT(at_logged, theString); - theSuccess = true; - } - } - else if(FStrEq(pcmd, "testscores")) - { - PlayerListType thePlayerList; - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(UTIL_IsValidEntity(theEntity->edict())) - { - thePlayerList.push_back(theEntity); - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - - // Find random player - if(thePlayerList.size() > 0) - { - AvHPlayer* thePlayer = thePlayerList[RANDOM_LONG(0, thePlayerList.size() - 1)]; - ASSERT(thePlayer); - - // Changes scores - int theChangeType = RANDOM_LONG(0, 2); - int theSign = RANDOM_LONG(0, 1) ? 1 : -1; - int theAmount = RANDOM_LONG(0, 5); - - switch(theChangeType) - { - case 0: - thePlayer->SetScore(thePlayer->GetScore() + theSign*theAmount); - break; - case 1: - thePlayer->pev->frags += theSign*theAmount; - break; - case 2: - thePlayer->m_iDeaths += theSign*theAmount; - break; - } - - thePlayer->EffectivePlayerClassChanged(); - - theSuccess = true; - } - } -#endif - else if(FStrEq(pcmd, kcOrderSelf)) - { - if(this->GetCheatsEnabled()) - { - GetGameRules()->SetCheatEnabled(kcOrderSelf); - theSuccess = true; - } - } -#ifdef DEBUG - else if(FStrEq(pcmd, kcAdjustScore)) - { - if(theAvHPlayer) - { - AvHTeam* theTeam = theAvHPlayer->GetTeamPointer(); - if(theTeam) - { - int theAmount = 0; - sscanf(CMD_ARGV(1), "%d", &theAmount); - - theAvHPlayer->AddPoints(theAmount, TRUE); - theAvHPlayer->EffectivePlayerClassChanged(); - - theSuccess = true; - } - } - } - else if(FStrEq(pcmd, kcAttackCS)) - { - if(this->GetCheatsEnabled()) - { - FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) - if(theEntity->pev->team == theAvHPlayer->pev->team) - { - GetGameRules()->TriggerAlert((AvHTeamNumber)theAvHPlayer->pev->team, ALERT_UNDER_ATTACK, theEntity->entindex()); - theSuccess = true; - break; - } - END_FOR_ALL_ENTITIES(kwsTeamCommand); - } - } - else if (FStrEq(pcmd, kcPlayHUDSound)) - { - if(theAvHPlayer && GetGameRules()->GetCheatsEnabled()) - { - AvHHUDSound theHUDSound = HUD_SOUND_INVALID; - if(sscanf(CMD_ARGV(1), "%d", &theHUDSound) == 1) - { - theSuccess = theAvHPlayer->PlayHUDSound(theHUDSound); - } - } - } - else if(FStrEq(pcmd, kcHighSpeed)) - { - if(this->GetCheatsEnabled()) - { - // Toggle hispeed cheat - bool theHiSpeedEnabled = GetGameRules()->GetIsCheatEnabled(kcHighSpeed); - GetGameRules()->SetCheatEnabled(kcHighSpeed, !theHiSpeedEnabled); - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcCrash)) - { - if(this->GetCheatsEnabled()) - { - char* theCrashString = NULL; - *theCrashString = 1; - } - } - else if (FStrEq(pcmd, kcAssert)) - { - if(this->GetCheatsEnabled()) - { - ASSERT(false); - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcRun)) - { - if(this->GetCheatsEnabled() /*|| theIsDeveloper*/) - { - const char* theScriptName = CMD_ARGV(1); - if(theScriptName) - { - AvHScriptManager::Instance()->RunScript(theScriptName); - theSuccess = true; - } - } - } - else if (FStrEq(pcmd, kcClientRun)) - { - if(this->GetCheatsEnabled() /*|| theIsDeveloper*/) - { - const char* theScriptName = CMD_ARGV(1); - if(theAvHPlayer && theScriptName) - { - theAvHPlayer->RunClientScript(theScriptName); - theSuccess = true; - } - } - } - else if(FStrEq(pcmd, kcParasite)) - { - if(this->GetCheatsEnabled()) - { - SetUpgradeMask(&theAvHPlayer->pev->iuser4, MASK_PARASITED); - } - } - else if(FStrEq( pcmd, kcStun) ) - { - if(this->GetCheatsEnabled() && theAvHPlayer) - { - theAvHPlayer->SetIsStunned(true, 4.0f); - theSuccess = true; - } - } - else if (FStrEq(pcmd, kcWeb)) - { - if(this->GetCheatsEnabled()) - { - theAvHPlayer->SetEnsnareState(true); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcDigest)) - { - if(this->GetCheatsEnabled()) - { - int theDevourerIndex = 0; - int theDevoureeIndex = 0; - if((sscanf(CMD_ARGV(1), "%d", &theDevourerIndex) == 1) && (sscanf(CMD_ARGV(2), "%d", &theDevoureeIndex) == 1)) - { - AvHPlayer* theDevourerPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theDevourerIndex))); - AvHPlayer* theDevoureePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theDevoureeIndex))); - if(theDevourerPlayer && theDevoureePlayer) - { - if(!theDevoureePlayer->GetIsBeingDigested()) - { - theDevourerPlayer->StartDigestion(theDevoureePlayer->entindex()); - } - else - { - theDevourerPlayer->StopDigestion(false); - } - } - } - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcBoxes)) - { - if(this->GetCheatsEnabled()) - { - FOR_ALL_BASEENTITIES() - AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); - if(theBuildable) - { - vec3_t theMinPosition = theBuildable->pev->origin + theBuildable->pev->mins; - vec3_t theMaxPosition = theBuildable->pev->origin + theBuildable->pev->maxs; - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - - WRITE_BYTE(TE_BOX); - - WRITE_COORD(theMinPosition.x); - WRITE_COORD(theMinPosition.y); - WRITE_COORD(theMinPosition.x); - - WRITE_COORD(theMaxPosition.x); - WRITE_COORD(theMaxPosition.y); - WRITE_COORD(theMaxPosition.z); - - WRITE_SHORT(100); - - WRITE_BYTE(0); - WRITE_BYTE(255); - WRITE_BYTE(0); - - MESSAGE_END(); - } - - END_FOR_ALL_BASEENTITIES(); - - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcOverflow)) - { - if(this->GetCheatsEnabled()) - { - // Force an overflow - for(int i = 0; i < 100; i++) - { - theAvHPlayer->ForceClientDllUpdate(); - } - theSuccess = true; - } - } - else if ( FStrEq( pcmd, kcViewAll) ) - { - // Allow even with cheats off right now, put this back in for first beta - if(this->GetCheatsEnabled() && theAvHPlayer) - { - GetGameRules()->SetCheatEnabled(kcViewAll); - theSuccess = true; - } - } - else if(FStrEq(pcmd, kcDeathMessage)) - { - if(this->GetCheatsEnabled()) - { - const char* theWeaponName = CMD_ARGV(1); - - NetMsg_DeathMsg( theAvHPlayer->entindex(), theAvHPlayer->entindex(), string(theWeaponName) ); - } - } - else if(FStrEq(pcmd, kcSetSkin)) - { - if(this->GetCheatsEnabled()) - { - int theSkin = 0; - if(sscanf(CMD_ARGV(1), "%d", &theSkin) == 1) - { - theAvHPlayer->SetSkin(theSkin); - - theSuccess = true; - } - } - } - else if(FStrEq(pcmd, kcElectric)) - { - if(this->GetCheatsEnabled()) - { - bool theNewValue = !GetGameRules()->GetIsCheatEnabled(kcElectric); - GetGameRules()->SetCheatEnabled(kcElectric, theNewValue); - } - } - else if(FStrEq(pcmd, kcRoomType)) - { - if(this->GetCheatsEnabled()) - { - int theRoomType = 0; - if(sscanf(CMD_ARGV(1), "%d", &theRoomType) == 1) - { - MESSAGE_BEGIN( MSG_ONE, SVC_ROOMTYPE, NULL, theAvHPlayer->pev); - WRITE_SHORT( (short)theRoomType ); - MESSAGE_END(); - - char theString[128]; - sprintf(theString, "Set room type to %d.\n", theRoomType); - UTIL_SayText(theString, theAvHPlayer); - - theSuccess = true; - } - } - } - #ifdef WIN32 - else if(FStrEq(pcmd, "createfake")) - { - if(this->GetCheatsEnabled()) - { - char theFakeClientName[256]; - sprintf(theFakeClientName, "Bot%d", RANDOM_LONG(0, 2000)); - edict_t* BotEnt = (*g_engfuncs.pfnCreateFakeClient)(theFakeClientName); - - // create the player entity by calling MOD's player function - // (from LINK_ENTITY_TO_CLASS for player object) - player( VARS(BotEnt) ); - - char ptr[128]; // allocate space for message from ClientConnect - ClientConnect( BotEnt, theFakeClientName, "127.0.0.1", ptr ); - - // Pieter van Dijk - use instead of DispatchSpawn() - Hip Hip Hurray! - ClientPutInServer( BotEnt ); - - BotEnt->v.flags |= FL_FAKECLIENT; - } - } - #endif -#endif - else if( FStrEq( pcmd, kcGiveUpgrade) ) - { - // Allow even with cheats off right now, put this back in for first beta - if(this->GetCheatsEnabled()) - { - ReportPlayer(theAvHPlayer, pcmd); - - int theUpgrade = 0; - sscanf(CMD_ARGV(1), "%d", &theUpgrade); - AvHMessageID theNewUpgrade = AvHMessageID(theUpgrade); - - AvHTeam* theVisibleTeam = theAvHPlayer->GetTeamPointer(true); - if(theVisibleTeam) - { - theVisibleTeam->GetTechNodes().SetResearchDone(theNewUpgrade); - this->ProcessTeamUpgrade(theNewUpgrade, theAvHPlayer->GetTeam(true), 0, true); - } - theSuccess = true; - } - } - else if( FStrEq( pcmd, kcRemoveUpgrade) ) - { - // Allow even with cheats off right now, put this back in for first beta - if(this->GetCheatsEnabled()) - { - ReportPlayer(theAvHPlayer, pcmd); - - int theUpgrade = 0; - sscanf(CMD_ARGV(1), "%d", &theUpgrade); - AvHMessageID theNewUpgrade = AvHMessageID(theUpgrade); - - AvHTeam* theVisibleTeam = theAvHPlayer->GetTeamPointer(true); - if(theVisibleTeam) - { - theVisibleTeam->GetTechNodes().SetResearchDone(theNewUpgrade, false); - this->ProcessTeamUpgrade(theNewUpgrade, theAvHPlayer->GetTeam(true), 0, false); - } - - theSuccess = true; - } - } - else if ( FStrEq( pcmd, kcSpectate ) ) - { - if(theAvHPlayer) - { - if( !theAvHPlayer->GetIsAuthorized(AUTH_ACTION_JOIN_TEAM, TEAM_SPECT) ) - { - AvHNexus::handleUnauthorizedJoinTeamAttempt(theAvHPlayer->edict(),TEAM_SPECT); - } -// puzl: 0001073 -#ifdef USE_OLDAUTH - else if(allow_spectators.value && GetGameRules()->PerformHardAuthorization(theAvHPlayer)) -#else - else if(allow_spectators.value) -#endif - { - if(theAvHPlayer->GetPlayMode() == PLAYMODE_READYROOM) - { - theAvHPlayer->SetPlayMode(PLAYMODE_OBSERVER); - //this->PutPlayerIntoSpectateMode(theAvHPlayer); - } - } - else - { - theAvHPlayer->SendMessage(kSpectatorsNotAllowed, TOOLTIP); - } - theSuccess = true; - } - } - else if ( FStrEq( pcmd, kcGivePoints) ) - { - // Allow even with cheats off right now, put this back in for first beta - if(this->GetCheatsEnabled() && theAvHPlayer) - { - int theAmount = 20; - - theAvHPlayer->SetResources(theAvHPlayer->GetResources() + theAmount, true); - - if(theTeam) - { - theTeam->AddResourcesGathered(theAmount); - } - - theSuccess = true; - } - } - else if(FStrEq( pcmd, kcKillCS) ) - { - if(this->GetCheatsEnabled()) - { - if(theTeam) - { - theTeam->KillCS(); - theSuccess = true; - } - } - } - else if(FStrEq( pcmd, kcKillHive) ) - { - if(this->GetCheatsEnabled() || theIsPlaytest) - { - if(theTeam) - { - ReportPlayer(theAvHPlayer, pcmd); - theTeam->KillHive(); - theSuccess = true; - } - } - } - else if(FStrEq( pcmd, kcSpawnHive) ) - { - if(this->GetCheatsEnabled() || theIsPlaytest) - { - AvHTeam* theHiveTeam = theAvHPlayer->GetTeamPointer(true); - if( theHiveTeam->GetTeamType() != AVH_CLASS_TYPE_ALIEN ) - { - for( int counter = TEAM_ACTIVE_BEGIN; counter < TEAM_ACTIVE_END; ++counter ) - { - theHiveTeam = GetGameRules()->GetTeam((AvHTeamNumber)counter); - if( theHiveTeam && theHiveTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN ) - { break; } - } - } - - if(theHiveTeam) - { - ReportPlayer(theAvHPlayer, pcmd); - theHiveTeam->SpawnHive(); - theSuccess = true; - } - } - } - else if(FStrEq( pcmd, kcAlert) ) - { - if(this->GetCheatsEnabled() && theAvHPlayer) - { - AvHTeam* theTeam = theAvHPlayer->GetTeamPointer(true); - if(theTeam) - { - if(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - // Look for random hive, set it under attack - AvHHive* theHive = AvHSUGetRandomActiveHive(theTeam->GetTeamNumber()); - if(theHive) - { - AvHAlertType theRandomAlert = (RANDOM_LONG(0, 1) == 0) ? ALERT_HIVE_DYING : ALERT_UNDER_ATTACK; - GetGameRules()->TriggerAlert(theTeam->GetTeamNumber(), theRandomAlert, theHive->entindex()); - } - } - else - { - if(this->GetIsCombatMode()) - { - // Find Command station, trigger alert (to test for soldiers on ground) - int theCCEntityIndex = 0; - FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) - theCCEntityIndex = theEntity->entindex(); - break; - END_FOR_ALL_ENTITIES(kwsTeamCommand); - - if(theCCEntityIndex > 0) - { - this->TriggerAlert(theTeam->GetTeamNumber(), ALERT_UNDER_ATTACK, theCCEntityIndex); - } - } - else - { - int theRandomEntity = RANDOM_LONG(1, 500); - - AvHAlertType theRandomAlert = AvHAlertType(RANDOM_LONG(0, ALERT_MAX_ALERTS-1)); - GetGameRules()->TriggerAlert(theTeam->GetTeamNumber(), theRandomAlert, theRandomEntity); - } - } - - theSuccess = true; - } - } - } - else if(FStrEq( pcmd, kcKillAll) ) - { - if(this->GetCheatsEnabled()) - { - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - theEntity->TakeDamage(theAvHPlayer->pev, theAvHPlayer->pev, 2000, DMG_GENERIC | DMG_ALWAYSGIB); - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - - theSuccess = true; - } - } -// puzl: 0001073 -#ifdef USE_OLDAUTH - #ifndef AVH_SECURE_PRERELEASE_BUILD - else if(FStrEq(pcmd, kcAuth)) - { - // Toggle auth mask - bool theNewState = !theAvHPlayer->GetAllowAuth(); - theAvHPlayer->SetAllowAuth(theNewState); - - if(theNewState) - { - UTIL_SayText("Authentication ON\n", theAvHPlayer); - } - else - { - UTIL_SayText("Authentication OFF\n", theAvHPlayer); - } - - theSuccess = true; - } - #endif -#endif - else if(FStrEq(pcmd, kcNumEnts)) - { - bool theEnableNumEnts = GetGameRules()->GetCheatsEnabled(); - - if(theEnableNumEnts) - { - if(theAvHPlayer) - { - ReportPlayer(theAvHPlayer, pcmd); - } - - int theEntityCount = this->GetNumEntities(); - - char theNumEntsString[1024]; - sprintf(theNumEntsString, "Total entity count: %d\n", theEntityCount); - ALERT(at_console, theNumEntsString); - ALERT(at_logged, theNumEntsString); - } - - theSuccess = true; - } - else if(FStrEq(pcmd, kcMapUtilNumEnts)) - { - if(GetGameRules()->GetCheatsEnabled()) - { - int theEntityCount = EntityInfoGetCount(); - - char theNumEntsString[1024]; - sprintf(theNumEntsString, "Total map related entity count: %d\n", theEntityCount); - ALERT(at_console, theNumEntsString); - ALERT(at_logged, theNumEntsString); - } - - theSuccess = true; - } - - return theSuccess; +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHConsoleCommands.cpp$ +// $Date: 2002/11/22 21:28:16 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHConsoleCommands.cpp,v $ +// Revision 1.31 2002/11/22 21:28:16 Flayra +// - mp_consistency changes +// +// Revision 1.30 2002/11/15 04:46:53 Flayra +// - Utility command +// +// Revision 1.29 2002/11/03 04:49:56 Flayra +// - Team balance fixes +// +// Revision 1.28 2002/10/28 20:33:55 Flayra +// - Added confirmation to auth command +// +// Revision 1.27 2002/10/24 21:22:40 Flayra +// - Commented out adjust score cheat for release (even with cheats on, this is abusive) +// +// Revision 1.26 2002/10/20 16:45:19 Flayra +// - Linux update +// +// Revision 1.25 2002/10/20 16:35:46 Flayra +// - Added hack code to create a fake client to test profiling +// +// Revision 1.24 2002/10/18 22:17:12 Flayra +// - Added redeem cheat +// +// Revision 1.23 2002/10/16 00:51:33 Flayra +// - Added auth command and setauth cheat +// - Require cheats for some dev commands (oops) +// +// Revision 1.22 2002/10/03 18:41:35 Flayra +// - Allow setskin to work without chets, for testing +// +// Revision 1.21 2002/09/23 22:11:46 Flayra +// - Regular update +// +// Revision 1.20 2002/09/09 19:49:25 Flayra +// - Added deathmessage cheat +// +// Revision 1.19 2002/08/16 02:33:32 Flayra +// - Added endgame cheat to end game for either team (to test endgame music) +// +// Revision 1.18 2002/08/09 00:56:11 Flayra +// - Added "adjustscore" cheat for testing new scoreboard +// +// Revision 1.17 2002/08/02 22:01:27 Flayra +// - Added some new cheats, refactored cheat names, changes for new alert system +// +// Revision 1.16 2002/07/10 14:39:24 Flayra +// - Added "overflow" cheat for debugging +// +// Revision 1.15 2002/07/08 16:49:34 Flayra +// - Unhacked this to fix some random bug +// +// Revision 1.14 2002/06/03 16:41:19 Flayra +// - Added "invul" cheat +// +// Revision 1.13 2002/05/23 02:34:00 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "util/nowarnings.h" +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "dlls/cbase.h" +#include "dlls/player.h" +#include "dlls/weapons.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHConstants.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHEntities.h" +#include "mod/AvHParticleTemplateServer.h" +#include "mod/AvHPlayer.h" +#include "dlls/client.h" +#include "dlls/game.h" +#include "dlls/util.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHMessage.h" +#include "mod/AvHSoundListManager.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHTitles.h" +#include "mod/AvHParticleSystemManager.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHSharedUtil.h" +#include "textrep/TRFactory.h" +#include "mod/AvHEntities.h" +#include "mod/AvHScriptManager.h" +#include "mod/AvHCommandConstants.h" +#include "mod/AvHPlayerUpgrade.h" +#include +#include "game_shared/voice_gamemgr.h" +#include "mod/AvHNetworkMessages.h" +#include "mod/AvHNexusServer.h" + +extern AvHParticleTemplateListServer gParticleTemplateList; +extern CVoiceGameMgr g_VoiceGameMgr; +extern int gCommanderPointsAwardedEventID; +extern cvar_t allow_spectators; +extern cvar_t avh_tournamentmode; +extern int kNumEntsProcessedForPlayerOne; + +#ifdef WIN32 +// this is the LINK_ENTITY_TO_CLASS function that creates a player (bot) +HINSTANCE h_Library = NULL; +typedef void (*LINK_ENTITY_FUNC)(entvars_t *); + +void player( entvars_t *pev ) +{ + if(!h_Library) + { + string libName = string(getModDirectory()) + "\\dlls\\ns.dll"; + h_Library = LoadLibrary(libName.c_str()); + } + + static LINK_ENTITY_FUNC otherClassName = NULL; + if (otherClassName == NULL) + otherClassName = (LINK_ENTITY_FUNC)GetProcAddress(h_Library, "player"); + if (otherClassName != NULL) + { + (*otherClassName)(pev); + } +} +#endif + +//------------------------------------------------------------------ +// begin future MapUtils.h / MapUtils.cpp +//------------------------------------------------------------------ +typedef std::pair EntityInfoNodeType; +typedef std::map EntityInfoMapType; +typedef std::vector EntityInfoVectorType; + +bool EntityInfoSortFunction(const EntityInfoNodeType& left,const EntityInfoNodeType& right) +{ + if(left.second > right.second) + { return true; } + if(left.first.compare(right.first) < 0) + { return true; } + return false; +} + +bool EntityInfoShouldCount(const string& inClassName) +{ + bool theShouldCount = true; + if(inClassName.empty()) // unassigned player slot + { + theShouldCount = false; + } + else if(inClassName.compare(0,7,"weapon_") == 0) + { + theShouldCount = false; + } + else if(inClassName.compare("bodyque") == 0) + { + theShouldCount = false; + } + else if(inClassName.compare("player") == 0) + { + theShouldCount = false; + } + return theShouldCount; +} + +EntityInfoVectorType EntityInfoGetVector(void) +{ + EntityInfoMapType theMap; + const char* theClassName; + string theClassNameString; + + for(int index = 0; index < gpGlobals->maxEntities; ++index) + { + edict_t* theEdict = INDEXENT(index); + if(!FNullEnt(theEdict)) + { + theClassName = STRING(theEdict->v.classname); + if(theClassName) + { + ++theMap[string(theClassName)]; + } + } + } + + EntityInfoVectorType theVector; + + EntityInfoMapType::iterator end = theMap.end(); + for(EntityInfoMapType::iterator current = theMap.begin(); current != end; ++current) + { + if(EntityInfoShouldCount(current->first)) + { + theVector.push_back(EntityInfoNodeType(current->first,current->second)); + } + } + + //sort it + sort(theVector.begin(),theVector.end(),EntityInfoSortFunction); + + return theVector; +} + +int EntityInfoGetCount() +{ + int theCount = 0; + EntityInfoVectorType theVector = EntityInfoGetVector(); + EntityInfoVectorType::iterator end = theVector.end(); + for(EntityInfoVectorType::iterator current = theVector.begin(); current != end; ++current) + { + theCount += current->second; + } + return theCount; +} +//------------------------------------------------------------------ +// end future MapUtils.h / MapUtils.cpp +//------------------------------------------------------------------ + + +void ReportPlayer(CBasePlayer* inPlayer, const char* inCommand) +{ + #ifdef AVH_PLAYTEST_BUILD + // Tell the server that this player executed x message + char* theMessage = UTIL_VarArgs("%s initiated command \"%s\"\n", STRING(inPlayer->pev->netname), inCommand); + UTIL_ClientPrintAll(HUD_PRINTNOTIFY, theMessage); + UTIL_LogPrintf(theMessage); + #endif +} + +BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd ) +{ +//adding Nexus TunnelToClient functionality up here... +// if( strcmp( pcmd, "NexusData" ) == 0 ) +// { +// const char* arg1 = CMD_ARGV(1); +// return AvHNexus::recv(pPlayer->pev,arg1,strlen(arg1)); +// } +//non-Nexus signal handler down here... + + AvHPlayer* theAvHPlayer = dynamic_cast(pPlayer); + AvHTeam* theTeam = NULL; + bool theSuccess = false; + bool theIsDeveloper = false; + bool theIsGuide = false; + bool theIsServerOp = false; + bool theIsPlaytester = false; + bool theIsPlayerHelper = false; + bool theIsPlaytest = false; + bool theReportPlayer = false; + bool theIsDedicatedServer = false; + bool theIsDebug = false; + + #ifdef DEBUG + theIsDebug = true; + #endif + + if(theAvHPlayer) + { + theTeam = theAvHPlayer->GetTeamPointer(); + theIsDeveloper = theAvHPlayer->GetIsMember(PLAYERAUTH_DEVELOPER); + theIsGuide = theAvHPlayer->GetIsMember(PLAYERAUTH_GUIDE); + theIsPlaytester = theAvHPlayer->GetIsMember(PLAYERAUTH_PLAYTESTER); + theIsPlayerHelper = theIsDeveloper || theIsGuide || theIsPlaytester; + + #ifdef DEBUG + theIsPlaytest = theIsPlaytester || theIsDeveloper; + #endif + } + else + { + theIsDedicatedServer = true; + } + + if( !theAvHPlayer || theAvHPlayer->GetIsMember(PLAYERAUTH_SERVEROP) ) + { + theIsServerOp = true; + } + + if(g_VoiceGameMgr.ClientCommand(pPlayer, pcmd)) + { + theSuccess = true; + } + else if ( FStrEq( pcmd, kcJoinTeamOne ) ) + { + if(theAvHPlayer) + { + this->AttemptToJoinTeam(theAvHPlayer, TEAM_ONE, true); + theSuccess = true; + } + } + else if ( FStrEq( pcmd, kcJoinTeamTwo ) ) + { + if(theAvHPlayer) + { + this->AttemptToJoinTeam(theAvHPlayer, TEAM_TWO, true); + theSuccess = true; + } + } + else if ( FStrEq( pcmd, kcJoinTeamThree ) ) + { + if(theAvHPlayer) + { + this->AttemptToJoinTeam(theAvHPlayer, TEAM_THREE, true); + theSuccess = true; + } + } + else if ( FStrEq( pcmd, kcJoinTeamFour ) ) + { + if(theAvHPlayer) + { + this->AttemptToJoinTeam(theAvHPlayer, TEAM_FOUR, true); + theSuccess = true; + } + } + else if ( FStrEq( pcmd, kcAutoAssign ) ) + { + if(theAvHPlayer) + { + this->AutoAssignPlayer(theAvHPlayer); + theSuccess = true; + } + } + else if ( FStrEq( pcmd, kcMenuReadyRoom ) ) + { + if(theAvHPlayer) + { + if(!(theAvHPlayer->pev->flags & FL_FAKECLIENT)) + { + if(!theAvHPlayer->GetIsBeingDigested()) + { + theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true); + } + theSuccess = true; + } + } + theSuccess = true; + } + else if ( FStrEq( pcmd, kcReadyRoom ) ) + { + if(theAvHPlayer) + { + if(!(theAvHPlayer->pev->flags & FL_FAKECLIENT)) + { + if(!theAvHPlayer->GetIsBeingDigested()) + { + // : 984 + // Add a throttle on the readyroom key + const static int kReadyRoomThrottleTimeout=2.0f; + if ( (theAvHPlayer->GetTimeLastF4() == -1.0f) || + (gpGlobals->time > theAvHPlayer->GetTimeLastF4() + kReadyRoomThrottleTimeout) ) + { + theAvHPlayer->SendMessage(kReadyRoomThrottleMessage); + theAvHPlayer->SetTimeLastF4(gpGlobals->time); + } + else if ( gpGlobals->time < theAvHPlayer->GetTimeLastF4() + kReadyRoomThrottleTimeout ) + { + theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true); + } + } + theSuccess = true; + } + } + theSuccess = true; + } + else if (FStrEq(pcmd, kcStartGame)) + { + if(this->GetCheatsEnabled()) + { + this->SetGameStarted(true); + } + theSuccess = true; + } + else if(FStrEq(pcmd, kcSwitch)) + { + if(this->GetCheatsEnabled() || theIsPlaytest) + { + AvHTeamNumber thePlayerTeam = theAvHPlayer->GetTeam(); + if(thePlayerTeam != TEAM_IND) + { + ReportPlayer(theAvHPlayer, kcSwitch); + AvHTeamNumber teamA = GetGameRules()->GetTeamA()->GetTeamNumber(); + AvHTeamNumber teamB = GetGameRules()->GetTeamB()->GetTeamNumber(); + + // Get other team + AvHTeamNumber theOtherTeamNumber = (thePlayerTeam == teamA) ? teamB : teamA; + + // Remember current position + Vector theCurrentPosition = theAvHPlayer->pev->origin; + Vector theCurrentAngles = theAvHPlayer->pev->angles; + + // Switch teams + theAvHPlayer->SetPlayMode(PLAYMODE_READYROOM, true); + this->AttemptToJoinTeam(theAvHPlayer, theOtherTeamNumber, false); + + // : 0001010 - Boost the player 32 units up to avoid sticking in the ground + theCurrentPosition[2] += 32.0f; + + // Set our position again + theAvHPlayer->pev->origin = theCurrentPosition; + theAvHPlayer->pev->angles = theCurrentAngles; + theAvHPlayer->pev->fixangle = 1; + } + + theSuccess = true; + } + } + else if(FStrEq(pcmd, "givexp")) + { + if(this->GetCheatsEnabled()) + { + AvHPlayer* thePlayer = dynamic_cast(theAvHPlayer->GetSpectatingEntity()); + if(!thePlayer) + { + thePlayer = theAvHPlayer; + } + + if(thePlayer && thePlayer->GetIsRelevant()) + { + int theTargetLevel = 1; + + int theNumArgs = CMD_ARGC(); + if(theNumArgs == 2) + { + sscanf(CMD_ARGV(1), "%d", &theTargetLevel); + } + + GetGameRules()->AwardExperience(thePlayer, theTargetLevel); + + theSuccess = true; + } + } + } + else if(FStrEq(pcmd, kcRestartRound) || FStrEq(pcmd, kcRestart)) + { + if(!theAvHPlayer || theIsServerOp || theIsPlaytest || theIsDedicatedServer || theIsDebug || this->GetCheatsEnabled()) + { + if(theAvHPlayer) + { + ReportPlayer(theAvHPlayer, pcmd); + } + + // Reset game world + this->ResetGame(true); + } + theSuccess = true; + } + #ifdef AVH_PLAYTEST_BUILD + else if(FStrEq(pcmd, kcTournyMode)) + { + if(theIsPlaytest) + { + char theCommand[128]; + sprintf(theCommand, "%s %d\n", kvTournamentMode, !((int)avh_tournamentmode.value)); + SERVER_COMMAND(theCommand); + + theSuccess = true; + } + } + #endif +// : 0001073 + #ifdef USE_OLDAUTH + else if(FStrEq(pcmd, "forceuplink")) + { + if(theIsDeveloper || theIsDedicatedServer) + { + this->InitializeAuthentication(); + UTIL_SayText("Initialized authentication.", theAvHPlayer); + } + theSuccess = true; + } + # endif + else if(FStrEq(pcmd, kcSetBalanceVar)) + { + if( theAvHPlayer && theAvHPlayer->GetIsAuthorized(AUTH_ACTION_ADJUST_BALANCE,0) ) + { + BalanceValueContainer* container = BalanceValueContainerFactory::get(BalanceValueContainerFactory::getDefaultFilename()); + int theNumArgs = CMD_ARGC(); + if( theNumArgs == 3 ) + { + string name(CMD_ARGV(1)); + string value(CMD_ARGV(2)); + if( value.at(0) == '"' ) + { + container->insert(name,value.substr(1,value.length()-1)); + } + else if( value.at(value.length()-1) == 'f' || value.find('.') != string::npos ) + { + float fvalue = (float)atof(value.c_str()); + container->insert(name,fvalue); + } + else + { + int ivalue = atoi(value.c_str()); + container->insert(name,ivalue); + } + } + } + + theSuccess = true; + } + else if(FStrEq(pcmd, kcNSChangeLevel)) + { + if(theIsServerOp || theIsPlaytest) + { + char theLevelName[1024]; + if(sscanf(CMD_ARGV(1), "%s", theLevelName) == 1) + { + ReportPlayer(theAvHPlayer, pcmd); + CHANGE_LEVEL(theLevelName, NULL ); + } + else + { + UTIL_SayText("Usage: changelevel ", theAvHPlayer); + } + } + else + { + UTIL_SayText("You're not authorized to changelevel.", theAvHPlayer); + } + + theSuccess = true; + } + else if (FStrEq(pcmd, kcTestEvent)) + { + if(theAvHPlayer && this->GetCheatsEnabled()) + { + vec3_t theOrigin = theAvHPlayer->pev->origin; + theOrigin.z -= 500; + int theFlags = 0;//FEV_GLOBAL;// | FEV_HOSTONLY; + PLAYBACK_EVENT_FULL(theFlags, theAvHPlayer->edict(), gCommanderPointsAwardedEventID, 0, theOrigin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcGetNumPlayers)) + { + if(theAvHPlayer && this->GetCheatsEnabled()) + { + int theNumPlayers = GetGameRules()->GetNumberOfPlayers(true); + char theMessage[128]; + sprintf(theMessage, "Num players: %d\n", theNumPlayers); + UTIL_SayText(theMessage, theAvHPlayer); + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcAddCat)) + { + if(theAvHPlayer && (this->GetCheatsEnabled() || theIsPlaytest)) + { + AvHTeam* theTeam = theAvHPlayer->GetTeamPointer(); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN)) + { + ReportPlayer(theAvHPlayer, pcmd); + + int theCategory = 0; + sscanf(CMD_ARGV(1), "%d", &theCategory); + AvHAlienUpgradeCategory theUpgradeCategory = AvHAlienUpgradeCategory(theCategory); + + theTeam->AddTeamUpgrade(theUpgradeCategory); + + theSuccess = true; + } + } + } + else if (FStrEq(pcmd, kcSendMessage)) + { + if(theAvHPlayer) + { + if(this->GetCheatsEnabled()) + { + if(CMD_ARGC() >= 3) + { + char theTooltipText[512]; + sscanf(CMD_ARGV(1), "%s", theTooltipText); + + int theMode = 0; + sscanf(CMD_ARGV(2), "%d", &theMode); + + if(theMode == 0) + { + UTIL_ShowMessage(theTooltipText, theAvHPlayer); + } + else if(theMode == 1) + { + UTIL_Error(theAvHPlayer, theTooltipText); + } + else + { + bool theIsToolTip = (theMode == 2); + + theAvHPlayer->SendMessage(theTooltipText, (theIsToolTip)?TOOLTIP:NORMAL); + } + + theSuccess = true; + } + } + } + } + else if (FStrEq(pcmd, kcTooltip)) + { + if(theAvHPlayer && theIsPlayerHelper) + { + char thePlayerName[512]; + sscanf(CMD_ARGV(1), "%s", thePlayerName); + + // Read first word as target player + AvHPlayer* theTargetPlayer = NULL; + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + const char* thePlayerCStrName = STRING(theEntity->pev->netname); + if(!strcmp(thePlayerCStrName, thePlayerName)) + { + theTargetPlayer = theEntity; + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + if(theTargetPlayer) + { + string theToolTip; + theToolTip = string(STRING(theAvHPlayer->pev->netname)); + theToolTip += ":"; + + int theNumArgs = CMD_ARGC(); + + for(int i=0; i < theNumArgs - 2; i++) + { + theToolTip += string(" "); + char theTooltipText[512]; + sscanf(CMD_ARGV(i+2), "%s ", theTooltipText); + theToolTip += string(theTooltipText); + + // For some reason, some punctuation comes through as separate arguments + //if(strcmp(theTooltipText, ",")) + //{ + // theToolTip += string(" "); + //} + } + + theTargetPlayer->SendMessage(theToolTip.c_str(), TOOLTIP); + theSuccess = true; + } + else + { + char theErrorMessage[1024]; + sprintf(theErrorMessage, "Player \"%s\" not found. Usage: %s CaseSensitivePlayerName Message goes here.\n", thePlayerName, kcTooltip); + UTIL_SayText(theErrorMessage, theAvHPlayer); + } + } + } + else if (FStrEq(pcmd, kcRemoveCat)) + { + if(theAvHPlayer) + { + if(this->GetCheatsEnabled() || theIsPlaytest) + { + AvHTeam* theTeam = theAvHPlayer->GetTeamPointer(); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN)) + { + ReportPlayer(theAvHPlayer, pcmd); + + int theCategory = 0; + sscanf(CMD_ARGV(1), "%d", &theCategory); + AvHAlienUpgradeCategory theUpgradeCategory = AvHAlienUpgradeCategory(theCategory); + + theTeam->RemoveAlienUpgradeCategory(theUpgradeCategory); + + theSuccess = true; + } + } + } + } + else if (FStrEq(pcmd, kcEndGame)) + { + // Put this back in when doing larger scale playtesting + if(this->GetCheatsEnabled() || theIsServerOp || theIsPlaytest) + { + ReportPlayer(theAvHPlayer, pcmd); + + string theCheat = kcEndGame1; + + int theTeam = 0; + sscanf(CMD_ARGV(1), "%d", &theTeam); + + if(theTeam == 2) + { + theCheat = kcEndGame2; + } + + this->SetCheatEnabled(theCheat); + + //this->mVictoryTeam = theTeamNumber; + //this->mVictoryTime = gpGlobals->time; + ////this->mVictoryDraw = true; + } + theSuccess = true; + } + else if (FStrEq(pcmd, kcStartCommandMode)) + { + if(this->GetCheatsEnabled() && theAvHPlayer) + { + if( theAvHPlayer->GetTeamPointer(true)->GetTeamType() != AVH_CLASS_TYPE_MARINE ) + { + for( int counter = TEAM_ACTIVE_BEGIN; counter < TEAM_ACTIVE_END; ++counter ) + { + AvHTeam* team = GetGameRules()->GetTeam((AvHTeamNumber)counter); + if( team && team->GetTeamType() == AVH_CLASS_TYPE_MARINE ) + { + this->AttemptToJoinTeam( theAvHPlayer, (AvHTeamNumber)counter ); + } + } + } + + // Find position of command station + FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) + if( theEntity->GetTeamNumber() == theAvHPlayer->pev->team ) + { + Vector theCommandStationOrigin; + theCommandStationOrigin.x = (theEntity->pev->absmax.x + theEntity->pev->absmin.x)/2.0f; + theCommandStationOrigin.y = (theEntity->pev->absmax.y + theEntity->pev->absmin.y)/2.0f; + theCommandStationOrigin.z = (theEntity->pev->absmax.z + theEntity->pev->absmin.z)/2.0f; + + // Circle the station, trying points around it to see if there's room + for(int i = 0; i < 8; i++) + { + const int kDistance = 100; + Vector theOffset; + float theAngle = (i/(float)360)*2*M_PI; + theOffset.x = cos(theAngle)*kDistance; + theOffset.y = sin(theAngle)*kDistance; + theOffset.z = 20; + + Vector thePosition = theCommandStationOrigin + theOffset; + + if(AvHSUGetIsEnoughRoomForHull(thePosition, AvHMUGetHull(false, theAvHPlayer->pev->iuser3), NULL)) + { + // Teleport to this place + theAvHPlayer->pev->origin = thePosition; + theSuccess = true; + break; + } + } + } + END_FOR_ALL_ENTITIES(kwsTeamCommand) + } + } + else if (FStrEq(pcmd, kcHurt)) + { + if(this->GetCheatsEnabled()) + { + theAvHPlayer->TakeDamage(theAvHPlayer->pev, theAvHPlayer->pev, 100, DMG_GENERIC); + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcSetCullDistance)) + { + if(this->GetCheatsEnabled()) + { + float theCullDistance = 0.0f; + if(sscanf(CMD_ARGV(1), "%f", &theCullDistance) == 1) + { + this->mMapExtents.SetTopDownCullDistance(theCullDistance); + theSuccess = true; + } + } + } + else if (FStrEq(pcmd, kcSetGamma)) + { + if(this->GetCheatsEnabled()) + { + sscanf(CMD_ARGV(1), "%f", &this->mMapGamma); + } + theSuccess = true; + } + else if (FStrEq(pcmd, kcStopCommandMode)) + { + // Fixes problem where players can bind "stopcommandermode" and execute in ready room or maybe as spectator to get weapon + if(theAvHPlayer && theAvHPlayer->GetIsInTopDownMode()) + { + theAvHPlayer->SetUser3(AVH_USER3_MARINE_PLAYER); + + // Cheesy way to make sure player class change is sent to everyone + theAvHPlayer->EffectivePlayerClassChanged(); + + theSuccess = true; + } + else + { + int a = 0; + } + } + else if(FStrEq(pcmd, kcBigDig)) + { + if(this->GetCheatsEnabled()) + { + bool theNewState = !GetGameRules()->GetIsCheatEnabled(kcBigDig); + GetGameRules()->SetCheatEnabled(kcBigDig, theNewState); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcLowCost)) + { + if(this->GetCheatsEnabled()) + { + bool theNewState = !GetGameRules()->GetIsCheatEnabled(kcLowCost); + GetGameRules()->SetCheatEnabled(kcLowCost, theNewState); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcHighDamage)) + { + if(this->GetCheatsEnabled()) + { + GetGameRules()->SetCheatEnabled(kcHighDamage); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcHighTech)) + { + if(this->GetCheatsEnabled()) + { + GetGameRules()->SetCheatEnabled(kcHighTech); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcSlowResearch)) + { + if(this->GetCheatsEnabled()) + { + GetGameRules()->SetCheatEnabled(kcSlowResearch); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcDetectAll)) + { + if(this->GetCheatsEnabled()) + { + GetGameRules()->SetCheatEnabled(kcDetectAll); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcNumInfos)) + { + if(this->GetCheatsEnabled()) + { + int theNumInfos = this->mInfoLocations.size(); + + char theNumInfosString[128]; + sprintf(theNumInfosString, "Num infos: %d\n", theNumInfos); + UTIL_SayText(theNumInfosString, theAvHPlayer); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcNumSpawns)) + { + if(this->GetCheatsEnabled()) + { + int theNumSpawns = this->mSpawnList.size(); + + char theNumSpawnsString[128]; + sprintf(theNumSpawnsString, "Num spawns: %d\n", theNumSpawns); + UTIL_SayText(theNumSpawnsString, theAvHPlayer); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcEntityInfo) || FStrEq(pcmd, kcMapUtilEntityInfo)) + { + if(this->GetCheatsEnabled()) + { + EntityInfoVectorType theEntityInfo = EntityInfoGetVector(); + + ALERT(at_console, "------------------------------------------------------\n"); + ALERT(at_logged, "------------------------------------------------------\nEntityInfo Report:\n------------------------------------------------------\n"); + + char theEntityMessage[1024]; + EntityInfoVectorType::iterator end = theEntityInfo.end(); + for(EntityInfoVectorType::iterator current = theEntityInfo.begin(); current != end; ++current) + { + sprintf(theEntityMessage, "\t%3d \"%s\" entities\n", current->second, current->first.c_str()); + ALERT(at_console, theEntityMessage); + ALERT(at_logged, theEntityMessage); + } + + ALERT(at_console, "------------------------------------------------------\n"); + ALERT(at_logged, "------------------------------------------------------\n"); + + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcInvul)) + { + if(this->GetCheatsEnabled()) + { + if(theAvHPlayer->pev->takedamage == DAMAGE_AIM) + { + theAvHPlayer->pev->takedamage = DAMAGE_NO; + UTIL_SayText("Invul ON", theAvHPlayer); + } + else + { + theAvHPlayer->pev->takedamage = DAMAGE_AIM; + UTIL_SayText("Invul OFF", theAvHPlayer); + } + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcEditPS)) + { + if(this->GetCheatsEnabled() && theAvHPlayer) + { + const char* theName = CMD_ARGV(1); + uint32 theParticleIndex; + if(gParticleTemplateList.GetTemplateIndexWithName(theName, theParticleIndex)) + { + theAvHPlayer->SendMessage(kEditingParticleSystem, TOOLTIP); + NetMsg_EditPS( theAvHPlayer->pev, theParticleIndex ); + } + else + { + theAvHPlayer->SendMessage(kNoParticleSystem, TOOLTIP); + } + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcListPS)) + { + if(this->GetCheatsEnabled() && theAvHPlayer) + { + int theNumTemplates = gParticleTemplateList.GetNumberTemplates(); + + for(int i = 0; i < theNumTemplates; i++) + { + string theTemplateName(""); + const AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(i); + if(theTemplate) + { theTemplateName = theTemplate->GetName(); } + + theTemplateName += "\n"; + NetMsg_ListPS( theAvHPlayer->pev, theTemplateName ); + } + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcRedeem)) + { + if(this->GetCheatsEnabled()) + { + theAvHPlayer->Redeem(); + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcBuildMiniMap)) + { + if(this->GetCheatsEnabled() && theAvHPlayer) + { + const char* theCStrLevelName = STRING(gpGlobals->mapname); + if(theCStrLevelName && !FStrEq(theCStrLevelName, "")) + { + GetGameRules()->BuildMiniMap(theAvHPlayer); + theSuccess = true; + } + } + } + else if(FStrEq(pcmd, "votemap")) + { + if(theAvHPlayer) + { + // If map number passed + int theMapNumber = 0; + if(sscanf(CMD_ARGV(1), "%d", &theMapNumber) == 1) + { + // Add vote to this map. Server might change maps immediately. + GetGameRules()->VoteMap(theAvHPlayer->entindex(), theMapNumber); + } + else + { + // Print the list of maps and votes to the player + StringList theMapVoteList; + GetGameRules()->GetMapVoteStrings(theMapVoteList); + + for(StringList::iterator theIter = theMapVoteList.begin(); theIter != theMapVoteList.end(); theIter++) + { + ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, theIter->c_str()); + } + } + + theSuccess = true; + } + } + else if(FStrEq(pcmd, "weapondebug")) + { + // ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, theIter->c_str()); + if(theAvHPlayer) + { + ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, "weapondebug\n"); + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + theEntity->PrintWeaponListToClient(theAvHPlayer); + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + theSuccess = true; + } + } +#ifdef DEBUG + else if(FStrEq(pcmd, "catalyst")) + { + if(this->GetCheatsEnabled()) + { + if(AvHCatalyst::GiveCatalyst(theAvHPlayer)) + { + theSuccess = true; + } + } + } + else if(FStrEq(pcmd, "showmenu")) + { + short theSlots = 0x1f; + char theDisplayTime = -1; + bool theNeedMore = false; + char* theString = NULL; + + char theMenuText[1024]; + if(sscanf(CMD_ARGV(1), "%s", theMenuText) == 1) + { + NetMsg_ShowMenu( theAvHPlayer->pev, theSlots, theDisplayTime, theNeedMore ? 1 : 0, string(theMenuText) ); + } + + theSuccess = true; + } + else if(FStrEq(pcmd, "calcxp")) + { + if(theAvHPlayer) + { + char theString[512]; + sprintf(theString, "Experience for levels:\n"); + + for(int i=1; i <= 10; i++) + { + float theExperienceForLevel = AvHPlayerUpgrade::GetExperienceForLevel(i); + char theExpString[128]; + sprintf(theExpString, "\t%d/%d\n", i, (int)theExperienceForLevel); + strcat(theString, theExpString); + } + + ALERT(at_logged, theString); + theSuccess = true; + } + } + else if(FStrEq(pcmd, "calcspawn")) + { + if(theAvHPlayer) + { + char theString[512]; + sprintf(theString, "Spawn times (marines):\n"); + + char theResult[128]; + sprintf(theResult, "1v1 -> low: %f high: %f\n", AvHSUCalcCombatSpawnTime(AVH_CLASS_TYPE_MARINE, 1, 0, 1), AvHSUCalcCombatSpawnTime(AVH_CLASS_TYPE_MARINE, 1, 9, 1)); + strcat(theString, theResult); + + sprintf(theResult, "16v16 -> low: %f high: %f\n", AvHSUCalcCombatSpawnTime(AVH_CLASS_TYPE_MARINE, 16, 0, 1), AvHSUCalcCombatSpawnTime(AVH_CLASS_TYPE_MARINE, 16, 9, 1)); + strcat(theString, theResult); + + ALERT(at_logged, theString); + theSuccess = true; + } + } + else if(FStrEq(pcmd, "testscores")) + { + PlayerListType thePlayerList; + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(UTIL_IsValidEntity(theEntity->edict())) + { + thePlayerList.push_back(theEntity); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + + // Find random player + if(thePlayerList.size() > 0) + { + AvHPlayer* thePlayer = thePlayerList[RANDOM_LONG(0, thePlayerList.size() - 1)]; + ASSERT(thePlayer); + + // Changes scores + int theChangeType = RANDOM_LONG(0, 2); + int theSign = RANDOM_LONG(0, 1) ? 1 : -1; + int theAmount = RANDOM_LONG(0, 5); + + switch(theChangeType) + { + case 0: + thePlayer->SetScore(thePlayer->GetScore() + theSign*theAmount); + break; + case 1: + thePlayer->pev->frags += theSign*theAmount; + break; + case 2: + thePlayer->m_iDeaths += theSign*theAmount; + break; + } + + thePlayer->EffectivePlayerClassChanged(); + + theSuccess = true; + } + } +#endif + else if(FStrEq(pcmd, kcOrderSelf)) + { + if(this->GetCheatsEnabled()) + { + GetGameRules()->SetCheatEnabled(kcOrderSelf); + theSuccess = true; + } + } +#ifdef DEBUG + else if(FStrEq(pcmd, kcAdjustScore)) + { + if(theAvHPlayer) + { + AvHTeam* theTeam = theAvHPlayer->GetTeamPointer(); + if(theTeam) + { + int theAmount = 0; + sscanf(CMD_ARGV(1), "%d", &theAmount); + + theAvHPlayer->AddPoints(theAmount, TRUE); + theAvHPlayer->EffectivePlayerClassChanged(); + + theSuccess = true; + } + } + } + else if(FStrEq(pcmd, kcAttackCS)) + { + if(this->GetCheatsEnabled()) + { + FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) + if(theEntity->pev->team == theAvHPlayer->pev->team) + { + GetGameRules()->TriggerAlert((AvHTeamNumber)theAvHPlayer->pev->team, ALERT_UNDER_ATTACK, theEntity->entindex()); + theSuccess = true; + break; + } + END_FOR_ALL_ENTITIES(kwsTeamCommand); + } + } + else if (FStrEq(pcmd, kcPlayHUDSound)) + { + if(theAvHPlayer && GetGameRules()->GetCheatsEnabled()) + { + AvHHUDSound theHUDSound = HUD_SOUND_INVALID; + if(sscanf(CMD_ARGV(1), "%d", &theHUDSound) == 1) + { + theSuccess = theAvHPlayer->PlayHUDSound(theHUDSound); + } + } + } + else if(FStrEq(pcmd, kcHighSpeed)) + { + if(this->GetCheatsEnabled()) + { + // Toggle hispeed cheat + bool theHiSpeedEnabled = GetGameRules()->GetIsCheatEnabled(kcHighSpeed); + GetGameRules()->SetCheatEnabled(kcHighSpeed, !theHiSpeedEnabled); + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcCrash)) + { + if(this->GetCheatsEnabled()) + { + char* theCrashString = NULL; + *theCrashString = 1; + } + } + else if (FStrEq(pcmd, kcAssert)) + { + if(this->GetCheatsEnabled()) + { + ASSERT(false); + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcRun)) + { + if(this->GetCheatsEnabled() /*|| theIsDeveloper*/) + { + const char* theScriptName = CMD_ARGV(1); + if(theScriptName) + { + AvHScriptManager::Instance()->RunScript(theScriptName); + theSuccess = true; + } + } + } + else if (FStrEq(pcmd, kcClientRun)) + { + if(this->GetCheatsEnabled() /*|| theIsDeveloper*/) + { + const char* theScriptName = CMD_ARGV(1); + if(theAvHPlayer && theScriptName) + { + theAvHPlayer->RunClientScript(theScriptName); + theSuccess = true; + } + } + } + else if(FStrEq(pcmd, kcParasite)) + { + if(this->GetCheatsEnabled()) + { + SetUpgradeMask(&theAvHPlayer->pev->iuser4, MASK_PARASITED); + } + } + else if(FStrEq( pcmd, kcStun) ) + { + if(this->GetCheatsEnabled() && theAvHPlayer) + { + theAvHPlayer->SetIsStunned(true, 4.0f); + theSuccess = true; + } + } + else if (FStrEq(pcmd, kcWeb)) + { + if(this->GetCheatsEnabled()) + { + theAvHPlayer->SetEnsnareState(true); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcDigest)) + { + if(this->GetCheatsEnabled()) + { + int theDevourerIndex = 0; + int theDevoureeIndex = 0; + if((sscanf(CMD_ARGV(1), "%d", &theDevourerIndex) == 1) && (sscanf(CMD_ARGV(2), "%d", &theDevoureeIndex) == 1)) + { + AvHPlayer* theDevourerPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theDevourerIndex))); + AvHPlayer* theDevoureePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theDevoureeIndex))); + if(theDevourerPlayer && theDevoureePlayer) + { + if(!theDevoureePlayer->GetIsBeingDigested()) + { + theDevourerPlayer->StartDigestion(theDevoureePlayer->entindex()); + } + else + { + theDevourerPlayer->StopDigestion(false); + } + } + } + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcBoxes)) + { + if(this->GetCheatsEnabled()) + { + FOR_ALL_BASEENTITIES() + AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); + if(theBuildable) + { + vec3_t theMinPosition = theBuildable->pev->origin + theBuildable->pev->mins; + vec3_t theMaxPosition = theBuildable->pev->origin + theBuildable->pev->maxs; + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + + WRITE_BYTE(TE_BOX); + + WRITE_COORD(theMinPosition.x); + WRITE_COORD(theMinPosition.y); + WRITE_COORD(theMinPosition.x); + + WRITE_COORD(theMaxPosition.x); + WRITE_COORD(theMaxPosition.y); + WRITE_COORD(theMaxPosition.z); + + WRITE_SHORT(100); + + WRITE_BYTE(0); + WRITE_BYTE(255); + WRITE_BYTE(0); + + MESSAGE_END(); + } + + END_FOR_ALL_BASEENTITIES(); + + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcOverflow)) + { + if(this->GetCheatsEnabled()) + { + // Force an overflow + for(int i = 0; i < 100; i++) + { + theAvHPlayer->ForceClientDllUpdate(); + } + theSuccess = true; + } + } + else if ( FStrEq( pcmd, kcViewAll) ) + { + // Allow even with cheats off right now, put this back in for first beta + if(this->GetCheatsEnabled() && theAvHPlayer) + { + GetGameRules()->SetCheatEnabled(kcViewAll); + theSuccess = true; + } + } + else if(FStrEq(pcmd, kcDeathMessage)) + { + if(this->GetCheatsEnabled()) + { + const char* theWeaponName = CMD_ARGV(1); + + NetMsg_DeathMsg( theAvHPlayer->entindex(), theAvHPlayer->entindex(), string(theWeaponName) ); + } + } + else if(FStrEq(pcmd, kcSetSkin)) + { + if(this->GetCheatsEnabled()) + { + int theSkin = 0; + if(sscanf(CMD_ARGV(1), "%d", &theSkin) == 1) + { + theAvHPlayer->SetSkin(theSkin); + + theSuccess = true; + } + } + } + else if(FStrEq(pcmd, kcElectric)) + { + if(this->GetCheatsEnabled()) + { + bool theNewValue = !GetGameRules()->GetIsCheatEnabled(kcElectric); + GetGameRules()->SetCheatEnabled(kcElectric, theNewValue); + } + } + else if(FStrEq(pcmd, kcRoomType)) + { + if(this->GetCheatsEnabled()) + { + int theRoomType = 0; + if(sscanf(CMD_ARGV(1), "%d", &theRoomType) == 1) + { + MESSAGE_BEGIN( MSG_ONE, SVC_ROOMTYPE, NULL, theAvHPlayer->pev); + WRITE_SHORT( (short)theRoomType ); + MESSAGE_END(); + + char theString[128]; + sprintf(theString, "Set room type to %d.\n", theRoomType); + UTIL_SayText(theString, theAvHPlayer); + + theSuccess = true; + } + } + } + #ifdef WIN32 + else if(FStrEq(pcmd, "createfake")) + { + if(this->GetCheatsEnabled()) + { + char theFakeClientName[256]; + sprintf(theFakeClientName, "Bot%d", RANDOM_LONG(0, 2000)); + edict_t* BotEnt = (*g_engfuncs.pfnCreateFakeClient)(theFakeClientName); + + // create the player entity by calling MOD's player function + // (from LINK_ENTITY_TO_CLASS for player object) + player( VARS(BotEnt) ); + + char ptr[128]; // allocate space for message from ClientConnect + ClientConnect( BotEnt, theFakeClientName, "127.0.0.1", ptr ); + + // Pieter van Dijk - use instead of DispatchSpawn() - Hip Hip Hurray! + ClientPutInServer( BotEnt ); + + BotEnt->v.flags |= FL_FAKECLIENT; + } + } + #endif +#endif + else if( FStrEq( pcmd, kcGiveUpgrade) ) + { + // Allow even with cheats off right now, put this back in for first beta + if(this->GetCheatsEnabled()) + { + ReportPlayer(theAvHPlayer, pcmd); + + int theUpgrade = 0; + sscanf(CMD_ARGV(1), "%d", &theUpgrade); + AvHMessageID theNewUpgrade = AvHMessageID(theUpgrade); + + AvHTeam* theVisibleTeam = theAvHPlayer->GetTeamPointer(true); + if(theVisibleTeam) + { + theVisibleTeam->GetTechNodes().SetResearchDone(theNewUpgrade); + this->ProcessTeamUpgrade(theNewUpgrade, theAvHPlayer->GetTeam(true), 0, true); + } + theSuccess = true; + } + } + else if( FStrEq( pcmd, kcRemoveUpgrade) ) + { + // Allow even with cheats off right now, put this back in for first beta + if(this->GetCheatsEnabled()) + { + ReportPlayer(theAvHPlayer, pcmd); + + int theUpgrade = 0; + sscanf(CMD_ARGV(1), "%d", &theUpgrade); + AvHMessageID theNewUpgrade = AvHMessageID(theUpgrade); + + AvHTeam* theVisibleTeam = theAvHPlayer->GetTeamPointer(true); + if(theVisibleTeam) + { + theVisibleTeam->GetTechNodes().SetResearchDone(theNewUpgrade, false); + this->ProcessTeamUpgrade(theNewUpgrade, theAvHPlayer->GetTeam(true), 0, false); + } + + theSuccess = true; + } + } + else if ( FStrEq( pcmd, kcSpectate ) ) + { + if(theAvHPlayer) + { + if( !theAvHPlayer->GetIsAuthorized(AUTH_ACTION_JOIN_TEAM, TEAM_SPECT) ) + { + AvHNexus::handleUnauthorizedJoinTeamAttempt(theAvHPlayer->edict(),TEAM_SPECT); + } +// : 0001073 +#ifdef USE_OLDAUTH + else if(allow_spectators.value && GetGameRules()->PerformHardAuthorization(theAvHPlayer)) +#else + else if(allow_spectators.value) +#endif + { + if(theAvHPlayer->GetPlayMode() == PLAYMODE_READYROOM) + { + theAvHPlayer->SetPlayMode(PLAYMODE_OBSERVER); + //this->PutPlayerIntoSpectateMode(theAvHPlayer); + } + } + else + { + theAvHPlayer->SendMessage(kSpectatorsNotAllowed, TOOLTIP); + } + theSuccess = true; + } + } + else if ( FStrEq( pcmd, kcGivePoints) ) + { + // Allow even with cheats off right now, put this back in for first beta + if(this->GetCheatsEnabled() && theAvHPlayer) + { + int theAmount = 20; + + theAvHPlayer->SetResources(theAvHPlayer->GetResources() + theAmount, true); + + if(theTeam) + { + theTeam->AddResourcesGathered(theAmount); + } + + theSuccess = true; + } + } + else if(FStrEq( pcmd, kcKillCS) ) + { + if(this->GetCheatsEnabled()) + { + if(theTeam) + { + theTeam->KillCS(); + theSuccess = true; + } + } + } + else if(FStrEq( pcmd, kcKillHive) ) + { + if(this->GetCheatsEnabled() || theIsPlaytest) + { + if(theTeam) + { + ReportPlayer(theAvHPlayer, pcmd); + theTeam->KillHive(); + theSuccess = true; + } + } + } + else if(FStrEq( pcmd, kcSpawnHive) ) + { + if(this->GetCheatsEnabled() || theIsPlaytest) + { + AvHTeam* theHiveTeam = theAvHPlayer->GetTeamPointer(true); + if( theHiveTeam->GetTeamType() != AVH_CLASS_TYPE_ALIEN ) + { + for( int counter = TEAM_ACTIVE_BEGIN; counter < TEAM_ACTIVE_END; ++counter ) + { + theHiveTeam = GetGameRules()->GetTeam((AvHTeamNumber)counter); + if( theHiveTeam && theHiveTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN ) + { break; } + } + } + + if(theHiveTeam) + { + ReportPlayer(theAvHPlayer, pcmd); + theHiveTeam->SpawnHive(); + theSuccess = true; + } + } + } + else if(FStrEq( pcmd, kcAlert) ) + { + if(this->GetCheatsEnabled() && theAvHPlayer) + { + AvHTeam* theTeam = theAvHPlayer->GetTeamPointer(true); + if(theTeam) + { + if(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + // Look for random hive, set it under attack + AvHHive* theHive = AvHSUGetRandomActiveHive(theTeam->GetTeamNumber()); + if(theHive) + { + AvHAlertType theRandomAlert = (RANDOM_LONG(0, 1) == 0) ? ALERT_HIVE_DYING : ALERT_UNDER_ATTACK; + GetGameRules()->TriggerAlert(theTeam->GetTeamNumber(), theRandomAlert, theHive->entindex()); + } + } + else + { + if(this->GetIsCombatMode()) + { + // Find Command station, trigger alert (to test for soldiers on ground) + int theCCEntityIndex = 0; + FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) + theCCEntityIndex = theEntity->entindex(); + break; + END_FOR_ALL_ENTITIES(kwsTeamCommand); + + if(theCCEntityIndex > 0) + { + this->TriggerAlert(theTeam->GetTeamNumber(), ALERT_UNDER_ATTACK, theCCEntityIndex); + } + } + else + { + int theRandomEntity = RANDOM_LONG(1, 500); + + AvHAlertType theRandomAlert = AvHAlertType(RANDOM_LONG(0, ALERT_MAX_ALERTS-1)); + GetGameRules()->TriggerAlert(theTeam->GetTeamNumber(), theRandomAlert, theRandomEntity); + } + } + + theSuccess = true; + } + } + } + else if(FStrEq( pcmd, kcKillAll) ) + { + if(this->GetCheatsEnabled()) + { + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + theEntity->TakeDamage(theAvHPlayer->pev, theAvHPlayer->pev, 2000, DMG_GENERIC | DMG_ALWAYSGIB); + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + + theSuccess = true; + } + } +// : 0001073 +#ifdef USE_OLDAUTH + #ifndef AVH_SECURE_PRERELEASE_BUILD + else if(FStrEq(pcmd, kcAuth)) + { + // Toggle auth mask + bool theNewState = !theAvHPlayer->GetAllowAuth(); + theAvHPlayer->SetAllowAuth(theNewState); + + if(theNewState) + { + UTIL_SayText("Authentication ON\n", theAvHPlayer); + } + else + { + UTIL_SayText("Authentication OFF\n", theAvHPlayer); + } + + theSuccess = true; + } + #endif +#endif + else if(FStrEq(pcmd, kcNumEnts)) + { + bool theEnableNumEnts = GetGameRules()->GetCheatsEnabled(); + + if(theEnableNumEnts) + { + if(theAvHPlayer) + { + ReportPlayer(theAvHPlayer, pcmd); + } + + int theEntityCount = this->GetNumEntities(); + + char theNumEntsString[1024]; + sprintf(theNumEntsString, "Total entity count: %d\n", theEntityCount); + ALERT(at_console, theNumEntsString); + ALERT(at_logged, theNumEntsString); + } + + theSuccess = true; + } + else if(FStrEq(pcmd, kcMapUtilNumEnts)) + { + if(GetGameRules()->GetCheatsEnabled()) + { + int theEntityCount = EntityInfoGetCount(); + + char theNumEntsString[1024]; + sprintf(theNumEntsString, "Total map related entity count: %d\n", theEntityCount); + ALERT(at_console, theNumEntsString); + ALERT(at_logged, theNumEntsString); + } + + theSuccess = true; + } + + return theSuccess; } \ No newline at end of file diff --git a/main/source/mod/AvHConstants.h b/main/source/mod/AvHConstants.h index c1609ec..69da76f 100644 --- a/main/source/mod/AvHConstants.h +++ b/main/source/mod/AvHConstants.h @@ -1,1011 +1,1011 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHConstants.h$ -// $Date: 2002/11/12 02:23:01 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHConstants.h,v $ -// Revision 1.61 2002/11/12 02:23:01 Flayra -// - Renamed avhplayer for 3rd party compatibility (adminmod, etc.) -// -// Revision 1.60 2002/11/06 01:39:48 Flayra -// - Regeneration now heals a percentage -// -// Revision 1.59 2002/11/05 06:17:25 Flayra -// - Balance changes -// -// Revision 1.58 2002/11/03 04:50:19 Flayra -// - Hard-coded gameplay constants instead of putting in skill.cfg -// -// Revision 1.57 2002/10/24 21:23:01 Flayra -// - Reworked jetpacks -// - Added alien easter eggs -// -// Revision 1.56 2002/10/18 22:18:44 Flayra -// - Added alien easter egg sayings -// - Added sensory chamber sayings -// - Limit number of buildings in radius -// - Fixed motd.txt length crash -// -// Revision 1.55 2002/10/16 20:51:44 Flayra -// - Hive health while gestating -// -// Revision 1.54 2002/10/16 00:52:45 Flayra -// - Added "need order" alert -// - Added authentication mask -// - Updated alien building sounds -// - Removed a couple unneeded sounds -// -// Revision 1.53 2002/10/03 18:41:58 Flayra -// - Added alien HUD sounds -// - Added more order sounds -// -// Revision 1.52 2002/09/25 20:43:40 Flayra -// - Removed use order, sound update -// -// Revision 1.51 2002/09/23 22:12:07 Flayra -// - New CC sounds -// - Regular update -// -// Revision 1.50 2002/09/09 19:49:25 Flayra -// - Added deathmessage cheat -// -// Revision 1.49 2002/08/31 18:01:01 Flayra -// - Work at VALVe -// -// Revision 1.48 2002/08/16 02:33:57 Flayra -// - Regular update -// -// Revision 1.47 2002/08/09 00:56:18 Flayra -// - Added "adjustscore" cheat for testing new scoreboard -// -// Revision 1.46 2002/08/02 22:01:04 Flayra -// - Regular update -// -// Revision 1.45 2002/07/28 19:38:59 Flayra -// - Linux path fixes -// -// Revision 1.44 2002/07/26 23:04:08 Flayra -// - Generate numerical feedback for damage events -// -// Revision 1.43 2002/07/23 17:01:02 Flayra -// - Regular update -// -// Revision 1.42 2002/07/08 16:50:20 Flayra -// - Reworked team colors, #define a few more sounds, weapon spread -// -// Revision 1.41 2002/07/01 22:41:40 Flayra -// - Removed outdated overwatch target and tension events -// -// Revision 1.40 2002/07/01 21:25:26 Flayra -// - Added new alien weapons, added build ranges -// -// Revision 1.39 2002/06/25 17:53:19 Flayra -// - Regular update (cleanup, new entities, new classnames) -// -// Revision 1.38 2002/06/10 19:51:29 Flayra -// - Regular update -// -// Revision 1.37 2002/06/03 16:41:50 Flayra -// - Removed duplicate hive class name, added more player class types for scoreboard info -// -// Revision 1.36 2002/05/28 17:37:48 Flayra -// - Reinforcment refactoring, renamed role sounds to be less confusing -// -// Revision 1.35 2002/05/23 02:34:00 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifndef AVHCONSTANTS_H -#define AVHCONSTANTS_H - -#include "types.h" -#include "game_shared/teamconst.h" - -// Basic constants -const int kMaxPlayers = 32; -const int kMaxEntities = 1500; -const int kSelectionStartRange = 50; -const int kSelectionEndRange = 8012; -const int kSelectionExtreme = 8012; -const float kSelectionNetworkConstant = 1000.0f; -const float kBuildInvalidRangeMultiplier = 2.0f; -const float kWorldPosNetworkConstant = 6.0f; -const float kNumericNetworkConstant = 100.0f; -const int kWorldMinimumZ = -4096; -const int kMaxGroundPlayerSpeed = 1200; -const int kNumHotkeyGroups = 5; -const int kSelectAllHotGroup = kNumHotkeyGroups + 1; - -const int kNumRequestTypes = 3; -const int kPendingIdleSoldiers = (kNumHotkeyGroups + 1); -const int kPendingAmmoRequests = (kNumHotkeyGroups + 2); -const int kPendingHealthRequests = (kNumHotkeyGroups + 3); - -const int kMaxHives = 3; -const int kMaxUpgradeCategories = 3; -const int kMaxUpgradeLevel = 3; - -const int kFuncResourceMaxResources = 300; -//const float kPlayerResourceScalar = 2.0f; // 1.5 might be better -const int kVictoryIntermission = 12; -const int kResetPlayersPerSecond = 8; -const float kBuildingUseWarmupTime = 2.0f; -const float kRedeemInvulnerableTime = 1.0f; - -const float kMaxBuildingDropSlope = 1.0f; -const float kMaxEquipmentDropSlope = 1.0f; - -// Used to be cl_forwardspeed, cl_backspeed, cl_sidespeed. -const float kForwardSpeed = 1000.0f; -const float kBackSpeed = 1000.0f; -const float kSideSpeed = 1000.0f; - -// Name this guy for custom propagation, maybe -typedef int EntityInfo; -typedef vector EntityListType; -typedef pair EntityFloatPairType; -typedef vector EntityFloatPairListType; -typedef map EntityMap; -typedef vector IntList; - -#define kAvHGameName ((const char*)("Natural Selection")) -#define kAvHGameAcronymn ((const char*)("NS")) -#define kInvalidTeamName "Invalid team name" -#define kAvHPlayerClassName "player" -#define kwsWeldableClassName "avhweldable" -#define kwsMapInfoClassName "avhmapinfo" -#define kwsGameplayClassName "avhgameplay" -#define kwsGammaClassName "avhgamma" - -#define kNumAlienLifeforms 5 -#define kNumAlienUpgrades 12 -#define kNumAlienUpgradeCategories 3 -#define kNumUpgradesInCategory 3 -#define kNumUpgradeLevelsInCategory 3 - -#define kMaxAlienHives 3 -#define kMaxAlienEnergy 100 -#define kMaxAlienResources 100 - -typedef enum -{ - AVH_CLASS_TYPE_UNDEFINED = 0, - AVH_CLASS_TYPE_MARINE = 1, - AVH_CLASS_TYPE_ALIEN = 2, - AVH_CLASS_TYPE_AUTOASSIGN = 3 -} AvHClassType; - -typedef enum -{ - MAP_MODE_UNDEFINED = 0, - MAP_MODE_NS = 1, - MAP_MODE_CS = 2, - MAP_MODE_DM = 3, - MAP_MODE_CO = 4, - MAP_MODE_NSC = 5 -} -AvHMapMode; - -#define kUndefinedTeam "undefinedteam" -#define kMarine1Team "marine1team" -#define kAlien1Team "alien1team" -#define kMarine2Team "marine2team" -#define kAlien2Team "alien2team" -#define kSpectatorTeam "spectatorteam" -#define kTeamString "undefinedteam;marine1team;alien1team;marine2team;alien2team;spectatorteam" - -#define kMarinePrettyName "Frontiersmen" -#define kAlienPrettyName "Kharaa" -#define kUndefPrettyName "Undefined" - -typedef enum -{ - AUTH_ACTION_JOIN_TEAM = 0, - AUTH_ACTION_ADJUST_BALANCE = 1 -} AvHAuthAction; - -typedef enum -{ - PLAYMODE_UNDEFINED = 0, - PLAYMODE_READYROOM = 1, - PLAYMODE_PLAYING = 2, - PLAYMODE_AWAITINGREINFORCEMENT = 3, // Player is dead and waiting in line to get back in - PLAYMODE_REINFORCING = 4, // Player is in the process of coming back into the game - PLAYMODE_OBSERVER = 5, - PLAYMODE_REINFORCINGCOMPLETE = 6 // Combat only: 'press fire to respawn' -} AvHPlayMode; - -typedef enum -{ - TEAM_IND = 0, - TEAM_ONE = 1, - TEAM_TWO = 2, - TEAM_THREE = 3, - TEAM_FOUR = 4, - TEAM_SPECT = 5, - TEAM_ACTIVE_BEGIN = 1, - TEAM_ACTIVE_END = 5 -} AvHTeamNumber; - -typedef enum -{ - ALERT_NONE = 0, - ALERT_PLAYER_ENGAGE = 1, - ALERT_UNDER_ATTACK = 2, - ALERT_RESEARCH_COMPLETE = 3, - ALERT_UPGRADE_COMPLETE = 4, - ALERT_LOW_RESOURCES = 5, - ALERT_SOLDIER_NEEDS_AMMO = 6, - ALERT_SOLDIER_NEEDS_HEALTH = 7, - ALERT_PLAYER_DIED = 8, - ALERT_SENTRY_FIRING = 9, - ALERT_SENTRY_DAMAGED = 10, - ALERT_HIVE_COMPLETE = 11, - ALERT_HIVE_DYING = 12, - ALERT_NEW_TRAIT = 13, - ALERT_ORDER_NEEDED = 14, - ALERT_ORDER_COMPLETE = 15, - ALERT_MAX_ALERTS = 16 -} AvHAlertType; - -typedef enum -{ - PLAYERCLASS_NONE = 0, - PLAYERCLASS_ALIVE_MARINE, - PLAYERCLASS_ALIVE_JETPACK_MARINE, - PLAYERCLASS_ALIVE_HEAVY_MARINE, - PLAYERCLASS_ALIVE_LEVEL1, - PLAYERCLASS_ALIVE_LEVEL2, - PLAYERCLASS_ALIVE_LEVEL3, - PLAYERCLASS_ALIVE_LEVEL4, - PLAYERCLASS_ALIVE_LEVEL5, - PLAYERCLASS_ALIVE_DIGESTING, - PLAYERCLASS_ALIVE_GESTATING, - PLAYERCLASS_DEAD_MARINE, - PLAYERCLASS_DEAD_ALIEN, - PLAYERCLASS_COMMANDER, - PLAYERCLASS_REINFORCING, - PLAYERCLASS_SPECTATOR, - PLAYERCLASS_REINFORCINGCOMPLETE -} AvHPlayerClass; - -// puzl: 0001073 -#ifdef USE_OLDAUTH -// This is a mask because players can have more then one of these -typedef enum -{ - PLAYERAUTH_NONE = 0, - PLAYERAUTH_DEVELOPER = 1, - PLAYERAUTH_GUIDE = 2, - PLAYERAUTH_SERVEROP = 4, - PLAYERAUTH_PLAYTESTER = 8, - PLAYERAUTH_CONTRIBUTOR = 16, - PLAYERAUTH_CHEATINGDEATH = 32, - PLAYERAUTH_VETERAN = 64, - PLAYERAUTH_BETASERVEROP = 128, - PLAYERAUTH_PENDING = 256, - PLAYERAUTH_CUSTOM = 512, - PLAYERAUTH_UPP_MODE = 16384 -} AvHPlayerAuthentication; -#else -// Mask replaced with explicit string set for GetIsMember function -const static string PLAYERAUTH_DEVELOPER("dev"); -const static string PLAYERAUTH_PLAYTESTER("pt"); -const static string PLAYERAUTH_GUIDE("guide"); -const static string PLAYERAUTH_CONTRIBUTOR("const"); -const static string PLAYERAUTH_SERVEROP("op"); -const static string PLAYERAUTH_VETERAN("vet"); -const static string PLAYERAUTH_BETASERVEROP("betaop"); -#endif - -typedef enum -{ - HUD_SOUND_INVALID = 0, - HUD_SOUND_POINTS_SPENT, - HUD_SOUND_COUNTDOWN, - HUD_SOUND_SELECT, - HUD_SOUND_SQUAD1, - HUD_SOUND_SQUAD2, - HUD_SOUND_SQUAD3, - HUD_SOUND_SQUAD4, - HUD_SOUND_SQUAD5, - HUD_SOUND_PLACE_BUILDING, - HUD_SOUND_MARINE_RESEARCHCOMPLETE, - HUD_SOUND_MARINE_SOLDIER_UNDER_ATTACK, - HUD_SOUND_MARINE_BASE_UNDER_ATTACK, - HUD_SOUND_MARINE_UPGRADE_COMPLETE, - HUD_SOUND_MARINE_MORE, - HUD_SOUND_MARINE_SOLDIERLOST, - HUD_SOUND_MARINE_CCONLINE, - HUD_SOUND_MARINE_CCUNDERATTACK, - HUD_SOUND_MARINE_COMMANDER_EJECTED, - HUD_SOUND_MARINE_RESOURCES_LOW, - HUD_SOUND_MARINE_NEEDS_AMMO, - HUD_SOUND_MARINE_NEEDS_HEALTH, - HUD_SOUND_MARINE_NEEDS_ORDER, - HUD_SOUND_MARINE_POINTS_RECEIVED, - HUD_SOUND_MARINE_SOLDIER_LOST, - HUD_SOUND_MARINE_SENTRYFIRING, - HUD_SOUND_MARINE_SENTRYDAMAGED, - HUD_SOUND_MARINE_GIVEORDERS, - HUD_SOUND_MARINE_NEEDPORTAL, - HUD_SOUND_MARINE_GOTOALERT, - HUD_SOUND_MARINE_COMMANDERIDLE, - HUD_SOUND_MARINE_ARMORYUPGRADING, - - HUD_SOUND_ALIEN_ENEMY_APPROACHES, - HUD_SOUND_ALIEN_GAMEOVERMAN, - HUD_SOUND_ALIEN_HIVE_ATTACK, - HUD_SOUND_ALIEN_HIVE_COMPLETE, - HUD_SOUND_ALIEN_HIVE_DYING, - HUD_SOUND_ALIEN_LIFEFORM_ATTACK, - HUD_SOUND_ALIEN_RESOURCES_LOW, - HUD_SOUND_ALIEN_MESS, - HUD_SOUND_ALIEN_MORE, - HUD_SOUND_ALIEN_NEED_BETTER, - HUD_SOUND_ALIEN_NEED_BUILDERS, - HUD_SOUND_ALIEN_NEW_TRAIT, - HUD_SOUND_ALIEN_NOW_DONCE, - HUD_SOUND_ALIEN_POINTS_RECEIVED, - HUD_SOUND_ALIEN_RESOURCES_ATTACK, - HUD_SOUND_ALIEN_STRUCTURE_ATTACK, - HUD_SOUND_ALIEN_UPGRADELOST, - - HUD_SOUND_ORDER_MOVE, - HUD_SOUND_ORDER_ATTACK, - HUD_SOUND_ORDER_BUILD, - HUD_SOUND_ORDER_WELD, - HUD_SOUND_ORDER_GUARD, - HUD_SOUND_ORDER_GET, - HUD_SOUND_ORDER_COMPLETE, - - HUD_SOUND_GAMESTART, - HUD_SOUND_YOU_WIN, - HUD_SOUND_YOU_LOSE, - HUD_SOUND_TOOLTIP, - // joev: bug 0000767 - HUD_SOUND_PLAYERJOIN, - // :joev - HUD_SOUND_MAX -} AvHHUDSound; - -typedef vector HUDSoundListType; - -//typedef enum -//{ -// ARMOR_BASE = 1, -// ARMOR_HEAVY = 2, -// ARMOR_LIFESUPPORT = 4, -// ARMOR_JETPACK = 8, -// ARMOR_MOTIONTRACK = 16 -//} AvHMarineArmor; - -// Location orders, global orders -typedef enum -{ - ORDERTYPE_UNDEFINED = 0, - ORDERTYPEL_DEFAULT, - ORDERTYPEL_MOVE, - - ORDERTYPET_ATTACK, - ORDERTYPET_BUILD, - ORDERTYPET_GUARD, - ORDERTYPET_WELD, - ORDERTYPET_GET, - - ORDERTYPEG_HOLD_POSITION, - ORDERTYPEG_CODE_DEPLOY_MINES, - ORDERTYPEG_CODE_GREEN, - ORDERTYPEG_CODE_YELLOW, - ORDERTYPEG_CODE_RED, - - ORDERTYPE_MAX -} -AvHOrderType; - -typedef enum -{ - ORDERTARGETTYPE_UNDEFINED = 0, - ORDERTARGETTYPE_GLOBAL = 1, - ORDERTARGETTYPE_LOCATION = 2, - ORDERTARGETTYPE_TARGET = 3 -} -AvHOrderTargetType; - -extern const char* getModDirectory(void); -extern const char* getModName(void); - -#define kSpriteDirectory "sprites" -#define kMiniMapSpritesDirectory "sprites/minimaps" -#define kTechTreeSpriteDirectory "sprites/techtree" -#define kTechTreeSpritePrefix "tech" -#define kScriptsDirectory "scripts" -#define kSoundDirectory "sound" -#define kMapDirectory "maps" -#define kMusicDirectory "music" -#define kBasePSName "ns.ps" - -// Entities -#define kesDeathMatchStart "info_player_deathmatch" -#define kesTerroristStart "info_player_deathmatch" -#define kesCounterTerroristStart "info_player_start" - -#define kesFuncDoor "func_door" -#define kesFuncWall "func_wall" -#define kesEnvSprite "env_sprite" -#define kesFuncIllusionary "func_illusionary" - -#define kesReadyRoomStart "info_player_start" -#define keTeamStart info_team_start -#define kesTeamStart "info_team_start" -#define keLeaveGame info_leave_game -#define keJoinTeam info_join_team -#define keSpectate info_spectate -#define keAutoAssign info_join_autoassign -#define keMapInfo info_mapinfo -#define kesMapInfo "info_mapinfo" -#define keGameplay info_gameplay -#define kesGameplay "info_gameplay" - -//#define keGlow glow -#define keGamma env_gamma -#define kesGamma "env_gamma" -#define keParticles env_particles -#define kesParticles "env_particles" -#define keParticlesCustom env_particles_custom -#define kesParticlesCustom "env_particles_custom" - -//#define keResource resource -#define keWeldable func_weldable -#define kesWeldable "func_weldable" - -#define keSeethrough func_seethrough -#define kesSeethrough "func_seethrough" - -#define keSeethroughDoor func_seethroughdoor -#define kesSeethroughDoor "func_seethroughdoor" - -//#define keWaypoint func_waypoint -//#define kesWaypoint "func_waypoint" - -#define keNoBuild func_nobuild -#define kesNoBuild "func_nobuild" - -#define keFuncResource func_resource -#define kesFuncResource "func_resource" - -#define keMP3Audio target_mp3audio -#define kesMP3Audio "target_mp3audio" - -#define keFog env_fog -#define kesFog "env_fog" - -#define keInfoLocation info_location -#define kesInfoLocation "info_location" - -#define keTeamHive team_hive -#define kesTeamHive "team_hive" - -#define keTeamEgg team_egg -#define kesTeamEgg "team_egg" - -#define keTriggerRandom trigger_random -#define kesTriggerRandom "trigger_random" - -#define keTriggerPresence trigger_presence -#define kesTriggerPresence "trigger_presence" - -#define keTriggerScript trigger_script -#define kesTriggerScript "trigger_script" - -#define keTeamWebStrand team_webstrand -#define kesTeamWebStrand "team_webstrand" - -// Targets fired by game -#define ktGameStartedStatus "gamestartedstatus" -#define ktGameReset "gamereset" - -// Weapons/Equipment -#define kwsKnife "weapon_knife" -#define kwKnife weapon_knife -#define kwsGrenade "weapon_grenade" -#define kwGrenade weapon_grenade -#define kwsMachineGun "weapon_machinegun" -#define kwMachineGun weapon_machinegun -#define kwsPistol "weapon_pistol" -#define kwPistol weapon_pistol -#define kwsShotGun "weapon_shotgun" -#define kwShotGun weapon_shotgun -#define kwsHeavyMachineGun "weapon_heavymachinegun" -#define kwHeavyMachineGun weapon_heavymachinegun -#define kwsGrenadeGun "weapon_grenadegun" -#define kwGrenadeGun weapon_grenadegun -#define kwsNukeGun "weapon_nukegun" -#define kwNukeGun weapon_nukegun -#define kwsFlameGun "weapon_flamegun" -#define kwFlameGun weapon_flamegun - -// Weapon that dispenses mines -#define kwsMine "weapon_mine" -#define kwMine weapon_mine - -// Deployed mines -#define kwDeployedMine item_mine -#define kwsDeployedMine "item_mine" - -#define kwsDeployedTurret "turret" -#define kwDeployedTurret turret - -#define kwsHealth "item_health" -#define kwHealth item_health - -#define kwsCatalyst "item_catalyst" -#define kwCatalyst item_catalyst - -#define kwsGenericAmmo "item_genericammo" -#define kwGenericAmmo item_genericammo - -#define kwsHeavyArmor "item_heavyarmor" -#define kwHeavyArmor item_heavyarmor - -#define kwsJetpack "item_jetpack" -#define kwJetpack item_jetpack - -#define kwsAmmoPack "item_ammopack" -#define kwAmmoPack item_ammopack - -// Alien weapons -#define kwsParalysisGun "weapon_paralysis" -#define kwParalysisGun weapon_paralysis - -#define kwsSpitGun "weapon_spit" -#define kwSpitGun weapon_spit - -#define kwClaws weapon_claws -#define kwsClaws "weapon_claws" - -#define kwSwipe weapon_swipe -#define kwsSwipe "weapon_swipe" - -#define kwSporeGun weapon_spore -#define kwsSporeGun "weapon_spore" - -// Alien projectiles -#define kwsSpitProjectile "spitgunprojectile" -#define kwSpitProjectile spitgunprojectile - -#define kwSporeProjectile sporegunprojectile -#define kwsSporeProjectile "sporegunprojectile" - -#define kwSpikeGun weapon_spikegun -#define kwsSpikeGun "weapon_spikegun" - -#define kwBiteGun weapon_bitegun -#define kwsBiteGun "weapon_bitegun" - -#define kwBite2Gun weapon_bite2gun -#define kwsBite2Gun "weapon_bite2gun" - -#define kwResourceTowerGun weapon_resourcetowergun -#define kwsResourceTowerGun "weapon_resourcetowergun" - -#define kwOffenseChamberGun weapon_offensechambergun -#define kwsOffenseChamberGun "weapon_offensechambergun" - -#define kwDefenseChamberGun weapon_defensechambergun -#define kwsDefenseChamberGun "weapon_defensechambergun" - -#define kwSensoryChamberGun weapon_sensorychambergun -#define kwsSensoryChamberGun "weapon_sensorychambergun" - -#define kwMovementChamberGun weapon_movementchambergun -#define kwsMovementChamberGun "weapon_movementchambergun" - -#define kwHiveGun weapon_hivegun -#define kwsHiveGun "weapon_hivegun" - -#define kwHealingSpray weapon_healingspray -#define kwsHealingSpray "weapon_healingspray" - -#define kwMetabolize weapon_metabolize -#define kwsMetabolize "weapon_metabolize" - -#define kwWebSpinner weapon_webspinner -#define kwsWebSpinner "weapon_webspinner" - -#define kwsWebProjectile "webgunprojectile" -#define kwWebProjectile webgunprojectile - -#define kwBabblerGun weapon_babblergun -#define kwsBabblerGun "weapon_babblergun" - -#define kwBabblerProjectile weapon_babblerprojectile -#define kwsBabblerProjectile "weapon_babblerprojectile" - -#define kwPrimalScream weapon_primalscream -#define kwsPrimalScream "weapon_primalscream" - -#define kwLeap weapon_leap -#define kwsLeap "weapon_leap" - -#define kwAmplify weapon_amplify -#define kwsAmplify "weapon_amplify" - -#define kwStomp weapon_stomp -#define kwsStomp "weapon_stomp" - -#define kwStompProjectile stompprojectile -#define kwsStompProjectile "stompprojectile" - -#define kwDevour weapon_devour -#define kwsDevour "weapon_devour" - -#define kwCharge weapon_charge -#define kwsCharge "weapon_charge" - -#define kwsParasiteGun "weapon_parasite" -#define kwParasiteGun weapon_parasite - -#define kwsUmbraCloud "umbracloud" -#define kwUmbraCloud umbracloud -#define kwsUmbraProjectile "umbraprojectile" -#define kwUmbraProjectile umbraprojectile - -#define kwsUmbraGun "weapon_umbra" -#define kwUmbraGun weapon_umbra - -#define kwsBlinkGun "weapon_blink" -#define kwBlinkGun weapon_blink - -#define kwsDivineWind "weapon_divinewind" -#define kwDivineWind weapon_divinewind - -#define kwBileBomb weapon_bilebomb -#define kwsBileBomb "weapon_bilebomb" - -#define kwBileBombGun weapon_bilebombgun -#define kwsBileBombGun "weapon_bilebombgun" - -#define kwAcidRocket weapon_acidrocket -#define kwsAcidRocket "weapon_acidrocket" - -#define kwAcidRocketGun weapon_acidrocketgun -#define kwsAcidRocketGun "weapon_acidrocketgun" - -// Debug item -#define kwsDebugEntity "item_genericammo" - -// Filenames -#define kMOTDName "motd.txt" - -// Tech node prefix (skill.cfg) -#define kTechCostPrefix "avh_techcost_" -#define kTechHealthPrefix "avh_techhealth_" -#define kTechTimePrefix "avh_techtime_" - -// Player models -#define kNullModel "models/null.mdl" -#define kReadyRoomModel "models/player.mdl" -#define kMarineSoldierModel "models/player/soldier/soldier.mdl" -#define kHeavySoldierModel "models/player/heavy/heavy.mdl" -#define kMarineCommanderModel "models/player/commander/commander.mdl" -#define kAlienLevelOneModel "models/player/alien1/alien1.mdl" -#define kAlienLevelTwoModel "models/player/alien2/alien2.mdl" -#define kAlienLevelThreeModel "models/player/alien3/alien3.mdl" -#define kAlienLevelFourModel "models/player/alien4/alien4.mdl" -#define kAlienLevelFiveModel "models/player/alien5/alien5.mdl" -#define kAlienGestateModel "models/player/gestate/gestate.mdl" - -//#define kAlienAbilitiesGrantedSound "misc/a-abilities_granted.wav" -//#define kAlienAbilitiesLostSound "misc/a-abilities_lost.wav" -#define kAlienBuildingSound1 "misc/a-build1.wav" -#define kAlienBuildingSound2 "misc/a-build2.wav" -#define kDistressBeaconSound "misc/distressbeacon.wav" - -#define kBuildableRecycleSound "misc/b_recycle.wav" -#define kBuildableHurt1Sound "misc/b_hurt1.wav" -#define kBuildableHurt2Sound "misc/b_hurt2.wav" -#define kElectricalSprite "sprites/lgtning.spr" - -// Model names for key -#define kSoldierName "soldier" -#define kHeavyName "heavy" -#define kCommanderName "commander" -#define kAlien1Name "alien1" -#define kAlien2Name "alien2" -#define kAlien3Name "alien3" -#define kAlien4Name "alien4" -#define kAlien5Name "alien5" -#define kAlienGestationName "gestate" - -// Sound lists -#define kPlayerLevelAttackSoundList "player/role%d_attack" -#define kPlayerLevelDieSoundList "player/role%d_die" -#define kPlayerLevelIdleSoundList "player/role%d_idle" -#define kPlayerLevelMoveSoundList "player/role%d_move" -#define kPlayerLevelPainSoundList "player/role%d_pain" -#define kPlayerLevelSpawnSoundList "player/role%d_spawn" -#define kPlayerLevelWoundSoundList "player/role%d_wound" -#define kHiveWoundSoundList "misc/hive_wound" - -#define kFallPainSoundFormat "player/pl_fallpain3-%d.wav" - - -#define kDigestingSound "player/digesting.wav" - -// Not quite a sound list -//#define kHiveWoundSoundPrefix "misc/hive_wound" - -#define kPieSelectForwardSound "hud/select_node_forward.wav" -#define kPieSelectBackwardSound "hud/select_node_backward.wav" -#define kPieSelectForwardAlienSound "hud/select_node_forward-a.wav" -#define kPieSelectBackwardAlienSound "hud/select_node_backward-a.wav" -#define kSelectSound "hud/select.wav" -#define kSelectAlienSound "hud/select-a.wav" -#define kSquad1Sound "hud/squad1.wav" -#define kSquad2Sound "hud/squad2.wav" -#define kSquad3Sound "hud/squad3.wav" -#define kSquad4Sound "hud/squad4.wav" -#define kSquad5Sound "hud/squad5.wav" - -#define kMarineSquad1Sound "hud/m-squad1.wav" -#define kMarineSquad2Sound "hud/m-squad2.wav" -#define kMarineSquad3Sound "hud/m-squad3.wav" -#define kMarineSquad4Sound "hud/m-squad4.wav" -#define kMarineSquad5Sound "hud/m-squad5.wav" - -#define kPlaceBuildingSound "hud/place_building.wav" -#define kCountdownSound "hud/countdown.wav" -#define kPointsSpentSound "hud/points_spent.wav" -#define kAlienPointsReceivedSound "hud/alien_points_received.wav" -#define kMarinePointsReceivedSound "hud/marine_points_received.wav" - -#define kMarineResearchComplete "hud/marine_research_complete.wav" -#define kMarineSoldierUnderAttack "hud/marine_soldierunderattack.wav" -#define kMarineCCOnline1 "hud/marine_cconline1.wav" -#define kMarineCCOnline2 "hud/marine_cconline2.wav" -#define kMarineCCUnderAttack1 "hud/marine_ccunderattack1.wav" -#define kMarineCCUnderAttack2 "hud/marine_ccunderattack2.wav" -#define kMarineCommanderEjected "hud/marine_commander_ejected.wav" -#define kMarineBaseUnderAttack1 "hud/marine_baseattack1.wav" -#define kMarineBaseUnderAttack2 "hud/marine_baseattack2.wav" -#define kMarineMoreResources "hud/marine_more.wav" -#define kMarineLowResources "hud/marine_lowresources.wav" -#define kMarineNeedsAmmo1 "hud/marine_needsammo1.wav" -#define kMarineNeedsAmmo2 "hud/marine_needsammo2.wav" -#define kMarineNeedsHealth1 "hud/marine_needshealth1.wav" -#define kMarineNeedsHealth2 "hud/marine_needshealth2.wav" -#define kMarineNeedsOrder1 "hud/marine_needsorder1.wav" -#define kMarineNeedsOrder2 "hud/marine_needsorder2.wav" -#define kMarineSoldierLost1 "hud/marine_soldierlost1.wav" -#define kMarineSoldierLost2 "hud/marine_soldierlost2.wav" -#define kMarineSentryFiring1 "hud/marine_sentryfiring1.wav" -#define kMarineSentryFiring2 "hud/marine_sentryfiring2.wav" -#define kMarineSentryTakingDamage1 "hud/marine_sentrytakingdamage1.wav" -#define kMarineSentryTakingDamage2 "hud/marine_sentrytakingdamage2.wav" -#define kMarineUpgradeComplete "hud/marine_upgradecomplete.wav" -#define kMarineGiveOrders "hud/marine_giveorders.wav" -#define kMarineNeedPortal1 "hud/marine_needportal1.wav" -#define kMarineNeedPortal2 "hud/marine_needportal2.wav" -#define kMarineGotoAlert "hud/marine_gotoalert.wav" -#define kMarineCommanderIdle1 "hud/marine_commanderidle1.wav" -#define kMarineCommanderIdle2 "hud/marine_commanderidle2.wav" -#define kMarineArmoryUpgrading "hud/marine_armoryupgrading.wav" - -#define kAlienEnemyApproaches1 "hud/alien_enemyapproaches1.wav" -#define kAlienEnemyApproaches2 "hud/alien_enemyapproaches2.wav" -#define kAlienGameOverMan "hud/alien_gameoverman.wav" -#define kAlienHiveAttack "hud/alien_hiveattack.wav" -#define kAlienHiveComplete1 "hud/alien_hivecomplete1.wav" -#define kAlienHiveComplete2 "hud/alien_hivecomplete2.wav" -#define kAlienHiveDying1 "hud/alien_hivedying1.wav" -#define kAlienHiveDying2 "hud/alien_hivedying2.wav" -#define kAlienLifeformAttack1 "hud/alien_lifeformattack1.wav" -#define kAlienLifeformAttack2 "hud/alien_lifeformattack2.wav" -#define kAlienLowResources "hud/alien_lowresources.wav" -#define kAlienMess "hud/alien_mess.wav" -#define kAlienMoreResources1 "hud/alien_more1.wav" -#define kAlienMoreResources2 "hud/alien_more2.wav" -#define kAlienNeedBetter "hud/alien_needbetter.wav" -#define kAlienNeedBuilders1 "hud/alien_needbuilders1.wav" -#define kAlienNeedBuilders2 "hud/alien_needbuilders2.wav" -#define kAlienNewTrait1 "hud/alien_newtrait1.wav" -#define kAlienNewTrait2 "hud/alien_newtrait2.wav" -#define kAlienNowDonce "hud/alien_now.wav" -#define kAlienResourceAttack1 "hud/alien_resourceattack1.wav" -#define kAlienResourceAttack2 "hud/alien_resourceattack2.wav" -#define kAlienSeeDead "hud/alien_seedead.wav" -#define kAlienStructureAttack1 "hud/alien_structureattack1.wav" -#define kAlienStructureAttack2 "hud/alien_structureattack2.wav" -#define kAlienUpgradeLost "hud/alien_upgrade_lost.wav" - -#define kSoundOrderMove1 "hud/marine_order_move1.wav" -#define kSoundOrderMove2 "hud/marine_order_move2.wav" -#define kSoundOrderMove3 "hud/marine_order_move3.wav" -#define kSoundOrderMove4 "hud/marine_order_move4.wav" -#define kSoundOrderAttack "hud/marine_order_attack.wav" -#define kSoundOrderBuild "hud/marine_order_build.wav" -#define kSoundOrderWeld "hud/marine_order_weld.wav" -#define kSoundOrderGuard "hud/marine_order_guard.wav" -#define kSoundOrderGet "hud/marine_order_get.wav" -#define kSoundOrderComplete1 "hud/marine_order_complete1.wav" -#define kSoundOrderComplete2 "hud/marine_order_complete2.wav" -#define kSoundOrderComplete3 "hud/marine_order_complete3.wav" -#define kSoundOrderComplete4 "hud/marine_order_complete4.wav" -#define kSoundOrderComplete5 "hud/marine_order_complete5.wav" -#define kSoundOrderComplete6 "hud/marine_order_complete6.wav" - -#define kAlienGameStart1 "hud/alien_gamestart1.wav" -#define kAlienGameStart2 "hud/alien_gamestart2.wav" -#define kMarineGameStart1 "hud/marine_gamestart1.wav" -#define kMarineGameStart2 "hud/marine_gamestart2.wav" -#define kYouWinSound "hud/you_win.wav" -#define kYouLoseSound "hud/you_lose.wav" -#define kTooltipSound "hud/tooltip.wav" -#define kMyHiveEasterEgg "hud/alien_myhive.wav" - -// Tech category names -#define kWeaponTechCategory "WeaponCategory" -#define kArmorTechCategory "ArmorCategory" -#define kBuildTechCategory "BuildCategory" -#define kRadioTechCategory "RadioCategory" - -// Tech button components are the category plus this suffix -//#define kMessageButtonsSuffix "Buttons" -#define kTechNodeLabelPrefix "TechNodeLabel_" -#define kTechNodeHelpPrefix "TechNodeHelp_" -#define kPrerequisitePrefix "Prerequisite" -#define kUser3Name "User3Name_" -#define kUser3Description "User3Desc_" -#define kUser3FriendlyDescription "User3FriendlyDesc_" -#define kUser3CommanderDescription "User3CommanderDesc_" -#define kBlipStatusPrefix "BlipStatus_" -#define kMessageButtonCost "Cost" -#define kNotFullyBuilt "NotFullyBuilt" -#define kPointsSuffix "Points" - -const float kSquadHierarchyScaleFactor = .0001f; -const float kCommanderHierarchyScaleFactor = .0002f; - -// Sayings -#define kSoldierSayingList "vox/ssay%d" -#define kCommanderSayingList "vox/csay%d" -#define kAlienSayingList "vox/asay%d" - -#define kSoldierOrderRequestList "vox/sreq" -#define kSoldierOrderAckList "vox/sack" - -// Other sounds -#define kJetpackSound "misc/jetpack.wav" -//#define kAdrenalineSound "misc/adren.wav" -#define kGestationSound "misc/gestate.wav" -#define kConnectSound "misc/connect.wav" -//#define kDisconnectSound "misc/disconnect.wav" -#define kEmptySound "weapons/357_cock1.wav" -#define kInvalidSound "misc/invalid.wav" -#define kLevelUpMarineSound "misc/levelup.wav" -#define kLevelUpAlienSound "misc/a-levelup.wav" -// joev: bug 0000767 -#define kPlayerJoinedSound "player/jointeam.wav" -// :joev - -// Events -#define kJetpackEvent "events/Jetpack.sc" -#define kStartOverwatchEvent "events/StartOverwatch.sc" -#define kEndOverwatchEvent "events/EndOverwatch.sc" - -#define kStopVoiceSoundEvent "events/StopVoice.sc" - -#define kRegenerationEvent "events/Regeneration.sc" -#define kStartCloakEvent "events/StartCloak.sc" -#define kEndCloakEvent "events/EndCloak.sc" - -#define kWallJumpEvent "events/WallJump.sc" -#define kFlightEvent "events/Flight.sc" -#define kTeleportEvent "events/Teleport.sc" -#define kSiegeHitEvent "events/SiegeHit.sc" -#define kSiegeViewHitEvent "events/SiegeViewHit.sc" -#define kPhaseInEvent "events/PhaseIn.sc" -#define kCommanderPointsAwardedEvent "events/CommandPoints.sc" -#define kEmptySoundEvent "events/Empty.sc" -#define kNumericalInfoEvent "events/NumericalInfo.sc" -#define kAlienSightOnEvent "events/AlienSightOn.sc" -#define kAlienSightOffEvent "events/AlienSightOff.sc" -#define kInvalidActionEvent "events/InvalidAction.sc" -#define kParticleEvent "events/Particle.sc" -#define kDistressBeaconEvent "events/DistressBeacon.sc" -#define kWeaponAnimationEvent "events/WeaponAnimation.sc" -#define kLevelUpEvent "events/LevelUp.sc" -#define kAbilityEventName "events/Ability.sc" - -// Targets -#define kTargetCommandStationUseTeamOne "commandstationuse1" -#define kTargetCommandStationUseTeamTwo "commandstationuse2" - -#define kTargetCommandStationLogoutTeamOne "commandstationlogout1" -#define kTargetCommandStationLogoutTeamTwo "commandstationlogout2" - -#define kTargetCommandStationDestroyedTeamOne "commandstationdestroyed1" -#define kTargetCommandStationDestroyedTeamTwo "commandstationdestroyed2" - -#define kReadyNotification "ready" -#define kNotReadyNotification "notready" - -const int kOverwatchBreakingVelocity = 5; -const float kOverwatchAcquireTime = 6.0f; -const float kOverwatchLostTargetTime = 4.0f; -const float kOverwatchKeepFiringAfterMissingTargetTime = 1.0f; -const float kSpeakingTime = 4.0f; -const float kEnemySightedTime = 2.0f; - -const int kDefaultViewHeight = 0; -const float kDefaultMinMapExtent = -4000; -const float kDefaultMaxMapExtent = 4000; - -// Energy constants for marine structures -const float kMarineStructureEnergyRate = .33f; -const float kMarineStructureMaxEnergy = 100; - -const int kShellRenderAmount = 50; -const int kInvulShellRenderAmount = 15; - -// This is one less then 4096 because we need top bit for sign. -// Only allow map dimensions of this size or lower -const float kMaxMapDimension = 4095; - -const float kDefaultMapGamma = 1.0f; - -const float kNormalizationNetworkFactor = 1000.0f; -const float kHotKeyNetworkFactor = 100.0f; - -const int kDetectionDistance = 500; -const float kMaxRelevantCullDistance = 1024; - -const int kHiveXYDistanceTolerance = 400; -const float kBaseHealthPercentage = .5f; - -const float kHUDSoundVolume = .3f; - -#define kHotKeyPrefix "hotkey" - -// How many directions can bullets travel in within spread vector? -//const int kBulletSpreadGranularity = 15; - -const int iNumberOfTeamColors = 6; -extern int kTeamColors[iNumberOfTeamColors][3]; -extern float kFTeamColors[iNumberOfTeamColors][3]; - -// Random resource constants -const int kNumSharesPerPlayer = 3; -const int kTotalShares = MAX_PLAYERS_PER_TEAM*kNumSharesPerPlayer; - -const int kNumericalInfoResourcesEvent = 0; -const int kNumericalInfoHealthEvent = 1; -const int kNumericalInfoResourcesDonatedEvent = 2; -const int kNumericalInfoAmmoEvent = 3; - -const int kGameStatusReset = 0; -const int kGameStatusResetNewMap = 1; -const int kGameStatusEnded = 2; -const int kGameStatusGameTime = 3; -const int kGameStatusUnspentLevels = 4; - -// Max message length is 192, take into account rest of message -const int kMaxPlayerSendMessageLength = 160; - -const int kTopDownYaw = 90; -const int kTopDownPitch = 0; -const int kTopDownRoll = -90; - -// Extra fudge factor to make sure players don't get stuck when respawning -const int kRespawnFudgeFactorHeight = 1; - -#endif +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHConstants.h$ +// $Date: 2002/11/12 02:23:01 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHConstants.h,v $ +// Revision 1.61 2002/11/12 02:23:01 Flayra +// - Renamed avhplayer for 3rd party compatibility (adminmod, etc.) +// +// Revision 1.60 2002/11/06 01:39:48 Flayra +// - Regeneration now heals a percentage +// +// Revision 1.59 2002/11/05 06:17:25 Flayra +// - Balance changes +// +// Revision 1.58 2002/11/03 04:50:19 Flayra +// - Hard-coded gameplay constants instead of putting in skill.cfg +// +// Revision 1.57 2002/10/24 21:23:01 Flayra +// - Reworked jetpacks +// - Added alien easter eggs +// +// Revision 1.56 2002/10/18 22:18:44 Flayra +// - Added alien easter egg sayings +// - Added sensory chamber sayings +// - Limit number of buildings in radius +// - Fixed motd.txt length crash +// +// Revision 1.55 2002/10/16 20:51:44 Flayra +// - Hive health while gestating +// +// Revision 1.54 2002/10/16 00:52:45 Flayra +// - Added "need order" alert +// - Added authentication mask +// - Updated alien building sounds +// - Removed a couple unneeded sounds +// +// Revision 1.53 2002/10/03 18:41:58 Flayra +// - Added alien HUD sounds +// - Added more order sounds +// +// Revision 1.52 2002/09/25 20:43:40 Flayra +// - Removed use order, sound update +// +// Revision 1.51 2002/09/23 22:12:07 Flayra +// - New CC sounds +// - Regular update +// +// Revision 1.50 2002/09/09 19:49:25 Flayra +// - Added deathmessage cheat +// +// Revision 1.49 2002/08/31 18:01:01 Flayra +// - Work at VALVe +// +// Revision 1.48 2002/08/16 02:33:57 Flayra +// - Regular update +// +// Revision 1.47 2002/08/09 00:56:18 Flayra +// - Added "adjustscore" cheat for testing new scoreboard +// +// Revision 1.46 2002/08/02 22:01:04 Flayra +// - Regular update +// +// Revision 1.45 2002/07/28 19:38:59 Flayra +// - Linux path fixes +// +// Revision 1.44 2002/07/26 23:04:08 Flayra +// - Generate numerical feedback for damage events +// +// Revision 1.43 2002/07/23 17:01:02 Flayra +// - Regular update +// +// Revision 1.42 2002/07/08 16:50:20 Flayra +// - Reworked team colors, #define a few more sounds, weapon spread +// +// Revision 1.41 2002/07/01 22:41:40 Flayra +// - Removed outdated overwatch target and tension events +// +// Revision 1.40 2002/07/01 21:25:26 Flayra +// - Added new alien weapons, added build ranges +// +// Revision 1.39 2002/06/25 17:53:19 Flayra +// - Regular update (cleanup, new entities, new classnames) +// +// Revision 1.38 2002/06/10 19:51:29 Flayra +// - Regular update +// +// Revision 1.37 2002/06/03 16:41:50 Flayra +// - Removed duplicate hive class name, added more player class types for scoreboard info +// +// Revision 1.36 2002/05/28 17:37:48 Flayra +// - Reinforcment refactoring, renamed role sounds to be less confusing +// +// Revision 1.35 2002/05/23 02:34:00 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifndef AVHCONSTANTS_H +#define AVHCONSTANTS_H + +#include "types.h" +#include "game_shared/teamconst.h" + +// Basic constants +const int kMaxPlayers = 32; +const int kMaxEntities = 1500; +const int kSelectionStartRange = 50; +const int kSelectionEndRange = 8012; +const int kSelectionExtreme = 8012; +const float kSelectionNetworkConstant = 1000.0f; +const float kBuildInvalidRangeMultiplier = 2.0f; +const float kWorldPosNetworkConstant = 6.0f; +const float kNumericNetworkConstant = 100.0f; +const int kWorldMinimumZ = -4096; +const int kMaxGroundPlayerSpeed = 1200; +const int kNumHotkeyGroups = 5; +const int kSelectAllHotGroup = kNumHotkeyGroups + 1; + +const int kNumRequestTypes = 3; +const int kPendingIdleSoldiers = (kNumHotkeyGroups + 1); +const int kPendingAmmoRequests = (kNumHotkeyGroups + 2); +const int kPendingHealthRequests = (kNumHotkeyGroups + 3); + +const int kMaxHives = 3; +const int kMaxUpgradeCategories = 3; +const int kMaxUpgradeLevel = 3; + +const int kFuncResourceMaxResources = 300; +//const float kPlayerResourceScalar = 2.0f; // 1.5 might be better +const int kVictoryIntermission = 12; +const int kResetPlayersPerSecond = 5; +const float kBuildingUseWarmupTime = 2.0f; +const float kRedeemInvulnerableTime = 1.0f; + +const float kMaxBuildingDropSlope = 1.0f; +const float kMaxEquipmentDropSlope = 1.0f; + +// Used to be cl_forwardspeed, cl_backspeed, cl_sidespeed. +const float kForwardSpeed = 1000.0f; +const float kBackSpeed = 1000.0f; +const float kSideSpeed = 1000.0f; + +// Name this guy for custom propagation, maybe +typedef int EntityInfo; +typedef vector EntityListType; +typedef pair EntityFloatPairType; +typedef vector EntityFloatPairListType; +typedef map EntityMap; +typedef vector IntList; + +#define kAvHGameName ((const char*)("Natural Selection")) +#define kAvHGameAcronymn ((const char*)("NS")) +#define kInvalidTeamName "Invalid team name" +#define kAvHPlayerClassName "player" +#define kwsWeldableClassName "avhweldable" +#define kwsMapInfoClassName "avhmapinfo" +#define kwsGameplayClassName "avhgameplay" +#define kwsGammaClassName "avhgamma" + +#define kNumAlienLifeforms 5 +#define kNumAlienUpgrades 12 +#define kNumAlienUpgradeCategories 3 +#define kNumUpgradesInCategory 3 +#define kNumUpgradeLevelsInCategory 3 + +#define kMaxAlienHives 3 +#define kMaxAlienEnergy 100 +#define kMaxAlienResources 100 + +typedef enum +{ + AVH_CLASS_TYPE_UNDEFINED = 0, + AVH_CLASS_TYPE_MARINE = 1, + AVH_CLASS_TYPE_ALIEN = 2, + AVH_CLASS_TYPE_AUTOASSIGN = 3 +} AvHClassType; + +typedef enum +{ + MAP_MODE_UNDEFINED = 0, + MAP_MODE_NS = 1, + MAP_MODE_CS = 2, + MAP_MODE_DM = 3, + MAP_MODE_CO = 4 +} +AvHMapMode; + +#define kUndefinedTeam "undefinedteam" +#define kMarine1Team "marine1team" +#define kAlien1Team "alien1team" +#define kMarine2Team "marine2team" +#define kAlien2Team "alien2team" +#define kSpectatorTeam "spectatorteam" +#define kTeamString "undefinedteam;marine1team;alien1team;marine2team;alien2team;spectatorteam" + +#define kMarinePrettyName "Frontiersmen" +#define kAlienPrettyName "Kharaa" +#define kUndefPrettyName "Undefined" + +typedef enum +{ + AUTH_ACTION_JOIN_TEAM = 0, + AUTH_ACTION_ADJUST_BALANCE = 1 +} AvHAuthAction; + +typedef enum +{ + PLAYMODE_UNDEFINED = 0, + PLAYMODE_READYROOM = 1, + PLAYMODE_PLAYING = 2, + PLAYMODE_AWAITINGREINFORCEMENT = 3, // Player is dead and waiting in line to get back in + PLAYMODE_REINFORCING = 4, // Player is in the process of coming back into the game + PLAYMODE_OBSERVER = 5, + PLAYMODE_REINFORCINGCOMPLETE = 6 // Combat only: 'press fire to respawn' +} AvHPlayMode; + +typedef enum +{ + TEAM_IND = 0, + TEAM_ONE = 1, + TEAM_TWO = 2, + TEAM_THREE = 3, + TEAM_FOUR = 4, + TEAM_SPECT = 5, + TEAM_ACTIVE_BEGIN = 1, + TEAM_ACTIVE_END = 5 +} AvHTeamNumber; + +typedef enum +{ + ALERT_NONE = 0, + ALERT_PLAYER_ENGAGE = 1, + ALERT_UNDER_ATTACK = 2, + ALERT_RESEARCH_COMPLETE = 3, + ALERT_UPGRADE_COMPLETE = 4, + ALERT_LOW_RESOURCES = 5, + ALERT_SOLDIER_NEEDS_AMMO = 6, + ALERT_SOLDIER_NEEDS_HEALTH = 7, + ALERT_PLAYER_DIED = 8, + ALERT_SENTRY_FIRING = 9, + ALERT_SENTRY_DAMAGED = 10, + ALERT_HIVE_COMPLETE = 11, + ALERT_HIVE_DYING = 12, + ALERT_NEW_TRAIT = 13, + ALERT_ORDER_NEEDED = 14, + ALERT_ORDER_COMPLETE = 15, + ALERT_HIVE_DEFEND = 16, + ALERT_MAX_ALERTS = 17 +} AvHAlertType; + +typedef enum +{ + PLAYERCLASS_NONE = 0, + PLAYERCLASS_ALIVE_MARINE, + PLAYERCLASS_ALIVE_JETPACK_MARINE, + PLAYERCLASS_ALIVE_HEAVY_MARINE, + PLAYERCLASS_ALIVE_LEVEL1, + PLAYERCLASS_ALIVE_LEVEL2, + PLAYERCLASS_ALIVE_LEVEL3, + PLAYERCLASS_ALIVE_LEVEL4, + PLAYERCLASS_ALIVE_LEVEL5, + PLAYERCLASS_ALIVE_DIGESTING, + PLAYERCLASS_ALIVE_GESTATING, + PLAYERCLASS_DEAD_MARINE, + PLAYERCLASS_DEAD_ALIEN, + PLAYERCLASS_COMMANDER, + PLAYERCLASS_REINFORCING, + PLAYERCLASS_SPECTATOR, + PLAYERCLASS_REINFORCINGCOMPLETE +} AvHPlayerClass; + +// : 0001073 +#ifdef USE_OLDAUTH +// This is a mask because players can have more then one of these +typedef enum +{ + PLAYERAUTH_NONE = 0, + PLAYERAUTH_DEVELOPER = 1, + PLAYERAUTH_GUIDE = 2, + PLAYERAUTH_SERVEROP = 4, + PLAYERAUTH_PLAYTESTER = 8, + PLAYERAUTH_CONTRIBUTOR = 16, + PLAYERAUTH_CHEATINGDEATH = 32, + PLAYERAUTH_VETERAN = 64, + PLAYERAUTH_BETASERVEROP = 128, + PLAYERAUTH_PENDING = 256, + PLAYERAUTH_CUSTOM = 512, + PLAYERAUTH_UPP_MODE = 16384 +} AvHPlayerAuthentication; +#else +// Mask replaced with explicit string set for GetIsMember function +const static string PLAYERAUTH_DEVELOPER("dev"); +const static string PLAYERAUTH_PLAYTESTER("pt"); +const static string PLAYERAUTH_GUIDE("guide"); +const static string PLAYERAUTH_CONTRIBUTOR("const"); +const static string PLAYERAUTH_SERVEROP("op"); +const static string PLAYERAUTH_VETERAN("vet"); +const static string PLAYERAUTH_BETASERVEROP("betaop"); +#endif + +typedef enum +{ + HUD_SOUND_INVALID = 0, + HUD_SOUND_POINTS_SPENT, + HUD_SOUND_COUNTDOWN, + HUD_SOUND_SELECT, + HUD_SOUND_SQUAD1, + HUD_SOUND_SQUAD2, + HUD_SOUND_SQUAD3, + HUD_SOUND_SQUAD4, + HUD_SOUND_SQUAD5, + HUD_SOUND_PLACE_BUILDING, + HUD_SOUND_MARINE_RESEARCHCOMPLETE, + HUD_SOUND_MARINE_SOLDIER_UNDER_ATTACK, + HUD_SOUND_MARINE_BASE_UNDER_ATTACK, + HUD_SOUND_MARINE_UPGRADE_COMPLETE, + HUD_SOUND_MARINE_MORE, + HUD_SOUND_MARINE_SOLDIERLOST, + HUD_SOUND_MARINE_CCONLINE, + HUD_SOUND_MARINE_CCUNDERATTACK, + HUD_SOUND_MARINE_COMMANDER_EJECTED, + HUD_SOUND_MARINE_RESOURCES_LOW, + HUD_SOUND_MARINE_NEEDS_AMMO, + HUD_SOUND_MARINE_NEEDS_HEALTH, + HUD_SOUND_MARINE_NEEDS_ORDER, + HUD_SOUND_MARINE_POINTS_RECEIVED, + HUD_SOUND_MARINE_SOLDIER_LOST, + HUD_SOUND_MARINE_SENTRYFIRING, + HUD_SOUND_MARINE_SENTRYDAMAGED, + HUD_SOUND_MARINE_GIVEORDERS, + HUD_SOUND_MARINE_NEEDPORTAL, + HUD_SOUND_MARINE_GOTOALERT, + HUD_SOUND_MARINE_COMMANDERIDLE, + HUD_SOUND_MARINE_ARMORYUPGRADING, + + HUD_SOUND_ALIEN_ENEMY_APPROACHES, + HUD_SOUND_ALIEN_GAMEOVERMAN, + HUD_SOUND_ALIEN_HIVE_ATTACK, + HUD_SOUND_ALIEN_HIVE_COMPLETE, + HUD_SOUND_ALIEN_HIVE_DYING, + HUD_SOUND_ALIEN_LIFEFORM_ATTACK, + HUD_SOUND_ALIEN_RESOURCES_LOW, + HUD_SOUND_ALIEN_MESS, + HUD_SOUND_ALIEN_MORE, + HUD_SOUND_ALIEN_NEED_BETTER, + HUD_SOUND_ALIEN_NEED_BUILDERS, + HUD_SOUND_ALIEN_NEW_TRAIT, + HUD_SOUND_ALIEN_NOW_DONCE, + HUD_SOUND_ALIEN_POINTS_RECEIVED, + HUD_SOUND_ALIEN_RESOURCES_ATTACK, + HUD_SOUND_ALIEN_STRUCTURE_ATTACK, + HUD_SOUND_ALIEN_UPGRADELOST, + + HUD_SOUND_ORDER_MOVE, + HUD_SOUND_ORDER_ATTACK, + HUD_SOUND_ORDER_BUILD, + HUD_SOUND_ORDER_WELD, + HUD_SOUND_ORDER_GUARD, + HUD_SOUND_ORDER_GET, + HUD_SOUND_ORDER_COMPLETE, + + HUD_SOUND_GAMESTART, + HUD_SOUND_YOU_WIN, + HUD_SOUND_YOU_LOSE, + HUD_SOUND_TOOLTIP, + // : bug 0000767 + HUD_SOUND_PLAYERJOIN, + // : + HUD_SOUND_MAX +} AvHHUDSound; + +typedef vector HUDSoundListType; + +//typedef enum +//{ +// ARMOR_BASE = 1, +// ARMOR_HEAVY = 2, +// ARMOR_LIFESUPPORT = 4, +// ARMOR_JETPACK = 8, +// ARMOR_MOTIONTRACK = 16 +//} AvHMarineArmor; + +// Location orders, global orders +typedef enum +{ + ORDERTYPE_UNDEFINED = 0, + ORDERTYPEL_DEFAULT, + ORDERTYPEL_MOVE, + + ORDERTYPET_ATTACK, + ORDERTYPET_BUILD, + ORDERTYPET_GUARD, + ORDERTYPET_WELD, + ORDERTYPET_GET, + + ORDERTYPEG_HOLD_POSITION, + ORDERTYPEG_CODE_DEPLOY_MINES, + ORDERTYPEG_CODE_GREEN, + ORDERTYPEG_CODE_YELLOW, + ORDERTYPEG_CODE_RED, + + ORDERTYPE_MAX +} +AvHOrderType; + +typedef enum +{ + ORDERTARGETTYPE_UNDEFINED = 0, + ORDERTARGETTYPE_GLOBAL = 1, + ORDERTARGETTYPE_LOCATION = 2, + ORDERTARGETTYPE_TARGET = 3 +} +AvHOrderTargetType; + +extern const char* getModDirectory(void); +extern const char* getModName(void); + +#define kSpriteDirectory "sprites" +#define kMiniMapSpritesDirectory "sprites/minimaps" +#define kTechTreeSpriteDirectory "sprites/techtree" +#define kTechTreeSpritePrefix "tech" +#define kScriptsDirectory "scripts" +#define kSoundDirectory "sound" +#define kMapDirectory "maps" +#define kMusicDirectory "music" +#define kBasePSName "ns.ps" + +// Entities +#define kesDeathMatchStart "info_player_deathmatch" +#define kesTerroristStart "info_player_deathmatch" +#define kesCounterTerroristStart "info_player_start" + +#define kesFuncDoor "func_door" +#define kesFuncWall "func_wall" +#define kesEnvSprite "env_sprite" +#define kesFuncIllusionary "func_illusionary" + +#define kesReadyRoomStart "info_player_start" +#define keTeamStart info_team_start +#define kesTeamStart "info_team_start" +#define keLeaveGame info_leave_game +#define keJoinTeam info_join_team +#define keSpectate info_spectate +#define keAutoAssign info_join_autoassign +#define keMapInfo info_mapinfo +#define kesMapInfo "info_mapinfo" +#define keGameplay info_gameplay +#define kesGameplay "info_gameplay" + +//#define keGlow glow +#define keGamma env_gamma +#define kesGamma "env_gamma" +#define keParticles env_particles +#define kesParticles "env_particles" +#define keParticlesCustom env_particles_custom +#define kesParticlesCustom "env_particles_custom" + +//#define keResource resource +#define keWeldable func_weldable +#define kesWeldable "func_weldable" + +#define keSeethrough func_seethrough +#define kesSeethrough "func_seethrough" + +#define keSeethroughDoor func_seethroughdoor +#define kesSeethroughDoor "func_seethroughdoor" + +//#define keWaypoint func_waypoint +//#define kesWaypoint "func_waypoint" + +#define keNoBuild func_nobuild +#define kesNoBuild "func_nobuild" + +#define keFuncResource func_resource +#define kesFuncResource "func_resource" + +#define keMP3Audio target_mp3audio +#define kesMP3Audio "target_mp3audio" + +#define keFog env_fog +#define kesFog "env_fog" + +#define keInfoLocation info_location +#define kesInfoLocation "info_location" + +#define keTeamHive team_hive +#define kesTeamHive "team_hive" + +#define keTeamEgg team_egg +#define kesTeamEgg "team_egg" + +#define keTriggerRandom trigger_random +#define kesTriggerRandom "trigger_random" + +#define keTriggerPresence trigger_presence +#define kesTriggerPresence "trigger_presence" + +#define keTriggerScript trigger_script +#define kesTriggerScript "trigger_script" + +#define keTeamWebStrand team_webstrand +#define kesTeamWebStrand "team_webstrand" + +// Targets fired by game +#define ktGameStartedStatus "gamestartedstatus" +#define ktGameReset "gamereset" + +// Weapons/Equipment +#define kwsKnife "weapon_knife" +#define kwKnife weapon_knife +#define kwsGrenade "weapon_grenade" +#define kwGrenade weapon_grenade +#define kwsMachineGun "weapon_machinegun" +#define kwMachineGun weapon_machinegun +#define kwsPistol "weapon_pistol" +#define kwPistol weapon_pistol +#define kwsShotGun "weapon_shotgun" +#define kwShotGun weapon_shotgun +#define kwsHeavyMachineGun "weapon_heavymachinegun" +#define kwHeavyMachineGun weapon_heavymachinegun +#define kwsGrenadeGun "weapon_grenadegun" +#define kwGrenadeGun weapon_grenadegun +#define kwsNukeGun "weapon_nukegun" +#define kwNukeGun weapon_nukegun +#define kwsFlameGun "weapon_flamegun" +#define kwFlameGun weapon_flamegun + +// Weapon that dispenses mines +#define kwsMine "weapon_mine" +#define kwMine weapon_mine + +// Deployed mines +#define kwDeployedMine item_mine +#define kwsDeployedMine "item_mine" + +#define kwsDeployedTurret "turret" +#define kwDeployedTurret turret + +#define kwsHealth "item_health" +#define kwHealth item_health + +#define kwsCatalyst "item_catalyst" +#define kwCatalyst item_catalyst + +#define kwsGenericAmmo "item_genericammo" +#define kwGenericAmmo item_genericammo + +#define kwsHeavyArmor "item_heavyarmor" +#define kwHeavyArmor item_heavyarmor + +#define kwsJetpack "item_jetpack" +#define kwJetpack item_jetpack + +#define kwsAmmoPack "item_ammopack" +#define kwAmmoPack item_ammopack + +// Alien weapons +#define kwsParalysisGun "weapon_paralysis" +#define kwParalysisGun weapon_paralysis + +#define kwsSpitGun "weapon_spit" +#define kwSpitGun weapon_spit + +#define kwClaws weapon_claws +#define kwsClaws "weapon_claws" + +#define kwSwipe weapon_swipe +#define kwsSwipe "weapon_swipe" + +#define kwSporeGun weapon_spore +#define kwsSporeGun "weapon_spore" + +// Alien projectiles +#define kwsSpitProjectile "spitgunprojectile" +#define kwSpitProjectile spitgunprojectile + +#define kwSporeProjectile sporegunprojectile +#define kwsSporeProjectile "sporegunprojectile" + +#define kwSpikeGun weapon_spikegun +#define kwsSpikeGun "weapon_spikegun" + +#define kwBiteGun weapon_bitegun +#define kwsBiteGun "weapon_bitegun" + +#define kwBite2Gun weapon_bite2gun +#define kwsBite2Gun "weapon_bite2gun" + +#define kwResourceTowerGun weapon_resourcetowergun +#define kwsResourceTowerGun "weapon_resourcetowergun" + +#define kwOffenseChamberGun weapon_offensechambergun +#define kwsOffenseChamberGun "weapon_offensechambergun" + +#define kwDefenseChamberGun weapon_defensechambergun +#define kwsDefenseChamberGun "weapon_defensechambergun" + +#define kwSensoryChamberGun weapon_sensorychambergun +#define kwsSensoryChamberGun "weapon_sensorychambergun" + +#define kwMovementChamberGun weapon_movementchambergun +#define kwsMovementChamberGun "weapon_movementchambergun" + +#define kwHiveGun weapon_hivegun +#define kwsHiveGun "weapon_hivegun" + +#define kwHealingSpray weapon_healingspray +#define kwsHealingSpray "weapon_healingspray" + +#define kwMetabolize weapon_metabolize +#define kwsMetabolize "weapon_metabolize" + +#define kwWebSpinner weapon_webspinner +#define kwsWebSpinner "weapon_webspinner" + +#define kwsWebProjectile "webgunprojectile" +#define kwWebProjectile webgunprojectile + +#define kwBabblerGun weapon_babblergun +#define kwsBabblerGun "weapon_babblergun" + +#define kwBabblerProjectile weapon_babblerprojectile +#define kwsBabblerProjectile "weapon_babblerprojectile" + +#define kwPrimalScream weapon_primalscream +#define kwsPrimalScream "weapon_primalscream" + +#define kwLeap weapon_leap +#define kwsLeap "weapon_leap" + +#define kwAmplify weapon_amplify +#define kwsAmplify "weapon_amplify" + +#define kwStomp weapon_stomp +#define kwsStomp "weapon_stomp" + +#define kwStompProjectile stompprojectile +#define kwsStompProjectile "stompprojectile" + +#define kwDevour weapon_devour +#define kwsDevour "weapon_devour" + +#define kwCharge weapon_charge +#define kwsCharge "weapon_charge" + +#define kwsParasiteGun "weapon_parasite" +#define kwParasiteGun weapon_parasite + +#define kwsUmbraCloud "umbracloud" +#define kwUmbraCloud umbracloud +#define kwsUmbraProjectile "umbraprojectile" +#define kwUmbraProjectile umbraprojectile + +#define kwsUmbraGun "weapon_umbra" +#define kwUmbraGun weapon_umbra + +#define kwsBlinkGun "weapon_blink" +#define kwBlinkGun weapon_blink + +#define kwsDivineWind "weapon_divinewind" +#define kwDivineWind weapon_divinewind + +#define kwBileBomb weapon_bilebomb +#define kwsBileBomb "weapon_bilebomb" + +#define kwBileBombGun weapon_bilebombgun +#define kwsBileBombGun "weapon_bilebombgun" + +#define kwAcidRocket weapon_acidrocket +#define kwsAcidRocket "weapon_acidrocket" + +#define kwAcidRocketGun weapon_acidrocketgun +#define kwsAcidRocketGun "weapon_acidrocketgun" + +// Debug item +#define kwsDebugEntity "item_genericammo" + +// Filenames +#define kMOTDName "motd.txt" + +// Tech node prefix (skill.cfg) +#define kTechCostPrefix "avh_techcost_" +#define kTechHealthPrefix "avh_techhealth_" +#define kTechTimePrefix "avh_techtime_" + +// Player models +#define kNullModel "models/null.mdl" +#define kReadyRoomModel "models/player.mdl" +#define kMarineSoldierModel "models/player/soldier/soldier.mdl" +#define kHeavySoldierModel "models/player/heavy/heavy.mdl" +#define kMarineCommanderModel "models/player/commander/commander.mdl" +#define kAlienLevelOneModel "models/player/alien1/alien1.mdl" +#define kAlienLevelTwoModel "models/player/alien2/alien2.mdl" +#define kAlienLevelThreeModel "models/player/alien3/alien3.mdl" +#define kAlienLevelFourModel "models/player/alien4/alien4.mdl" +#define kAlienLevelFiveModel "models/player/alien5/alien5.mdl" +#define kAlienGestateModel "models/player/gestate/gestate.mdl" + +//#define kAlienAbilitiesGrantedSound "misc/a-abilities_granted.wav" +//#define kAlienAbilitiesLostSound "misc/a-abilities_lost.wav" +#define kAlienBuildingSound1 "misc/a-build1.wav" +#define kAlienBuildingSound2 "misc/a-build2.wav" +#define kDistressBeaconSound "misc/distressbeacon.wav" + +#define kBuildableRecycleSound "misc/b_recycle.wav" +#define kBuildableHurt1Sound "misc/b_hurt1.wav" +#define kBuildableHurt2Sound "misc/b_hurt2.wav" +#define kElectricalSprite "sprites/lgtning.spr" + +// Model names for key +#define kSoldierName "soldier" +#define kHeavyName "heavy" +#define kCommanderName "commander" +#define kAlien1Name "alien1" +#define kAlien2Name "alien2" +#define kAlien3Name "alien3" +#define kAlien4Name "alien4" +#define kAlien5Name "alien5" +#define kAlienGestationName "gestate" + +// Sound lists +#define kPlayerLevelAttackSoundList "player/role%d_attack" +#define kPlayerLevelDieSoundList "player/role%d_die" +#define kPlayerLevelIdleSoundList "player/role%d_idle" +#define kPlayerLevelMoveSoundList "player/role%d_move" +#define kPlayerLevelPainSoundList "player/role%d_pain" +#define kPlayerLevelSpawnSoundList "player/role%d_spawn" +#define kPlayerLevelWoundSoundList "player/role%d_wound" +#define kHiveWoundSoundList "misc/hive_wound" + +#define kFallPainSoundFormat "player/pl_fallpain3-%d.wav" + + +#define kDigestingSound "player/digesting.wav" + +// Not quite a sound list +//#define kHiveWoundSoundPrefix "misc/hive_wound" + +#define kPieSelectForwardSound "hud/select_node_forward.wav" +#define kPieSelectBackwardSound "hud/select_node_backward.wav" +#define kPieSelectForwardAlienSound "hud/select_node_forward-a.wav" +#define kPieSelectBackwardAlienSound "hud/select_node_backward-a.wav" +#define kSelectSound "hud/select.wav" +#define kSelectAlienSound "hud/select-a.wav" +#define kSquad1Sound "hud/squad1.wav" +#define kSquad2Sound "hud/squad2.wav" +#define kSquad3Sound "hud/squad3.wav" +#define kSquad4Sound "hud/squad4.wav" +#define kSquad5Sound "hud/squad5.wav" + +#define kMarineSquad1Sound "hud/m-squad1.wav" +#define kMarineSquad2Sound "hud/m-squad2.wav" +#define kMarineSquad3Sound "hud/m-squad3.wav" +#define kMarineSquad4Sound "hud/m-squad4.wav" +#define kMarineSquad5Sound "hud/m-squad5.wav" + +#define kPlaceBuildingSound "hud/place_building.wav" +#define kCountdownSound "hud/countdown.wav" +#define kPointsSpentSound "hud/points_spent.wav" +#define kAlienPointsReceivedSound "hud/alien_points_received.wav" +#define kMarinePointsReceivedSound "hud/marine_points_received.wav" + +#define kMarineResearchComplete "hud/marine_research_complete.wav" +#define kMarineSoldierUnderAttack "hud/marine_soldierunderattack.wav" +#define kMarineCCOnline1 "hud/marine_cconline1.wav" +#define kMarineCCOnline2 "hud/marine_cconline2.wav" +#define kMarineCCUnderAttack1 "hud/marine_ccunderattack1.wav" +#define kMarineCCUnderAttack2 "hud/marine_ccunderattack2.wav" +#define kMarineCommanderEjected "hud/marine_commander_ejected.wav" +#define kMarineBaseUnderAttack1 "hud/marine_baseattack1.wav" +#define kMarineBaseUnderAttack2 "hud/marine_baseattack2.wav" +#define kMarineMoreResources "hud/marine_more.wav" +#define kMarineLowResources "hud/marine_lowresources.wav" +#define kMarineNeedsAmmo1 "hud/marine_needsammo1.wav" +#define kMarineNeedsAmmo2 "hud/marine_needsammo2.wav" +#define kMarineNeedsHealth1 "hud/marine_needshealth1.wav" +#define kMarineNeedsHealth2 "hud/marine_needshealth2.wav" +#define kMarineNeedsOrder1 "hud/marine_needsorder1.wav" +#define kMarineNeedsOrder2 "hud/marine_needsorder2.wav" +#define kMarineSoldierLost1 "hud/marine_soldierlost1.wav" +#define kMarineSoldierLost2 "hud/marine_soldierlost2.wav" +#define kMarineSentryFiring1 "hud/marine_sentryfiring1.wav" +#define kMarineSentryFiring2 "hud/marine_sentryfiring2.wav" +#define kMarineSentryTakingDamage1 "hud/marine_sentrytakingdamage1.wav" +#define kMarineSentryTakingDamage2 "hud/marine_sentrytakingdamage2.wav" +#define kMarineUpgradeComplete "hud/marine_upgradecomplete.wav" +#define kMarineGiveOrders "hud/marine_giveorders.wav" +#define kMarineNeedPortal1 "hud/marine_needportal1.wav" +#define kMarineNeedPortal2 "hud/marine_needportal2.wav" +#define kMarineGotoAlert "hud/marine_gotoalert.wav" +#define kMarineCommanderIdle1 "hud/marine_commanderidle1.wav" +#define kMarineCommanderIdle2 "hud/marine_commanderidle2.wav" +#define kMarineArmoryUpgrading "hud/marine_armoryupgrading.wav" + +#define kAlienEnemyApproaches1 "hud/alien_enemyapproaches1.wav" +#define kAlienEnemyApproaches2 "hud/alien_enemyapproaches2.wav" +#define kAlienGameOverMan "hud/alien_gameoverman.wav" +#define kAlienHiveAttack "hud/alien_hiveattack.wav" +#define kAlienHiveComplete1 "hud/alien_hivecomplete1.wav" +#define kAlienHiveComplete2 "hud/alien_hivecomplete2.wav" +#define kAlienHiveDying1 "hud/alien_hivedying1.wav" +#define kAlienHiveDying2 "hud/alien_hivedying2.wav" +#define kAlienLifeformAttack1 "hud/alien_lifeformattack1.wav" +#define kAlienLifeformAttack2 "hud/alien_lifeformattack2.wav" +#define kAlienLowResources "hud/alien_lowresources.wav" +#define kAlienMess "hud/alien_mess.wav" +#define kAlienMoreResources1 "hud/alien_more1.wav" +#define kAlienMoreResources2 "hud/alien_more2.wav" +#define kAlienNeedBetter "hud/alien_needbetter.wav" +#define kAlienNeedBuilders1 "hud/alien_needbuilders1.wav" +#define kAlienNeedBuilders2 "hud/alien_needbuilders2.wav" +#define kAlienNewTrait1 "hud/alien_newtrait1.wav" +#define kAlienNewTrait2 "hud/alien_newtrait2.wav" +#define kAlienNowDonce "hud/alien_now.wav" +#define kAlienResourceAttack1 "hud/alien_resourceattack1.wav" +#define kAlienResourceAttack2 "hud/alien_resourceattack2.wav" +#define kAlienSeeDead "hud/alien_seedead.wav" +#define kAlienStructureAttack1 "hud/alien_structureattack1.wav" +#define kAlienStructureAttack2 "hud/alien_structureattack2.wav" +#define kAlienUpgradeLost "hud/alien_upgrade_lost.wav" + +#define kSoundOrderMove1 "hud/marine_order_move1.wav" +#define kSoundOrderMove2 "hud/marine_order_move2.wav" +#define kSoundOrderMove3 "hud/marine_order_move3.wav" +#define kSoundOrderMove4 "hud/marine_order_move4.wav" +#define kSoundOrderAttack "hud/marine_order_attack.wav" +#define kSoundOrderBuild "hud/marine_order_build.wav" +#define kSoundOrderWeld "hud/marine_order_weld.wav" +#define kSoundOrderGuard "hud/marine_order_guard.wav" +#define kSoundOrderGet "hud/marine_order_get.wav" +#define kSoundOrderComplete1 "hud/marine_order_complete1.wav" +#define kSoundOrderComplete2 "hud/marine_order_complete2.wav" +#define kSoundOrderComplete3 "hud/marine_order_complete3.wav" +#define kSoundOrderComplete4 "hud/marine_order_complete4.wav" +#define kSoundOrderComplete5 "hud/marine_order_complete5.wav" +#define kSoundOrderComplete6 "hud/marine_order_complete6.wav" + +#define kAlienGameStart1 "hud/alien_gamestart1.wav" +#define kAlienGameStart2 "hud/alien_gamestart2.wav" +#define kMarineGameStart1 "hud/marine_gamestart1.wav" +#define kMarineGameStart2 "hud/marine_gamestart2.wav" +#define kYouWinSound "hud/you_win.wav" +#define kYouLoseSound "hud/you_lose.wav" +#define kTooltipSound "hud/tooltip.wav" +#define kMyHiveEasterEgg "hud/alien_myhive.wav" + +// Tech category names +#define kWeaponTechCategory "WeaponCategory" +#define kArmorTechCategory "ArmorCategory" +#define kBuildTechCategory "BuildCategory" +#define kRadioTechCategory "RadioCategory" + +// Tech button components are the category plus this suffix +//#define kMessageButtonsSuffix "Buttons" +#define kTechNodeLabelPrefix "TechNodeLabel_" +#define kTechNodeHelpPrefix "TechNodeHelp_" +#define kPrerequisitePrefix "Prerequisite" +#define kUser3Name "User3Name_" +#define kUser3Description "User3Desc_" +#define kUser3FriendlyDescription "User3FriendlyDesc_" +#define kUser3CommanderDescription "User3CommanderDesc_" +#define kBlipStatusPrefix "BlipStatus_" +#define kMessageButtonCost "Cost" +#define kNotFullyBuilt "NotFullyBuilt" +#define kPointsSuffix "Points" + +const float kSquadHierarchyScaleFactor = .0001f; +const float kCommanderHierarchyScaleFactor = .0002f; + +// Sayings +#define kSoldierSayingList "vox/ssay%d" +#define kCommanderSayingList "vox/csay%d" +#define kAlienSayingList "vox/asay%d" + +#define kSoldierOrderRequestList "vox/sreq" +#define kSoldierOrderAckList "vox/sack" + +// Other sounds +#define kJetpackSound "misc/jetpack.wav" +//#define kAdrenalineSound "misc/adren.wav" +#define kGestationSound "misc/gestate.wav" +#define kConnectSound "misc/connect.wav" +//#define kDisconnectSound "misc/disconnect.wav" +#define kEmptySound "weapons/357_cock1.wav" +#define kInvalidSound "misc/invalid.wav" +#define kLevelUpMarineSound "misc/levelup.wav" +#define kLevelUpAlienSound "misc/a-levelup.wav" +// : bug 0000767 +#define kPlayerJoinedSound "player/jointeam.wav" +// : + +// Events +#define kJetpackEvent "events/Jetpack.sc" +#define kStartOverwatchEvent "events/StartOverwatch.sc" +#define kEndOverwatchEvent "events/EndOverwatch.sc" + +#define kStopVoiceSoundEvent "events/StopVoice.sc" + +#define kRegenerationEvent "events/Regeneration.sc" +#define kStartCloakEvent "events/StartCloak.sc" +#define kEndCloakEvent "events/EndCloak.sc" + +#define kWallJumpEvent "events/WallJump.sc" +#define kFlightEvent "events/Flight.sc" +#define kTeleportEvent "events/Teleport.sc" +#define kSiegeHitEvent "events/SiegeHit.sc" +#define kSiegeViewHitEvent "events/SiegeViewHit.sc" +#define kPhaseInEvent "events/PhaseIn.sc" +#define kCommanderPointsAwardedEvent "events/CommandPoints.sc" +#define kEmptySoundEvent "events/Empty.sc" +#define kNumericalInfoEvent "events/NumericalInfo.sc" +#define kAlienSightOnEvent "events/AlienSightOn.sc" +#define kAlienSightOffEvent "events/AlienSightOff.sc" +#define kInvalidActionEvent "events/InvalidAction.sc" +#define kParticleEvent "events/Particle.sc" +#define kDistressBeaconEvent "events/DistressBeacon.sc" +#define kWeaponAnimationEvent "events/WeaponAnimation.sc" +#define kLevelUpEvent "events/LevelUp.sc" +#define kAbilityEventName "events/Ability.sc" + +// Targets +#define kTargetCommandStationUseTeamOne "commandstationuse1" +#define kTargetCommandStationUseTeamTwo "commandstationuse2" + +#define kTargetCommandStationLogoutTeamOne "commandstationlogout1" +#define kTargetCommandStationLogoutTeamTwo "commandstationlogout2" + +#define kTargetCommandStationDestroyedTeamOne "commandstationdestroyed1" +#define kTargetCommandStationDestroyedTeamTwo "commandstationdestroyed2" + +#define kReadyNotification "ready" +#define kNotReadyNotification "notready" + +const int kOverwatchBreakingVelocity = 5; +const float kOverwatchAcquireTime = 6.0f; +const float kOverwatchLostTargetTime = 4.0f; +const float kOverwatchKeepFiringAfterMissingTargetTime = 1.0f; +const float kSpeakingTime = 4.0f; +const float kEnemySightedTime = 2.0f; + +const int kDefaultViewHeight = 0; +const float kDefaultMinMapExtent = -4000; +const float kDefaultMaxMapExtent = 4000; + +// Energy constants for marine structures +const float kMarineStructureEnergyRate = .33f; +const float kMarineStructureMaxEnergy = 100; + +const int kShellRenderAmount = 50; +const int kInvulShellRenderAmount = 15; + +// This is one less then 4096 because we need top bit for sign. +// Only allow map dimensions of this size or lower +const float kMaxMapDimension = 4095; + +const float kDefaultMapGamma = 1.0f; + +const float kNormalizationNetworkFactor = 1000.0f; +const float kHotKeyNetworkFactor = 100.0f; + +const int kDetectionDistance = 500; +const float kMaxRelevantCullDistance = 1024; + +const int kHiveXYDistanceTolerance = 400; +const float kBaseHealthPercentage = .5f; + +const float kHUDSoundVolume = .3f; + +#define kHotKeyPrefix "hotkey" + +// How many directions can bullets travel in within spread vector? +//const int kBulletSpreadGranularity = 15; + +const int iNumberOfTeamColors = 6; +extern int kTeamColors[iNumberOfTeamColors][3]; +extern float kFTeamColors[iNumberOfTeamColors][3]; + +// Random resource constants +const int kNumSharesPerPlayer = 3; +const int kTotalShares = MAX_PLAYERS_PER_TEAM*kNumSharesPerPlayer; + +const int kNumericalInfoResourcesEvent = 0; +const int kNumericalInfoHealthEvent = 1; +const int kNumericalInfoResourcesDonatedEvent = 2; +const int kNumericalInfoAmmoEvent = 3; + +const int kGameStatusReset = 0; +const int kGameStatusResetNewMap = 1; +const int kGameStatusEnded = 2; +const int kGameStatusGameTime = 3; +const int kGameStatusUnspentLevels = 4; + +// Max message length is 192, take into account rest of message +const int kMaxPlayerSendMessageLength = 160; + +const int kTopDownYaw = 90; +const int kTopDownPitch = 0; +const int kTopDownRoll = -90; + +// Extra fudge factor to make sure players don't get stuck when respawning +const int kRespawnFudgeFactorHeight = 1; + +#endif diff --git a/main/source/mod/AvHEntities.cpp b/main/source/mod/AvHEntities.cpp index c9215ca..9845fe3 100644 --- a/main/source/mod/AvHEntities.cpp +++ b/main/source/mod/AvHEntities.cpp @@ -1658,7 +1658,7 @@ void AvHTriggerScript::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TY } -AvHWebStrand::AvHWebStrand() +AvHWebStrand::AvHWebStrand() : mSolid(false) { } @@ -1666,6 +1666,7 @@ void AvHWebStrand::Break() { EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kWebStrandBreakSound, 1.0, ATTN_IDLE); UTIL_Remove(this); + SetThink(NULL); // Decrement number of strands AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)this->pev->team); @@ -1685,20 +1686,24 @@ void AvHWebStrand::Precache(void) // Precache web strand sprite PRECACHE_UNMODIFIED_MODEL(kWebStrandSprite); PRECACHE_UNMODIFIED_SOUND(kWebStrandBreakSound); + PRECACHE_UNMODIFIED_SOUND(kWebStrandHardenSound); + PRECACHE_UNMODIFIED_SOUND(kWebStrandFormSound); } void AvHWebStrand::Setup(const Vector& inPointOne, const Vector& inPointTwo) { // Create a new entity with CBeam private data - this->BeamInit(kWebStrandSprite, kWebStrandWidth); + this->BeamInit(kWebStrandSprite, 40); //kWebStrandWidth); this->PointsInit(inPointOne, inPointTwo); this->SetColor( 255, 255, 255 ); this->SetScrollRate( 0 ); + this->SetFrame(0); //this->SetBrightness( 64 ); - this->SetBrightness( 8 ); + this->SetBrightness( 16 ); this->pev->classname = MAKE_STRING(kesTeamWebStrand); + this->pev->rendermode = kRenderNormal; } void AvHWebStrand::Spawn(void) @@ -1713,15 +1718,28 @@ void AvHWebStrand::Spawn(void) //this->pev->solid = SOLID_BBOX; this->pev->health = kWebHitPoints; this->pev->takedamage = DAMAGE_YES; - + this->mSolid=false; + this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kWebWarmupTime); + SetThink(&AvHWebStrand::StrandThink); + //SetBits(this->pev->flags, FL_MONSTER); this->RelinkBeam(); + EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kWebStrandFormSound, 1.0, ATTN_IDLE); //SetThink(StrandExpire); //this->pev->nextthink = gpGlobals->time + kWebStrandLifetime; } +void AvHWebStrand::StrandThink() +{ + EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kWebStrandHardenSound, 1.0, ATTN_IDLE); + this->SetBrightness( 32 ); + this->SetColor( 255, 255, 255 ); + this->SetFrame(1); + this->mSolid=true; + SetThink(NULL); +} void AvHWebStrand::StrandExpire() { this->Break(); @@ -1734,15 +1752,20 @@ void AvHWebStrand::StrandTouch( CBaseEntity *pOther ) //if(GetGameRules()->CanEntityDoDamageTo(this, pOther)) if(pOther->pev->team != this->pev->team) { - AvHPlayer* thePlayer = dynamic_cast(pOther); - if(thePlayer && thePlayer->GetCanBeAffectedByEnemies()) - { - // Break web and ensnare player if possible (players can't be too webbed) - if(thePlayer->SetEnsnareState(true)) + if ( this->mSolid ) { + AvHPlayer* thePlayer = dynamic_cast(pOther); + if(thePlayer && thePlayer->GetCanBeAffectedByEnemies()) { - this->Break(); + // Break web and ensnare player if possible (players can't be too webbed) + if(thePlayer->SetEnsnareState(true)) + { + this->Break(); + } } } + else { + this->Break(); + } } } diff --git a/main/source/mod/AvHEntities.h b/main/source/mod/AvHEntities.h index 701b227..abe2168 100644 --- a/main/source/mod/AvHEntities.h +++ b/main/source/mod/AvHEntities.h @@ -522,8 +522,10 @@ public: void EXPORT StrandExpire(); virtual int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + void EXPORT StrandThink(); private: + bool mSolid; }; class AvHFuncResource : public CBaseAnimating diff --git a/main/source/mod/AvHEntityHierarchy.cpp b/main/source/mod/AvHEntityHierarchy.cpp index b3978f3..ed1a197 100644 --- a/main/source/mod/AvHEntityHierarchy.cpp +++ b/main/source/mod/AvHEntityHierarchy.cpp @@ -68,6 +68,9 @@ AvHEntityHierarchy::AvHEntityHierarchy() void AvHEntityHierarchy::Clear() { + mNumMovement=0; + mNumSensory=0; + mNumDefence=0; this->mEntityList.clear(); } @@ -95,6 +98,18 @@ void AvHEntityHierarchy::GetEntityInfoList(MapEntityMap& outList) const outList = mEntityList; } +int AvHEntityHierarchy::GetNumSensory() const { + return this->mNumSensory; +} + +int AvHEntityHierarchy::GetNumMovement() const { + return this->mNumMovement; +} + +int AvHEntityHierarchy::GetNumDefense() const { + return this->mNumDefence; +} + //////////////// // end shared // //////////////// @@ -130,31 +145,51 @@ int GetHotkeyGroupContainingPlayer(AvHPlayer* player) } - void AvHEntityHierarchy::BuildFromTeam(const AvHTeam* inTeam, BaseEntityListType& inBaseEntityList) { this->Clear(); + int mc=0, sc=0, dc=0; if (inTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE || inTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) { - + // Loop through all entities in the world for(BaseEntityListType::iterator theIter = inBaseEntityList.begin(); theIter != inBaseEntityList.end(); theIter++) - { - + { CBaseEntity* theBaseEntity = *theIter; int theEntityIndex = theBaseEntity->entindex(); bool theEntityIsVisible = (theBaseEntity->pev->team == (int)(inTeam->GetTeamNumber())) || GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_VIS_SIGHTED); bool theEntityIsDetected = GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_VIS_DETECTED); - + bool theEntityIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntityIndex); + if ( theEntityIsUnderAttack ) + { + int a=0; + } // Don't send ammo, health, weapons, or scans bool theIsTransient = ((AvHUser3)(theBaseEntity->pev->iuser3) == AVH_USER3_MARINEITEM) || (theBaseEntity->pev->classname == MAKE_STRING(kwsScan)); - MapEntity mapEntity; + + + if ( inTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN && theBaseEntity->pev->team == (int)(inTeam->GetTeamNumber()) ) { + AvHBuildable *theBuildable=dynamic_cast(theBaseEntity); + if ( theBuildable && theBuildable->GetHasBeenBuilt() ) { + if ( theBaseEntity->pev->iuser3 == AVH_USER3_MOVEMENT_CHAMBER ) { + mc++; + } + if ( theBaseEntity->pev->iuser3 == AVH_USER3_SENSORY_CHAMBER ) { + sc++; + } + if ( theBaseEntity->pev->iuser3 == AVH_USER3_DEFENSE_CHAMBER ) { + dc++; + } + } + } + + MapEntity mapEntity; mapEntity.mX = theBaseEntity->pev->origin.x; mapEntity.mY = theBaseEntity->pev->origin.y; @@ -167,6 +202,7 @@ void AvHEntityHierarchy::BuildFromTeam(const AvHTeam* inTeam, BaseEntityListType mapEntity.mAngle = theBaseEntity->pev->angles[1]; mapEntity.mTeam = (AvHTeamNumber)(theBaseEntity->pev->team); mapEntity.mSquadNumber = 0; + mapEntity.mUnderAttack = theEntityIsUnderAttack ? 1 : 0; bool sendEntity = false; @@ -191,7 +227,6 @@ void AvHEntityHierarchy::BuildFromTeam(const AvHTeam* inTeam, BaseEntityListType } else if ((theEntityIsVisible || theEntityIsDetected) && !(theBaseEntity->pev->effects & EF_NODRAW) && !theIsTransient) { - AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); if (thePlayer) @@ -241,20 +276,136 @@ void AvHEntityHierarchy::BuildFromTeam(const AvHTeam* inTeam, BaseEntityListType mEntityList[theEntityIndex] = mapEntity; } - + } + if ( inTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN ) { + sc=min(3, sc); + dc=min(3,dc); + mc=min(3,mc); + if ( this->mNumSensory != sc || this->mNumDefence != dc || this->mNumMovement != mc ) { + this->mNumSensory=sc; + this->mNumDefence=dc; + this->mNumMovement=mc; + } + } + } +} + +void AvHEntityHierarchy::BuildForSpec(BaseEntityListType& inBaseEntityList) +{ + + this->Clear(); + + // Loop through all entities in the world + for(BaseEntityListType::iterator theIter = inBaseEntityList.begin(); theIter != inBaseEntityList.end(); theIter++) + { + CBaseEntity* theBaseEntity = *theIter; + + int theEntityIndex = theBaseEntity->entindex(); + bool theEntityIsVisible = false; + if ( theBaseEntity->pev->team == TEAM_ONE || theBaseEntity->pev->team == TEAM_TWO || theBaseEntity->pev->team == TEAM_THREE || theBaseEntity->pev->team == TEAM_FOUR ) + theEntityIsVisible=true; + bool theEntityIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntityIndex); + // Don't send ammo, health, weapons, or scans + bool theIsTransient = ((AvHUser3)(theBaseEntity->pev->iuser3) == AVH_USER3_MARINEITEM) || (theBaseEntity->pev->classname == MAKE_STRING(kwsScan)); + + MapEntity mapEntity; + + mapEntity.mX = theBaseEntity->pev->origin.x; + mapEntity.mY = theBaseEntity->pev->origin.y; + mapEntity.mUser3 = (AvHUser3)(theBaseEntity->pev->iuser3); + + mapEntity.mAngle = theBaseEntity->pev->angles[1]; + mapEntity.mTeam = (AvHTeamNumber)(theBaseEntity->pev->team); + mapEntity.mSquadNumber = 0; + mapEntity.mUnderAttack = theEntityIsUnderAttack ? 1 : 0; + + bool sendEntity = false; + + if (mapEntity.mUser3 == AVH_USER3_HIVE) + { + if (!theEntityIsVisible) + { + mapEntity.mTeam = TEAM_IND; + } + sendEntity = true; + } + else if (mapEntity.mUser3 == AVH_USER3_WELD) + { + vec3_t theEntityOrigin = AvHSHUGetRealLocation(theBaseEntity->pev->origin, theBaseEntity->pev->mins, theBaseEntity->pev->maxs); + mapEntity.mX = theEntityOrigin.x; + mapEntity.mY = theEntityOrigin.y; + sendEntity = true; + } + else if (mapEntity.mUser3 == AVH_USER3_FUNC_RESOURCE) + { + sendEntity = true; + } + else if (theEntityIsVisible && !(theBaseEntity->pev->effects & EF_NODRAW) && !theIsTransient) + { + AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); + + if (thePlayer) + { + ASSERT(theEntityIndex > 0); + ASSERT(theEntityIndex <= 32); + mapEntity.mSquadNumber = GetHotkeyGroupContainingPlayer(thePlayer) + 1; + + if ((thePlayer->GetPlayMode() == PLAYMODE_PLAYING) && !thePlayer->GetIsSpectator()) + { + + sendEntity = true; + + // If the player has the heavy armor upgrade switch the + // user3 to something that will let us reconstruct that later. + + if (thePlayer->pev->iuser3 == AVH_USER3_MARINE_PLAYER && + GetHasUpgrade(thePlayer->pev->iuser4, MASK_UPGRADE_13)) + { + mapEntity.mUser3 = AVH_USER3_HEAVY; + } + + } + + } + else + { + if (mapEntity.mUser3 != AVH_USER3_HEAVY) + { + sendEntity = true; + } + } + + } + + if (sendEntity) + { + //const AvHMapExtents& theMapExtents = GetGameRules()->GetMapExtents(); +// commented this out here, commented out corresponding shift in AvHOverviewMap::Draw at line 771 +// float theMinMapX = theMapExtents.GetMinMapX(); +// float theMinMapY = theMapExtents.GetMinMapY(); + +// mapEntity.mX -= theMinMapX; +// mapEntity.mY -= theMinMapY; + + mEntityList[theEntityIndex] = mapEntity; + // debug the entity hierarchy message size +// if ( mapEntity.mUser3 == AVH_USER3_COMMANDER_STATION ) { +// for (int i=0; i<200; i++ ) { +// mEntityList[100+i] = mapEntity; +// } +// } } } - } // Returns true when something was sent -bool AvHEntityHierarchy::SendToNetworkStream(AvHEntityHierarchy& inClientHierarchy, entvars_t* inPlayer) +bool AvHEntityHierarchy::SendToNetworkStream(AvHEntityHierarchy& inClientHierarchy, entvars_t* inPlayer, bool spectating) { // Get iterators for both hierarchies MapEntityMap::const_iterator clientIter = inClientHierarchy.mEntityList.begin(); MapEntityMap::const_iterator clientEnd = inClientHierarchy.mEntityList.end(); - MapEntityMap::const_iterator serverIter = mEntityList.begin(); + MapEntityMap::iterator serverIter = mEntityList.begin(); MapEntityMap::const_iterator serverEnd = mEntityList.end(); // Create maps for changes @@ -262,42 +413,63 @@ bool AvHEntityHierarchy::SendToNetworkStream(AvHEntityHierarchy& inClientHierarc EntityListType OldItems; //to be deleted //TODO : replace OldItems with vector + int entityCount=0; + // HACK + // If we update more than kMaxSpecEntity then we need to clean out the remaining ones so they get sent next time. + const int kMaxSpecEntity=100; while (clientIter != clientEnd && serverIter != serverEnd) { - if (serverIter->first < clientIter->first) - { - NewItems.insert( *serverIter ); + if ( entityCount < kMaxSpecEntity ) { + if (serverIter->first < clientIter->first) + { + NewItems.insert( *serverIter ); + entityCount++; + ++serverIter; + continue; + } + if (serverIter->first > clientIter->first) + { + OldItems.push_back( clientIter->first ); + entityCount++; + ++clientIter; + continue; + } + if (clientIter->second != serverIter->second ) + { + entityCount++; + NewItems.insert( *serverIter ); + } ++serverIter; - continue; - } - if (serverIter->first > clientIter->first) - { - OldItems.push_back( clientIter->first ); ++clientIter; - continue; } - if (clientIter->second != serverIter->second) - { - NewItems.insert( *serverIter ); + else { + mEntityList.erase(serverIter++); } - - ++serverIter; - ++clientIter; } while(serverIter != serverEnd) { - NewItems.insert( *serverIter ); - ++serverIter; + if ( entityCount < kMaxSpecEntity ) { + entityCount++; + NewItems.insert( *serverIter ); + ++serverIter; + } + else { + mEntityList.erase(serverIter++); + } } while(clientIter != clientEnd) { - OldItems.push_back( clientIter->first ); + if ( entityCount < kMaxSpecEntity ) { + entityCount++; + OldItems.push_back( clientIter->first ); + } ++clientIter; } - NetMsg_UpdateEntityHierarchy( inPlayer, NewItems, OldItems ); + ASSERT(entityCount < kMaxSpecEntity +1); + NetMsg_UpdateEntityHierarchy( inPlayer, NewItems, OldItems, spectating ); return (!NewItems.empty() || !OldItems.empty()); } @@ -319,4 +491,4 @@ bool AvHEntityHierarchy::DeleteEntity( const int inIndex ) { return false; } this->mEntityList.erase(loc); return true; -} \ No newline at end of file +} diff --git a/main/source/mod/AvHEntityHierarchy.h b/main/source/mod/AvHEntityHierarchy.h index dab2ac9..b38b056 100644 --- a/main/source/mod/AvHEntityHierarchy.h +++ b/main/source/mod/AvHEntityHierarchy.h @@ -54,7 +54,7 @@ const int kNumStatusTypes = 15; class MapEntity { public: - MapEntity(void) : mUser3(AVH_USER3_NONE), mTeam(TEAM_IND), mX(0.0f), mY(0.0f), mAngle(0.0f), mSquadNumber(0) {} + MapEntity(void) : mUser3(AVH_USER3_NONE), mTeam(TEAM_IND), mX(0.0f), mY(0.0f), mAngle(0.0f), mSquadNumber(0), mUnderAttack(0) {} AvHUser3 mUser3; AvHTeamNumber mTeam; @@ -62,6 +62,7 @@ public: float mY; float mAngle; int mSquadNumber; + int mUnderAttack; bool operator==(const MapEntity& e) const { @@ -70,6 +71,7 @@ public: mX == e.mX && mY == e.mY && mAngle == e.mAngle && + mUnderAttack == e.mUnderAttack && mSquadNumber == e.mSquadNumber; } @@ -93,8 +95,9 @@ public: void Clear(); #ifdef AVH_SERVER - bool SendToNetworkStream(AvHEntityHierarchy& inClientHierarchy, entvars_t* inPlayer); + bool SendToNetworkStream(AvHEntityHierarchy& inClientHierarchy, entvars_t* inPlayer, bool spectating); void BuildFromTeam(const AvHTeam* inTeam, BaseEntityListType& inBaseEntityList); + void BuildForSpec(BaseEntityListType& inBaseEntityList); #endif bool GetHasBaseLineBeenSent() const; @@ -108,13 +111,18 @@ public: bool operator!=(const AvHEntityHierarchy& inHierarchy) const; bool operator==(const AvHEntityHierarchy& inHierarchy) const; + int GetNumSensory() const; + int GetNumDefense() const; + int GetNumMovement() const; private: // The encoded entity info that has been sent to clients MapEntityMap mEntityList; - + int mNumMovement; + int mNumSensory; + int mNumDefence; }; #endif \ No newline at end of file diff --git a/main/source/mod/AvHEvents.cpp b/main/source/mod/AvHEvents.cpp index ca2d403..2d409fe 100644 --- a/main/source/mod/AvHEvents.cpp +++ b/main/source/mod/AvHEvents.cpp @@ -757,8 +757,13 @@ void EV_MachineGun(struct event_args_s* args) // General x-punch axis if ( EV_IsLocal( idx ) ) { + // Multiply punch by upgrade level - float theHalfSpread = (kMGXPunch/4.0f)*(theUpgradeLevel+1); + //float theHalfSpread = (kMGXPunch/4.0f)*(theUpgradeLevel+1); + + // Changed to ignore upgrade level + float theHalfSpread = (kMGXPunch/4.0f); + if(theHalfSpread > 0.0f) { V_PunchAxis( 0, gEngfuncs.pfnRandomFloat( -theHalfSpread, theHalfSpread ) ); @@ -843,7 +848,11 @@ void EV_Pistol(struct event_args_s* args) if ( EV_IsLocal( idx ) ) { // Multiply punch by upgrade level - float theHalfSpread = (kHGXPunch/3.0f)*(theUpgradeLevel+1); + //float theHalfSpread = (kHGXPunch/3.0f)*(theUpgradeLevel+1); + + // Changed to ignore upgrade level + float theHalfSpread = (kHGXPunch/3.0f); + if(theHalfSpread > 0.0f) { V_PunchAxis( 0, gEngfuncs.pfnRandomFloat( -theHalfSpread, theHalfSpread ) ); @@ -934,7 +943,12 @@ void EV_SonicGun(struct event_args_s* args) // General x-punch axis if ( EV_IsLocal( idx ) ) { - float theHalfSpread = (kSGXPunch/3.0f)*(theUpgradeLevel+1); + //float theHalfSpread = (kSGXPunch/3.0f)*(theUpgradeLevel+1); + + // Changed to ignore upgrade level + float theHalfSpread = (kSGXPunch/3.0f); + + if(theHalfSpread > 0.0f) { V_PunchAxis( 0, gEngfuncs.pfnRandomFloat( -theHalfSpread, theHalfSpread ) ); @@ -1033,7 +1047,11 @@ void EV_HeavyMachineGun(struct event_args_s* args) // General x-punch axis if ( EV_IsLocal( idx ) ) { - float theHalfSpread = (kHMGXPunch/4.0f)*(theUpgradeLevel+1); + //float theHalfSpread = (kHMGXPunch/4.0f)*(theUpgradeLevel+1); + + // Changed to ignore upgrade level + float theHalfSpread = (kHMGXPunch/4.0f); + if(theHalfSpread > 0.0f) { V_PunchAxis( 0, gEngfuncs.pfnRandomFloat( -theHalfSpread, theHalfSpread ) ); @@ -1112,7 +1130,11 @@ void EV_GrenadeGun(struct event_args_s* inArgs) // General x-punch axis if ( EV_IsLocal( idx ) ) { - float theHalfSpread = (kGGXPunch/2.0f)*(theUpgradeLevel+1); + //float theHalfSpread = (kGGXPunch/2.0f)*(theUpgradeLevel+1); + + // Changed to ignore upgrade level + float theHalfSpread = (kGGXPunch/2.0f); + if(theHalfSpread > 0.0f) { V_PunchAxis( 0, gEngfuncs.pfnRandomFloat( -theHalfSpread, theHalfSpread ) ); @@ -1465,7 +1487,7 @@ void EV_OffenseChamber(struct event_args_s* inArgs) theTempEntity->hitcallback = SpikeHit; theTempEntity->flags = (FTENT_COLLIDEALL | FTENT_PERSIST | FTENT_COLLIDEKILL); theTempEntity->die += kSpikeLifetime; - theTempEntity->clientIndex = inArgs->entindex; // Entity to ignore collisions with + theTempEntity->clientIndex = inArgs->iparam1; // Entity to ignore collisions with //theTempEntity->entity.curstate.framerate = 30; //theTempEntity->frameMax = 4;//theModel->numframes; @@ -1478,7 +1500,6 @@ void EV_OffenseChamber(struct event_args_s* inArgs) //VectorCopy(inArgs->angles, theTempEntity->entity.angles); } - // // // ASSERT(inArgs); @@ -2409,17 +2430,17 @@ void EV_Welder(struct event_args_s* inArgs) float thePercentage; AvHSHUGetBuildResearchState(thePhysEnt->iuser3, thePhysEnt->iuser4, thePhysEnt->fuser1, theIsBuilding, theIsResearching, thePercentage); - if(thePhysEnt->iuser3 == AVH_USER3_WELD) - { - if((thePercentage < 1.0f) && (thePercentage != -1)) - { - gHUD.SetProgressStatus(thePercentage); - } - else - { - gHUD.HideProgressStatus(); - } - } +// if(thePhysEnt->iuser3 == AVH_USER3_WELD) +// { +// if((thePercentage < 1.0f) && (thePercentage != -1)) +// { +// gHUD.SetProgressStatus(thePercentage); +// } +// else +// { +// gHUD.HideProgressStatus(); +// } +// } } } // Fire light smoke from muzzle @@ -2994,11 +3015,14 @@ void EV_Leap(struct event_args_s* inArgs) { char* theSoundToPlay = kLeapSound; - gEngfuncs.pEventAPI->EV_PlaySound(inArgs->entindex, inArgs->origin, CHAN_WEAPON, theSoundToPlay, inArgs->fparam1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf )); - if (EV_IsLocal(inArgs->entindex)) + if (EV_IsSpec(inArgs->entindex)) { gEngfuncs.pEventAPI->EV_WeaponAnimation(inArgs->iparam2, 2); + gEngfuncs.pEventAPI->EV_PlaySound(inArgs->entindex, inArgs->origin, CHAN_WEAPON, theSoundToPlay, inArgs->fparam1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf )); + } + if (!EV_IsLocal(inArgs->entindex)) { + gEngfuncs.pEventAPI->EV_PlaySound(inArgs->entindex, inArgs->origin, CHAN_WEAPON, theSoundToPlay, inArgs->fparam1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf )); } } @@ -3264,7 +3288,8 @@ void EV_MetabolizeSuccess(struct event_args_s* inArgs) void WebHit(struct tempent_s* ent, struct pmtrace_s* ptr) { - gEngfuncs.pEventAPI->EV_PlaySound(ent->entity.index, ptr->endpos, CHAN_AUTO, kWebSpinSound1, 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf )); + gEngfuncs.pEventAPI->EV_PlaySound(ent->entity.index, ptr->endpos, CHAN_AUTO, kWebStrandHitSound, 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf )); + //gEngfuncs.pEventAPI->EV_PlaySound(ent->entity.index, ptr->endpos, CHAN_AUTO, kWebSpinSound1, 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf )); if(ptr && ent) { ent->die = gEngfuncs.GetClientTime(); diff --git a/main/source/mod/AvHGamerules.cpp b/main/source/mod/AvHGamerules.cpp index 4ed6ac5..d159e39 100644 --- a/main/source/mod/AvHGamerules.cpp +++ b/main/source/mod/AvHGamerules.cpp @@ -1,4387 +1,4488 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: NS high-level game rules -// -// $Workfile: AvHGamerules.cpp $ -// $Date: 2002/11/22 21:23:55 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHGamerules.cpp,v $ -// Revision 1.79 2002/11/22 21:23:55 Flayra -// - Players can't switch teams after seeing a team -// - Adminmod fixes -// - Fixed bug where players were only doing friendly-fire damage to world entities (ie, 33% to doors and breakables) -// - Changed allowed team discrepancy from 2 to 1. Affects casual model only. -// - Fixes for timelimit -// - A team now automatically wins if they have 4 or more players then another team (generally because the losing team starts quitting). Affects casual mode only. -// -// Revision 1.78 2002/11/15 23:41:52 Flayra -// - Spectators get end-game victory sound and message -// -// Revision 1.77 2002/11/15 23:31:12 Flayra -// - Added "ready" verification for tourny mode -// -// Revision 1.76 2002/11/15 04:46:18 Flayra -// - Changes to for profiling and for improving AddToFullPack performance -// -// Revision 1.75 2002/11/12 18:44:10 Flayra -// - Allow team unbalancing in tourny mode (assume everyone knows what they're doing) -// -// Revision 1.74 2002/11/12 06:21:12 Flayra -// - More AdminMod fixes -// -// Revision 1.73 2002/11/12 02:24:50 Flayra -// - HLTV updates -// - Remove babblers on level clean up -// - Don't dynamic_cast all entities, it's causes AdminMod to crash -// - Log end-game stats in standard way -// - Potential fix for server overflow after big games -// -// Revision 1.72 2002/11/06 01:40:01 Flayra -// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) -// -// Revision 1.71 2002/11/05 06:17:25 Flayra -// - Balance changes -// -// Revision 1.70 2002/11/03 04:49:44 Flayra -// - Moved constants into .dll out of .cfg -// - Particle systems update much less often, big optimization -// - Team balance fixes -// -// Revision 1.69 2002/10/28 20:34:53 Flayra -// - Reworked game reset slightly, to meter out network usage during game reset -// -// Revision 1.68 2002/10/24 21:25:42 Flayra -// - Skin fixes (random number generator returning all 0s at some stages of the engine) -// - Builders don't get points for building anymore -// - Removed code that was never getting called in ClientConnected (AvHPlayer isn't built yet) -// - Remove armor and jetpacks on round reset -// - Update particle systems again, for particle culling -// - Unbuilt hives show up on hive sight -// -// Revision 1.67 2002/10/20 21:10:41 Flayra -// - Optimizations -// -// Revision 1.66 2002/10/20 17:24:41 Flayra -// - Redid performance improvement (agh) -// -// Revision 1.65 2002/10/20 16:36:09 Flayra -// - Added #ifdef for network metering -// - Added particles back in -// -// Revision 1.64 2002/10/19 22:33:44 Flayra -// - Various server optimizations -// -// Revision 1.63 2002/10/18 22:19:11 Flayra -// - Add sensory chamber saying -// -// Revision 1.62 2002/10/16 20:52:46 Flayra -// - Fixed bug introduced with secondary weapons -// -// Revision 1.61 2002/10/16 00:56:22 Flayra -// - Prototyped curl support for NS stats -// - Added player auth stuff (server string) -// - Added concept of secondary weapons -// - Removed disconnect sound -// - Fixed MOTD -// - Added "order needed" alert -// -// Revision 1.60 2002/10/07 17:49:50 Flayra -// - Play marine building alerts properly -// -// Revision 1.59 2002/10/03 18:44:03 Flayra -// - Play disconnected sound quieter because of mass exodus effect -// - Added functionality to allow players to join a team and run around freely before game countdown starts. Game resets when countdown starts. -// - Added new alien alerts -// -// Revision 1.58 2002/09/25 20:44:22 Flayra -// - Removed old LOS code -// - Added extra alerts, allow alien code to peacefully coexist -// -// Revision 1.57 2002/09/23 22:16:01 Flayra -// - Added game victory status logging -// - Fixed commander alerts -// - Added new alerts -// - Particle system changes, only send them down when connecting or map changes -// -// Revision 1.56 2002/09/09 19:51:13 Flayra -// - Removed gamerules dictating alien respawn time, now it's in server variable -// -// Revision 1.55 2002/08/31 18:01:01 Flayra -// - Work at VALVe -// -// Revision 1.54 2002/08/16 02:35:09 Flayra -// - Blips now update once per second, instead of once per player per second -// -// Revision 1.53 2002/08/09 00:58:35 Flayra -// - Removed error condition for map validity, allow marines to have only one primary weapon, adjust weapon weights -// -// Revision 1.52 2002/08/02 22:00:24 Flayra -// - New alert system that's not so annoying and is more helpful. Tweaks for new help system. -// -// Revision 1.51 2002/07/25 16:57:59 flayra -// - Linux changes -// -// Revision 1.50 2002/07/24 18:45:41 Flayra -// - Linux and scripting changes -// -// Revision 1.49 2002/07/23 17:03:20 Flayra -// - Resource model changes, refactoring spawning to fix level 5 redemption bug without code duplication -// -// Revision 1.48 2002/07/10 14:40:48 Flayra -// - Profiling and performance tweaks -// -// Revision 1.47 2002/07/08 16:59:14 Flayra -// - Don't pick up empty weapons, added handicapping, check map validity when loaded, reset players just like all other entities, fixed spawn bug code where it was counting non-team spawns -// -// Revision 1.46 2002/07/01 21:32:43 Flayra -// - Visibility update now updates world for primal scream and umbra, don't update reliable network messages every tick (big optimization) -// -// Revision 1.45 2002/06/25 17:59:03 Flayra -// - Added timelimit (switches map after a game finishes), added info_locations, tried adding team balance (not sure it works yet), give resources for kills -// -// Revision 1.44 2002/06/10 19:54:29 Flayra -// - New minimap support, more attempts to fix picking up of alien weapons -// -// Revision 1.43 2002/06/03 16:45:20 Flayra -// - Breakables and buttons take damage like they should, points added for buildables correctly, weapon weights tweaked -// -// Revision 1.42 2002/05/28 17:40:32 Flayra -// - Minimap refactoring, hive sight "under attack", reinforcement refactoring, don't delete entities marked as permanent (it was deleting hives!), allow players to join the opposite team until this can be fixed for real (server retry gets around this code so this is just an inconvenience), things build fast with cheats -// -// Revision 1.41 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "util/nowarnings.h" -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "dlls/cbase.h" -#include "dlls/player.h" -#include "dlls/weapons.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHConstants.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "mod/AvHEntities.h" -#include "mod/AvHParticleTemplateServer.h" -#include "mod/AvHPlayer.h" -#include "dlls/client.h" -#include "dlls/game.h" -#include "dlls/util.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHMessage.h" -#include "mod/AvHSoundListManager.h" -#include "mod/AvHMovementUtil.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHTitles.h" -#include "mod/AvHParticleSystemManager.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHEntities.h" -#include "mod/AvHVoiceHelper.h" -#include "common/director_cmds.h" -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHDramaticPriority.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHHulls.h" -#include "textrep/TRFactory.h" -#include -#include "mod/NetworkMeter.h" -#include "mod/AvHScriptManager.h" -#include "mod/AvHCloakable.h" -#include "mod/AvHCommandConstants.h" -#include "mod/AvHAlert.h" -#include "mod/AvHParticleConstants.h" -#include "util/MathUtil.h" -#include "mod/AvHNetworkMessages.h" -#include "mod/AvHNexusServer.h" - -// puzl: 0001073 -#ifdef USE_OLDAUTH -AuthMaskListType gAuthMaskList; -extern const char* kSteamIDPending; -extern const char* kSteamIDLocal; -extern const char* kSteamIDBot; -extern const char* kSteamIDInvalidID; -extern const char* kSteamIDDefault; -#endif - -AvHSoundListManager gSoundListManager; -extern AvHVoiceHelper gVoiceHelper; -CVoiceGameMgr g_VoiceGameMgr; -extern cvar_t allow_spectators; -extern cvar_t avh_autoconcede; -extern cvar_t avh_limitteams; -//extern cvar_t avh_teamsizehandicapping; -extern cvar_t avh_team1damagepercent; -extern cvar_t avh_team2damagepercent; -extern cvar_t avh_team3damagepercent; -extern cvar_t avh_team4damagepercent; -extern cvar_t avh_drawinvisible; -extern cvar_t avh_uplink; -extern cvar_t avh_gametime; -extern cvar_t avh_ironman; -extern cvar_t avh_mapvoteratio; - -BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ); -inline int FNullEnt( CBaseEntity *ent ) { return (ent == NULL) || FNullEnt( ent->edict() ); } -//extern void ResetCachedEntities(); - -// Allow assignment within conditional -#pragma warning (disable: 4706) - -// Quick way to define or undefine security -extern AvHParticleTemplateListServer gParticleTemplateList; -extern cvar_t avh_deathmatchmode; -extern cvar_t avh_countdowntime; -extern int gCommanderPointsAwardedEventID; -extern cvar_t avh_networkmeterrate; - -std::string gPlayerNames[128]; -cvar_t* cocName; -cvar_t* cocExp; -int gStartPlayerID = 0; -int gServerTick = 0; -int gUpdateEntitiesTick = 0; -bool gServerUpdate = false; -float kPVSCoherencyTime = 1.0f; -extern int kNumReturn0; -extern int kNumReturn1; -extern int kNumCached; -extern int kNumComputed; -extern int kMaxE; -int kServerFrameRate = 0; - -#ifdef PROFILE_BUILD -extern cvar_t avh_performance; -int kProfileRunConfig = 0xFFFFFFFF; -#endif - -std::string GetLogStringForPlayer( edict_t *pEntity ); - -// SCRIPTENGINE: -#include "scriptengine/AvHLUA.h" -extern AvHLUA *gLUA; -// :SCRIPTENGINE - -const AvHMapExtents& GetMapExtents() -{ - return GetGameRules()->GetMapExtents(); -} - -void InstallGameRules( void ) -{ - SERVER_COMMAND( "exec game.cfg\n" ); - SERVER_EXECUTE( ); - - AvHGamerules* theNewGamerules = new AvHGamerules; - SetGameRules(theNewGamerules); -} - -static AvHGamerules* sGameRules = NULL; - -AvHGamerules* GetGameRules() -{ - if(!g_pGameRules) - { - InstallGameRules(); - } - - if(!sGameRules) - { - sGameRules = dynamic_cast(g_pGameRules); - } - ASSERT(sGameRules); - - return sGameRules; -} - -void SetGameRules(AvHGamerules* inGameRules) -{ - sGameRules = inGameRules; - g_pGameRules = inGameRules; -} - -static float gSvCheatsLastUpdateTime; -AvHGamerules::AvHGamerules() : mTeamA(TEAM_ONE), mTeamB(TEAM_TWO) -{ - this->mGameStarted = false; - this->mPreserveTeams = false; - - this->mTeamA.SetTeamType(AVH_CLASS_TYPE_MARINE); - this->mTeamB.SetTeamType(AVH_CLASS_TYPE_ALIEN); - gSvCheatsLastUpdateTime = -1.0f; - this->mVictoryTime = -1; - this->mMapMode = MAP_MODE_UNDEFINED; - this->mLastParticleUpdate = -1; - this->mLastNetworkUpdate = -1; - this->mLastWorldEntityUpdate = -1; - this->mLastMapChange = -1; - this->mTimeOfLastPlaytestUpdate = -1; - this->mTimeOfLastHandicapUpdate = -1; - this->mMapGamma = kDefaultMapGamma; - this->mCombatAttackingTeamNumber = TEAM_IND; - this->mCheats.clear(); - this->mSpawnEntity = NULL; - - RegisterServerVariable(kvBlockScripts); - RegisterServerVariable(kvTournamentMode); - RegisterServerVariable(kvTeam1DamagePercent); - RegisterServerVariable(kvTeam2DamagePercent); - RegisterServerVariable(kvTeam3DamagePercent); - RegisterServerVariable(kvTeam4DamagePercent); - RegisterServerVariable("sv_cheats"); - - g_VoiceGameMgr.Init(&gVoiceHelper, gpGlobals->maxClients); - - #ifdef DEBUG - avh_drawinvisible.value = 1; - #endif - - this->ResetGame(); -} - -AvHGamerules::~AvHGamerules() -{ - int a = 0; -} - - -int AvHGamerules::AmmoShouldRespawn( CBasePlayerAmmo *pAmmo ) -{ - return GR_AMMO_RESPAWN_NO; -} - -int AvHGamerules::WeaponShouldRespawn(CBasePlayerItem *pWeapon) -{ - return GR_WEAPON_RESPAWN_NO; -} - -// puzl: 0001073 -#ifdef USE_OLDAUTH //players are authorized by UPP now. -const AuthIDListType& AvHGamerules::GetServerOpList() const -{ - return this->mServerOpList; -} - -bool AvHGamerules::PerformHardAuthorization(AvHPlayer* inPlayer) const -{ - bool theAuthorized = true; - - #ifdef AVH_SECURE_PRERELEASE_BUILD - if(!this->GetIsClientAuthorizedToPlay(inPlayer->edict(), false, true)) - { - char* theMessage = UTIL_VarArgs( - "%s<%s> is not authorized to play on beta NS servers.\n", - STRING(inPlayer->pev->netname), - AvHSUGetPlayerAuthIDString(inPlayer->edict()).c_str() - ); - ALERT(at_logged, theMessage); - - // Boot player off server - inPlayer->Kick(); - - theAuthorized = false; - } - #endif - - return theAuthorized; -} -#endif - -// Sets the player up to join the team, though they may not respawn in immediately depending -// on the ruleset and the state of the game. Assumes 1 or a 2 for team number -bool AvHGamerules::AttemptToJoinTeam(AvHPlayer* inPlayer, AvHTeamNumber inTeamToJoin, bool inDisplayErrorMessage) -{ - bool theSuccess = false; - string theErrorString; - - // Check authorization in secure build - if(!inPlayer->GetIsAuthorized(AUTH_ACTION_JOIN_TEAM,inTeamToJoin)) - { - AvHNexus::handleUnauthorizedJoinTeamAttempt(inPlayer->edict(),inTeamToJoin); - } - else -// puzl: 0001073 -#ifdef USE_OLDAUTH - if(this->PerformHardAuthorization(inPlayer)) -#endif - { - int teamA = this->mTeamA.GetTeamNumber(); - int teamB = this->mTeamB.GetTeamNumber(); - if( inTeamToJoin != teamA && inTeamToJoin != teamB && inTeamToJoin != TEAM_IND ) - { - theErrorString = kTeamNotAvailable; - } - else if(inPlayer->GetTeam() != TEAM_IND) - { - theErrorString = kAlreadyOnTeam; - } - // Can't join teams during the victory intermission (required by AvHPlayer::InternalCommonThink() for gradual player reset) - else if(this->mVictoryTeam != TEAM_IND) - { - int a = 0; - } - else if(this->GetCanJoinTeamInFuture(inPlayer, inTeamToJoin, theErrorString)) - { - this->JoinTeam(inPlayer, inTeamToJoin, inDisplayErrorMessage, false); - this->MarkDramaticEvent(kJoinTeamPriority, inPlayer->entindex()); - theSuccess = true; - } - - // Print error message to HUD - if(!theSuccess) - { - if(inDisplayErrorMessage) - { - // Display error string - inPlayer->SendMessage(theErrorString.c_str()); - } - } - else - { - // joev: Bug 0000767 - // Tell the other players that this player is joining a team. - if (!this->GetCheatsEnabled()) { - AvHTeam* theTeam = GetTeam(inTeamToJoin); - // ensure that the sound only plays if the game already has started - if (this->mGameStarted == true) { - theTeam->PlayHUDSoundForAlivePlayers(HUD_SOUND_PLAYERJOIN); - } - char* theMessage = UTIL_VarArgs("%s has joined the %s\n",STRING(inPlayer->pev->netname),theTeam->GetTeamPrettyName()); - UTIL_ClientPrintAll(HUD_PRINTTALK, theMessage); - UTIL_LogPrintf( "%s joined team \"%s\"\n", GetLogStringForPlayer( inPlayer->edict() ).c_str(), AvHSUGetTeamName(inPlayer->pev->team) ); - } - // :joev - } - } - return theSuccess; -} - -// Pick a team for the player to join. Choose the team with less players. -// If both teams have the same number of player, pick a team randomly -void AvHGamerules::AutoAssignPlayer(AvHPlayer* inPlayer) -{ - int theTeamACount = this->mTeamA.GetPlayerCount(); - int theTeamBCount = this->mTeamB.GetPlayerCount(); - - bool joinTeamA = (theTeamACount < theTeamBCount) || ( (theTeamACount == theTeamBCount) && RANDOM_LONG(0,1) ); - - AvHTeamNumber theTeam = (joinTeamA ? this->mTeamA.GetTeamNumber() : this->mTeamB.GetTeamNumber()); - - // Try to join the first team, but don't emit an error if it fails - if(!this->AttemptToJoinTeam(inPlayer, theTeam, false)) - { - // If it failed, try the other team and emit an error if it too fails - theTeam = (joinTeamA ? this->mTeamB.GetTeamNumber() : this->mTeamA.GetTeamNumber()); - this->AttemptToJoinTeam(inPlayer, theTeam, true); - } -} - -void AvHGamerules::RewardPlayerForKill(AvHPlayer* inPlayer, CBaseEntity* inTarget, entvars_t* inInflictor) -{ - ASSERT(inPlayer); - ASSERT(inTarget); - - // Doesn't count targets of TEAM_IND - if(inPlayer->pev->team != inTarget->pev->team && (inTarget->pev->team != TEAM_IND)) - { - // Team could be NULL if spectating and using cheats - AvHTeam* theTeamPointer = inPlayer->GetTeamPointer(); - - // Only award resources for killing players - if(theTeamPointer && inTarget->IsPlayer()) - { - if(!this->GetIsCombatMode()) - { - int theResourceValue = 0; - int theMin = BALANCE_VAR(kKillRewardMin); - int theMax = BALANCE_VAR(kKillRewardMax); - theResourceValue = RANDOM_LONG(theMin, theMax); - - if(theResourceValue > 0) - { - // Send killing player a message and sound, telling him he just got the kill and is getting the points - AvHClassType theClassType = inPlayer->GetClassType(); - if(theClassType == AVH_CLASS_TYPE_MARINE) - { - inPlayer->SendMessageOnce(kMarinePointsAwarded, TOOLTIP); - } - else if(theClassType == AVH_CLASS_TYPE_ALIEN) - { - inPlayer->SendMessageOnce(kAlienPointsAwarded, TOOLTIP); - } - - // Increment resource score in tourny mode - theTeamPointer->AddResourcesGathered(theResourceValue); - - AvHSUPlayNumericEvent(theResourceValue, inTarget->edict(), inTarget->pev->origin, 0, kNumericalInfoResourcesEvent, inPlayer->pev->team); - - inPlayer->SetResources(inPlayer->GetResources() + theResourceValue, true); - } - } - else - { - // Mine kills don't share experience - bool theShareExperience = true; - if(inInflictor) - { - const char* theClassName = STRING(inInflictor->classname); - if(theClassName && FStrEq(theClassName, kwsDeployedMine)) - { - theShareExperience = false; - } - } - - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - ASSERT(thePlayer); - this->AwardExperience(inPlayer, thePlayer->GetExperienceLevel(), theShareExperience); - } - } - - // Give points or frags for kill - int thePointReward = inTarget->GetPointValue(); - if(thePointReward != 0) - { - inPlayer->SetScore(inPlayer->GetScore() + thePointReward); - - if(inTarget->IsPlayer()) - inPlayer->pev->frags += 1; - inPlayer->EffectivePlayerClassChanged(); - } - } -} - -int AvHGamerules::IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled) -{ - // Return 0 because we increment our points via RewardPlayerForKill() - return 0; -} - -void AvHGamerules::BuildableBuilt(AvHBuildable* inBuildable) -{ - // Get player owner from buildable - edict_t* theEdict = g_engfuncs.pfnPEntityOfEntIndex(inBuildable->GetBuilder()); - if(theEdict != NULL) - { - CBaseEntity* theEntity = CBaseEntity::Instance(theEdict); - AvHPlayer* thePlayer = dynamic_cast(theEntity); - if(thePlayer) - { - AvHBaseBuildable* theBaseBuildable = dynamic_cast(inBuildable); - if(theBaseBuildable) - { - thePlayer->AddPoints(theBaseBuildable->GetPointValue(), TRUE); - } - } - } -} - -void AvHGamerules::BuildableKilled(AvHBuildable* inBuildable) -{ -} - -void AvHGamerules::BuildMiniMap(AvHPlayer* inPlayer) -{ - const char* theCStrLevelName = STRING(gpGlobals->mapname); - if(theCStrLevelName && !FStrEq(theCStrLevelName, "")) - { - this->mMiniMap.BuildMiniMap(theCStrLevelName, inPlayer, this->mMapExtents); - } -} - -BOOL AvHGamerules::CanHaveAmmo( CBasePlayer *pPlayer, const char *pszAmmoName, int iMaxCarry ) -{ - BOOL theCanHaveIt = CHalfLifeTeamplay::CanHaveAmmo(pPlayer, pszAmmoName, iMaxCarry); - return theCanHaveIt; -} - -// The player is touching an CBasePlayerItem, do I give it to him? -BOOL AvHGamerules::CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) -{ - BOOL theCanHaveIt = FALSE; - - // Allow it if we don't already have it - if(CHalfLifeTeamplay::CanHavePlayerItem(pPlayer, pWeapon) && !pPlayer->HasItem(pWeapon)) - { - // Don't allow players to have more then one primary weapon - ItemInfo theItemInfo; - pWeapon->GetItemInfo(&theItemInfo); - int theWeaponFlags = theItemInfo.iFlags; - - // Check primary and secondary weapons (assumes we only have one, but should work even with both) - int theCurrentFlag = (theWeaponFlags & (PRIMARY_WEAPON | SECONDARY_WEAPON)); - CBasePlayerItem* theCurrentItem = NULL; - bool theHasWeaponWithFlag = pPlayer->HasItemWithFlag(theCurrentFlag, theCurrentItem); - - if(theHasWeaponWithFlag) - { - if(theCurrentItem->iWeight() < pWeapon->iWeight()) - { - theCanHaveIt = TRUE; - } - else if(this->GetIsCombatMode()) - { - theCanHaveIt = TRUE; - } - } - else - { - theCanHaveIt = TRUE; - } - } - - return theCanHaveIt; -} - -bool AvHGamerules::CanPlayerBeKilled(CBasePlayer* inPlayer) -{ - bool theCanBeKilled = false; - - // Don't allow players that are being digested to suicide - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - if(thePlayer) - { - if(thePlayer->GetCanBeAffectedByEnemies() && this->GetGameStarted() && !thePlayer->GetIsBeingDigested()) - { - theCanBeKilled = true; - } - } - - return theCanBeKilled; -} - -void AvHGamerules::CalculateMapGamma() -{ - // Set defaults - this->mCalculatedMapGamma = kDefaultMapGamma; - - // Fetch from map extents entity if the map has one - FOR_ALL_ENTITIES(kwsGammaClassName, AvHGamma*) - this->mMapGamma = theEntity->GetGamma(); - END_FOR_ALL_ENTITIES(kwsGammaClassName) - - this->mCalculatedMapGamma = true; -} - -// puzl: 0001073 -#ifdef USE_OLDAUTH -BOOL AvHGamerules::GetIsClientAuthorizedToPlay(edict_t* inEntity, bool inDisplayMessage, bool inForcePending) const -{ - BOOL theIsAuthorized = false; - - #ifndef AVH_SECURE_PRERELEASE_BUILD - theIsAuthorized = true; - #endif - - #ifdef AVH_SECURE_PRERELEASE_BUILD - string theAuthID = AvHSUGetPlayerAuthIDString(inEntity); - const char* thePlayerName = STRING(inEntity->v.netname); - if(!strcmp(thePlayerName, "")) - { - thePlayerName = "unknown"; - } - - // Allow only select players to play - int theSecurityMask = PLAYERAUTH_DEVELOPER | PLAYERAUTH_PLAYTESTER | PLAYERAUTH_CONTRIBUTOR; - - // If any of these bits are set, allow them to play - int theAuthMask = 0; - - // Get the auth mask the cheap way if possible - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(inEntity)); - if(thePlayer) - { - theAuthMask = thePlayer->GetAuthenticationMask(); - } - else - { - theAuthMask = GetGameRules()->GetAuthenticationMask(theAuthID); - } - - if(theAuthMask & theSecurityMask) - { - if(inDisplayMessage) - { - char theAuthenticateString[512]; - sprintf(theAuthenticateString, "Player (%s -> %s) authenticated as privileged player.\r\n", thePlayerName, theAuthID.c_str()); - ALERT(at_logged, theAuthenticateString); - } - - theIsAuthorized = true; - } - // Pending - else if(theAuthID == kSteamIDPending) - { - if(!inForcePending) - { - // The player is authorized - theIsAuthorized = true; - } - } - // Local players or bots are always allowed - else if((theAuthID == kSteamIDLocal) || (theAuthID == kSteamIDBot)) - { - theIsAuthorized = true; - } - - // Display message on failure - if(!theIsAuthorized && inDisplayMessage) - { - char theAuthenticateString[512]; - sprintf(theAuthenticateString, "Player (%s -> %s) failed authentication.\r\n", thePlayerName, theAuthID.c_str()); - ALERT(at_logged, theAuthenticateString); - } - - #endif - - return theIsAuthorized; -} -#endif - -// puzl: 0001073 -#ifdef USE_OLDAUTH -BOOL AvHGamerules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) -{ - bool theAllowedToConnect = true; - BOOL theSuccess = false; - - #ifdef AVH_SECURE_PRERELEASE_BUILD - this->UpdateUplink(); - #endif - - #ifdef AVH_SECURE_PRERELEASE_BUILD - theAllowedToConnect = false; - theAllowedToConnect = this->GetIsClientAuthorizedToPlay(pEntity, true, false); - #endif - - if(theAllowedToConnect) - { - g_VoiceGameMgr.ClientConnected(pEntity); - - // Play connect sound - EMIT_SOUND(pEntity, CHAN_AUTO, kConnectSound, 0.8, ATTN_NORM); - - theSuccess = CHalfLifeTeamplay::ClientConnected(pEntity, pszName, pszAddress, szRejectReason); - } - else - { - sprintf(szRejectReason, "Only authorized players can join beta NS servers.\n"); - } - - return theSuccess; -} - -#else - -BOOL AvHGamerules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) -{ - BOOL theSuccess = false; - - g_VoiceGameMgr.ClientConnected(pEntity); - - // Play connect sound - EMIT_SOUND(pEntity, CHAN_AUTO, kConnectSound, 0.8, ATTN_NORM); - - theSuccess = CHalfLifeTeamplay::ClientConnected(pEntity, pszName, pszAddress, szRejectReason); - return theSuccess; -} -#endif - - -void AvHGamerules::ClientDisconnected( edict_t *pClient ) -{ - // Call down to base class - CHalfLifeTeamplay::ClientDisconnected(pClient); - - // Play disconnect sound (don't play, it encourages other people to leave) - //EMIT_SOUND(pClient, CHAN_AUTO, kDisconnectSound, 0.5, ATTN_NORM); - - // Remove him from whatever team he was on (does the player get killed first?) - CBaseEntity* theEntity = CBaseEntity::Instance(ENT(pClient)); - AvHPlayer* theAvHPlayer = dynamic_cast(theEntity); - if(theAvHPlayer) - { - theAvHPlayer->ClientDisconnected(); - } - - // trigger a votemap check - this->RemovePlayerFromVotemap(theEntity->entindex()); -} - -void AvHGamerules::ClientKill( edict_t *pEntity ) -{ -} - -void AvHGamerules::ClientUserInfoChanged(CBasePlayer *pPlayer, char *infobuffer) -{ - // NOTE: Not currently calling down to parent CHalfLifeTeamplay -} - -void AvHGamerules::ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib ) -{ - CHalfLifeTeamplay::ChangePlayerTeam(pPlayer, pTeamName, bKill, bGib); -} - - -int AvHGamerules::DeadPlayerAmmo( CBasePlayer *pPlayer ) -{ - return GR_PLR_DROP_AMMO_NO; -} - -int AvHGamerules::DeadPlayerWeapons(CBasePlayer* inPlayer) -{ - int theReturnCode = GR_PLR_DROP_GUN_NO; - - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - ASSERT(thePlayer); - if(thePlayer->GetIsMarine()) - { - theReturnCode = GR_PLR_DROP_GUN_ACTIVE; - } - - return theReturnCode; -} - -void AvHGamerules::DeathNotice(CBasePlayer* pVictim, entvars_t* pKiller, entvars_t* pInflictor) -{ - // TODO: do something here? Call CHalfLifeMultiplay::DeathNotice() to get normal death notices back - CHalfLifeTeamplay::DeathNotice(pVictim, pKiller, pInflictor); -} - -void AvHGamerules::DeleteAndResetEntities() -{ - // Print reset message at console - char theResetString[128]; - - sprintf(theResetString, "Game reset started.\n"); - ALERT(at_logged, theResetString); - - //ResetCachedEntities(); - - //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - // theEntity->Reset(); - //END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - // Clear out alien weapons - AvHSURemoveAllEntities(kwsBiteGun); - AvHSURemoveAllEntities(kwsParasiteGun); - AvHSURemoveAllEntities(kwsLeap); - AvHSURemoveAllEntities(kwsDivineWind); - AvHSURemoveAllEntities(kwsSpitGun); - AvHSURemoveAllEntities(kwsHealingSpray); - AvHSURemoveAllEntities(kwsBileBombGun); - AvHSURemoveAllEntities(kwsWebSpinner); - AvHSURemoveAllEntities(kwsSpikeGun); - AvHSURemoveAllEntities(kwsSporeGun); - AvHSURemoveAllEntities(kwsUmbraGun); - AvHSURemoveAllEntities(kwsPrimalScream); - AvHSURemoveAllEntities(kwsSwipe); - AvHSURemoveAllEntities(kwsBlinkGun); - AvHSURemoveAllEntities(kwsAcidRocketGun); - AvHSURemoveAllEntities(kwsMetabolize); - AvHSURemoveAllEntities(kwsClaws); - AvHSURemoveAllEntities(kwsDevour); - AvHSURemoveAllEntities(kwsStomp); - AvHSURemoveAllEntities(kwsCharge); - - // Clear out marine weapons - AvHSURemoveAllEntities(kwsMachineGun); - AvHSURemoveAllEntities(kwsPistol); - AvHSURemoveAllEntities(kwsShotGun); - AvHSURemoveAllEntities(kwsHeavyMachineGun); - AvHSURemoveAllEntities(kwsGrenadeGun); - AvHSURemoveAllEntities(kwsMine); - AvHSURemoveAllEntities(kwsWelder); - AvHSURemoveAllEntities(kwsKnife); - - // Remove alien items and projectiles - AvHSURemoveAllEntities(kwsAcidRocket); - AvHSURemoveAllEntities(kwsBileBomb); - AvHSURemoveAllEntities(kwsBabblerProjectile); - AvHSURemoveAllEntities(kwsSpitProjectile); - AvHSURemoveAllEntities(kwsSporeProjectile); - AvHSURemoveAllEntities(kwsStomp); - AvHSURemoveAllEntities(kwsUmbraCloud); - AvHSURemoveAllEntities(kwsUmbraProjectile); - AvHSURemoveAllEntities(kesTeamWebStrand); - - // Remove marine items and projectiles - AvHSURemoveAllEntities(kwsDeployedMine); - AvHSURemoveAllEntities(kwsHeavyArmor); - AvHSURemoveAllEntities(kwsJetpack); - AvHSURemoveAllEntities(kwsGrenade); - AvHSURemoveAllEntities(kwsScan); - AvHSURemoveAllEntities(kwsGenericAmmo); - AvHSURemoveAllEntities(kwsHealth); - AvHSURemoveAllEntities(kwsWelder); - AvHSURemoveAllEntities(kwsCatalyst); - AvHSURemoveAllEntities(kwsAmmoPack); - - // Remove all non-persistent entities marked buildable - //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::DeleteAndResetEntities\n"); - - FOR_ALL_BASEENTITIES(); - AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); - if(GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_BUILDABLE) && (!theBuildable || !theBuildable->GetIsPersistent())) - { - ASSERT(theBaseEntity->pev->iuser3 != AVH_USER3_HIVE); - if(theBaseEntity->pev->iuser3 == AVH_USER3_HIVE) - { - int a = 0; - } - UTIL_Remove(theBaseEntity); - } - END_FOR_ALL_BASEENTITIES(); - - // Delete all non-persistent base buildables. - //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::DeleteAndResetEntities#2\n"); - - FOR_ALL_BASEENTITIES(); - AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); - if(theBuildable && !theBuildable->GetIsPersistent()) - { - UTIL_Remove(theBuildable); - } - END_FOR_ALL_BASEENTITIES(); - - // TODO: Remove decals - - this->ResetEntities(); - - FireTargets(ktGameReset, NULL, NULL, USE_TOGGLE, 0.0f); - - sprintf(theResetString, "Game reset complete.\n"); - ALERT(at_logged, theResetString); -} - -BOOL AvHGamerules::FAllowMonsters( void ) -{ - return TRUE; -} - -BOOL AvHGamerules::FPlayerCanRespawn( CBasePlayer *pPlayer ) -{ - bool theCanRespawn = false; - - return theCanRespawn; -} - -bool AvHGamerules::CanEntityDoDamageTo(const CBaseEntity* inAttacker, const CBaseEntity* inReceiver, float* outScalar) -{ - bool theCanDoDamage = false; - - if(inAttacker && inReceiver) - { - AvHTeamNumber theAttackerTeam = (AvHTeamNumber)inAttacker->pev->team; - AvHTeamNumber theReceiverTeam = (AvHTeamNumber)inReceiver->pev->team; - bool theTeamsAreDifferent = (theAttackerTeam != theReceiverTeam); - bool theTeamsAreOpposing = theTeamsAreDifferent && (theAttackerTeam != TEAM_IND) && (theReceiverTeam != TEAM_IND); - bool theGameHasStarted = this->GetGameStarted(); - bool theIsBreakable = (inReceiver->pev->iuser3 == AVH_USER3_BREAKABLE); - bool theIsDoor = !strcmp(STRING(inReceiver->pev->classname), kesFuncDoor) || !strcmp(STRING(inReceiver->pev->classname), "func_door_rotating") || !strcmp(STRING(inReceiver->pev->classname), "func_button"); - bool theAttackerIsSiege = inAttacker->pev->classname == MAKE_STRING(kwsSiegeTurret); - bool theAttackerIsMine = inAttacker->pev->classname == MAKE_STRING(kwsDeployedMine); - bool theReceiverIsMine = inReceiver->pev->classname == MAKE_STRING(kwsDeployedMine); - bool theIsFriendlyFireEnabled = friendlyfire.value; - bool theAttackerIsWorld = false; - bool theReceiverIsPlayer = false; - bool theAttackerIsReceiver = false; - float theScalar = 1.0f; - bool theReceiverIsWorld = false; - - // Never dynamic_cast any entities that could be non-NS entities - if(!AvHSUGetIsExternalClassName(STRING(inAttacker->pev->classname))) - { - theAttackerIsWorld = (dynamic_cast(inAttacker) != NULL); - } - - if(!AvHSUGetIsExternalClassName(STRING(inAttacker->pev->classname))) - { - theReceiverIsPlayer = (dynamic_cast(inReceiver) != NULL); - } - - if(!AvHSUGetIsExternalClassName(STRING(inReceiver->pev->classname))) - { - theReceiverIsWorld = (dynamic_cast(inReceiver) != NULL); - } - - if(!theReceiverIsWorld) - { - if((inAttacker == inReceiver) || (CBaseEntity::Instance(inAttacker->pev->owner) == inReceiver)) - { - theAttackerIsReceiver = true; - } - - // If we're in tournament mode and two teams are different, yes - if(theGameHasStarted) - { - if(theTeamsAreOpposing || theIsFriendlyFireEnabled || theIsBreakable || theIsDoor || theAttackerIsWorld || theAttackerIsReceiver || ( theAttackerIsMine && theTeamsAreDifferent )) - { - theCanDoDamage = true; - // Do less damage with friendly fire - if(theAttackerTeam == theReceiverTeam) - { - theScalar = .33f; - } - - if(theAttackerIsReceiver) - { - theScalar = .5f; - } - } - } - } - - // Mines never blow up friendly mines or anything else - if(theAttackerIsMine) - { - // Check if teams are the same - if( inAttacker->pev->team == inReceiver->pev->team ) - { - theCanDoDamage = false; - } - } - - // Mines can't be blown up by anonymous sources (prevents mines from blowing up other mines) - if(theReceiverIsMine && theAttackerIsWorld) - { - theCanDoDamage = false; - } - - if(inReceiver->pev->takedamage == DAMAGE_NO) - { - theCanDoDamage = false; - } - if ( theAttackerIsSiege && theReceiverIsPlayer ) - { - theCanDoDamage = false; - } - if(theCanDoDamage && outScalar) - { - *outScalar = theScalar; - } - } - return theCanDoDamage; -} - - -BOOL AvHGamerules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ) -{ - BOOL theCanTakeDamage = TRUE; - - if(!this->GetDeathMatchMode() && pAttacker) - { - AvHPlayer* thePlayer = dynamic_cast(pPlayer); - if(thePlayer->GetInReadyRoom()) - { - theCanTakeDamage = FALSE; - } - // Friendly fire only in tournament mode (allow players to damage themselves) - else if((pPlayer != pAttacker) && this->PlayerRelationship(pPlayer, pAttacker) == GR_TEAMMATE) - { - if(!(friendlyfire.value)) - { - theCanTakeDamage = FALSE; - } - } - } - - return theCanTakeDamage; -} - -void AvHGamerules::ComputeWorldChecksum(Checksum& outChecksum) const -{ - // Run through all entities in the world, adding their checksum - //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::ComputeWorldChecksum\n"); - - FOR_ALL_BASEENTITIES(); - theBaseEntity->AddChecksum(outChecksum); - END_FOR_ALL_BASEENTITIES(); -} - -bool AvHGamerules::GetArePlayersAllowedToJoinImmediately(void) const -{ - bool thePlayerIsAllowedToJoinImmediately = false; - - // if the game hasn't started - if(!this->mGameStarted) - { - thePlayerIsAllowedToJoinImmediately = true; - } - // if cheats are enabled - else if(this->GetCheatsEnabled()) - { - thePlayerIsAllowedToJoinImmediately = true; - } - else - { - // if it's not tournament mode and it's within x seconds of game start - float theLateJoinSeconds = CVAR_GET_FLOAT(kvLateJoinTime)*60; - - if(!this->GetIsTournamentMode() && (gpGlobals->time < (this->mTimeGameStarted + theLateJoinSeconds))) - { - thePlayerIsAllowedToJoinImmediately = true; - } - } - - return thePlayerIsAllowedToJoinImmediately; -} - -bool AvHGamerules::GetCanJoinTeamInFuture(AvHPlayer* inPlayer, AvHTeamNumber inTeamNumber, string& outString) const -{ - // You can switch teams, unless - bool theCanJoinTeam = false; - - // You can always go back to ready room - if(inTeamNumber == TEAM_IND) - { - theCanJoinTeam = true; - } - - // You can always join before the game has started - // Don't allow team switching once the game has started, or ever in tourny mode - else if(inPlayer->GetTeam() != inTeamNumber) - { - // Server ops can ignore team balance - if( inPlayer->GetIsMember(PLAYERAUTH_SERVEROP) ) - { - theCanJoinTeam = true; - } - else - { - // Check to see if teams are wildly unbalanced - AvHTeamNumber teamA = this->mTeamA.GetTeamNumber(); - AvHTeamNumber teamB = this->mTeamB.GetTeamNumber(); - AvHTeamNumber theOtherTeamNumber = (inTeamNumber == teamA) ? teamB : teamA; - - const AvHTeam* theTeam = this->GetTeam(inTeamNumber); - const AvHTeam* theOtherTeam = this->GetTeam(theOtherTeamNumber); - - if( theTeam && theOtherTeam ) - { - int theWouldBeNumPlayersOnTeam = theTeam->GetPlayerCount() + 1; - int theWouldBeNumPlayersOnOtherTeam = theOtherTeam->GetPlayerCount(); - - // Subtract ourselves off - if(inPlayer->GetTeam() == theOtherTeamNumber) - { - theWouldBeNumPlayersOnOtherTeam--; - } - - int theDiscrepancyAllowed = max(1.0f, avh_limitteams.value); - if(((theWouldBeNumPlayersOnTeam - theWouldBeNumPlayersOnOtherTeam) <= theDiscrepancyAllowed) || this->GetIsTournamentMode() || this->GetCheatsEnabled()) - { - // tankefugl: 0000953 - if (!(this->GetCheatsEnabled()) && !(inPlayer->JoinTeamCooledDown(BALANCE_VAR(kJoinTeamCooldown)))) - outString = kJoinTeamTooFast; - else - // :tankefugl - theCanJoinTeam = true; - } - else - { - outString = kTooManyPlayersOnTeam; - } - } - } - } - - return theCanJoinTeam; -} - -const AvHBaseInfoLocationListType& AvHGamerules::GetInfoLocations() const -{ - return mInfoLocations; -} - -bool AvHGamerules::GetCheatsEnabled(void) const -{ - static float theCheatsEnabled = CVAR_GET_FLOAT( "sv_cheats" ); - if (gpGlobals->time > (gSvCheatsLastUpdateTime + 0.5f)) - { - theCheatsEnabled = CVAR_GET_FLOAT( "sv_cheats" ); - gSvCheatsLastUpdateTime = gpGlobals->time; - } - return (theCheatsEnabled == 1.0f); -} - -float AvHGamerules::GetFirstScanThinkTime() const -{ - return this->GetIsCombatMode() ? 0.25f : 0.5f; -} - -bool AvHGamerules::GetDrawInvisibleEntities() const -{ - bool theDrawInvisible = false; - - #ifdef DEBUG - if(CVAR_GET_FLOAT(kvDrawInvisible) > 0) - { - theDrawInvisible = true; - } - #endif - - return theDrawInvisible; -} - -bool AvHGamerules::GetDeathMatchMode(void) const -{ - bool theInDMMode = false; - - // deathmatch mode means there is no ready room - if(avh_deathmatchmode.value == 1.0f) - { - theInDMMode = true; - } - - return theInDMMode; -} - -bool AvHGamerules::GetEntityExists(const char* inName) const -{ - bool theSuccess = false; - - edict_t* theEnt = FIND_ENTITY_BY_CLASSNAME(NULL, inName); - if(!FNullEnt(theEnt)) - { - theSuccess = true; - } - else - { - // Look in spawns - for(SpawnListType::const_iterator theIter = this->mSpawnList.begin(); theIter != this->mSpawnList.end(); theIter++) - { - if(!strcmp(theIter->GetClassName().c_str(), inName)) - { - theSuccess = true; - break; - } - } - } - - return theSuccess; -} - -const char* AvHGamerules::GetGameDescription(void) -{ - static char sGameDescription[512]; - - sprintf(sGameDescription, "%s %s", kAvHGameAcronymn, AvHSUGetGameVersionString()); - - return sGameDescription; -} - -//edict_t* AvHGamerules::GetPlayerSpawnSpot( CBasePlayer *pPlayer ) -//{ -//} - -bool AvHGamerules::GetCountdownStarted() const -{ - return this->mStartedCountdown; -} - -bool AvHGamerules::GetGameStarted() const -{ - return this->mGameStarted; -} - -int AvHGamerules::GetGameTime() const -{ - int theGameTime = 0; - - if(this->GetGameStarted()) - { - theGameTime = (gpGlobals->time - this->mTimeGameStarted); - } - - return theGameTime; -} - -float AvHGamerules::GetMapGamma() -{ - if(!this->mCalculatedMapGamma) - { - this->CalculateMapGamma(); - } - - return this->mMapGamma; -} - -const AvHGameplay& AvHGamerules::GetGameplay() const -{ - return this->mGameplay; -} - -bool AvHGamerules::GetIsTesting(void) const -{ - return CVAR_GET_FLOAT(kvTesting) > 0; -} - -bool AvHGamerules::GetIsTournamentMode(void) const -{ - return (CVAR_GET_FLOAT(kvTournamentMode) == 1.0f); -} - -bool AvHGamerules::GetIsCombatMode(void) const -{ - return (this->GetMapMode() == MAP_MODE_CO); -} - -AvHTeamNumber AvHGamerules::GetCombatAttackingTeamNumber() const -{ - return this->mCombatAttackingTeamNumber; -} - -bool AvHGamerules::GetIsNSMode(void) const -{ - return (this->GetMapMode() == MAP_MODE_NS); -} - -bool AvHGamerules::GetIsScriptedMode(void) const -{ - return (this->GetMapMode() == MAP_MODE_NSC); -} - -bool AvHGamerules::GetIsHamboneMode() const -{ - return this->GetIsCombatMode() && (CVAR_GET_FLOAT(kvIronMan) == 2); -} - -bool AvHGamerules::GetIsIronMan(void) const -{ - return this->GetIsCombatMode() && (CVAR_GET_FLOAT(kvIronMan) == 1); -} - -bool AvHGamerules::GetIsTrainingMode(void) const -{ - return (CVAR_GET_FLOAT(kvTrainingMode) == 1.0f); -} - -AvHMapMode AvHGamerules::GetMapMode(void) const -{ - return this->mMapMode; -} - -const AvHMapExtents& AvHGamerules::GetMapExtents() -{ - this->mMapExtents.CalculateMapExtents(); - - return this->mMapExtents; -} - -AvHEntityHierarchy& AvHGamerules::GetEntityHierarchy(AvHTeamNumber inTeam) -{ - if(inTeam == this->mTeamA.GetTeamNumber()) - { - return this->mTeamAEntityHierarchy; - } - else if(inTeam == this->mTeamB.GetTeamNumber()) - { - return this->mTeamBEntityHierarchy; - } - else - { - ASSERT(false); - } - return this->mTeamAEntityHierarchy; -} - -bool AvHGamerules::GetIsPlayerSelectableByPlayer(AvHPlayer* inTargetPlayer, AvHPlayer* inByPlayer) -{ - ASSERT(inTargetPlayer); - ASSERT(inByPlayer); - - bool thePlayerIsSelectable = false; - - if(inTargetPlayer && inTargetPlayer->IsAlive() && (inTargetPlayer != inByPlayer)) - { - if(inTargetPlayer->pev->team == inByPlayer->pev->team) - { - if(!inTargetPlayer->IsObserver()) - { - thePlayerIsSelectable = true; - } - } - } - - return thePlayerIsSelectable; -} - -int AvHGamerules::GetServerTick() const -{ - return gServerTick; -} - -const char* AvHGamerules::GetSpawnEntityName(AvHPlayer* inPlayer) const -{ - // Come back to ready room by default (in case there is a bug, go back to ready room, don't crash) - const char* theSpawnEntityName = kesReadyRoomStart; - - AvHClassType theClassType = inPlayer->GetClassType(); - - // If there is no no avh start points, try to start up as CS. If that doesn't look - // right, always return DM spawns. - if((this->mMapMode == MAP_MODE_NS) || (this->mMapMode == MAP_MODE_CO) || (this->mMapMode == MAP_MODE_NSC)) - { - // The different cases: - // Player just connected to server and hasn't done anything yet OR - // Player is leaving game and going back to ready room - if(theClassType == AVH_CLASS_TYPE_UNDEFINED) - { - // Use ready room spawn - theSpawnEntityName = kesReadyRoomStart; - } - else - { - theSpawnEntityName = kesTeamStart; - } - } - else if(this->mMapMode == MAP_MODE_CS) - { - switch(theClassType) - { - // "CT" - case AVH_CLASS_TYPE_MARINE: - theSpawnEntityName = kesCounterTerroristStart; - inPlayer->SetPendingCommand(kcJoinTeamOne); - break; - - // "T" - case AVH_CLASS_TYPE_ALIEN: - theSpawnEntityName = kesTerroristStart; - inPlayer->SetPendingCommand(kcJoinTeamTwo); - break; - - // Random - case AVH_CLASS_TYPE_UNDEFINED: - case AVH_CLASS_TYPE_AUTOASSIGN: - if(RANDOM_LONG(0, 1) == 0) - { - theSpawnEntityName = kesCounterTerroristStart; - inPlayer->SetPendingCommand(kcJoinTeamOne); - } - else - { - theSpawnEntityName = kesTerroristStart; - inPlayer->SetPendingCommand(kcJoinTeamTwo); - } - break; - } - } - else if(this->mMapMode == MAP_MODE_DM) - { - theSpawnEntityName = kesDeathMatchStart; - } - - return theSpawnEntityName; -} - -float AvHGamerules::GetTimeGameStarted() const -{ - return this->mTimeGameStarted; -} - -int AvHGamerules::GetTimeLimit() const -{ - float theMinutes = CVAR_GET_FLOAT("mp_timelimit"); - - if(this->GetIsCombatMode()) - { - theMinutes = CVAR_GET_FLOAT(kvCombatTime); - } - - int theTimeLimit = (int)(theMinutes*60); - - return theTimeLimit; -} - -const AvHTeam* AvHGamerules::GetTeam(AvHTeamNumber inTeamNumber) const -{ - const AvHTeam* theTeam = NULL; - - if( this->mTeamA.GetTeamNumber() == inTeamNumber ) - { theTeam = &this->mTeamA; } - if( this->mTeamB.GetTeamNumber() == inTeamNumber ) - { theTeam = &this->mTeamB; } - return theTeam; -} - -const AvHTeam* AvHGamerules::GetTeamA(void) const -{ - return &this->mTeamA; -} - -const AvHTeam* AvHGamerules::GetTeamB(void) const -{ - return &this->mTeamB; -} - -AvHTeam* AvHGamerules::GetTeam(AvHTeamNumber inTeamNumber) -{ - AvHTeam* theTeam = NULL; - - if( this->mTeamA.GetTeamNumber() == inTeamNumber ) - { theTeam = &this->mTeamA; } - if( this->mTeamB.GetTeamNumber() == inTeamNumber ) - { theTeam = &this->mTeamB; } - return theTeam; -} -AvHTeam* AvHGamerules::GetTeamA(void) -{ - return &this->mTeamA; -} - -AvHTeam* AvHGamerules::GetTeamB(void) -{ - return &this->mTeamB; -} - -AvHTeamNumber AvHGamerules::GetVictoryTeam() const -{ - return this->mVictoryTeam; -} - -float AvHGamerules::GetVictoryTime() const -{ - return this->mVictoryTime; -} - -// Assumes full encumbered weight around 50 -int AvHGamerules::GetMaxWeight(void) const -{ - return 30; -} - -int AvHGamerules::GetNumCommandersOnTeam(AvHTeamNumber inTeam) -{ - const AvHTeam* theTeam = this->GetTeam(inTeam); - ASSERT(theTeam); - - int theNumCommanders = (theTeam->GetCommander() == -1 ? 0 : 1); - return theNumCommanders; -} - -int AvHGamerules::GetNumActiveHives(AvHTeamNumber inTeam) const -{ - int theNumHives = 0; - - const AvHTeam* theTeam = this->GetTeam(inTeam); - if(theTeam) - { - theNumHives = theTeam->GetNumActiveHives(); - } - - return theNumHives; -} - -int AvHGamerules::GetNumEntities() const -{ - int theNumEntities = 0; - - //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::GetNumEntities\n"); - -// FOR_ALL_BASEENTITIES(); -// theNumEntities++; -// END_FOR_ALL_BASEENTITIES(); - - theNumEntities = g_engfuncs.pfnNumberOfEntities(); - - return theNumEntities; -} - -int AvHGamerules::GetWeightForItemAndAmmo(AvHWeaponID inWeapon, int inNumRounds) const -{ - // Assumes full encumbered weight around 50 - - int theWeight = 0; - ASSERT(inNumRounds >= 0); - - switch(inWeapon) - { - case AVH_WEAPON_NONE: - break; - - case AVH_WEAPON_MG: - theWeight += 4; - theWeight += (inNumRounds/100.0f)*1; - break; - - case AVH_WEAPON_PISTOL: - theWeight += 2; - theWeight += (inNumRounds/8.0f)*.5; - break; - - case AVH_WEAPON_KNIFE: - // No weight for the knife - break; - - case AVH_WEAPON_SONIC: - theWeight += 5; - theWeight += (inNumRounds/10.0f)*2; - break; - - case AVH_WEAPON_HMG: - theWeight += 7; - theWeight += (inNumRounds/150.0f)*2; - break; - - case AVH_WEAPON_GRENADE_GUN: - theWeight += 7; - theWeight += (inNumRounds/4.0f)*1; - break; - - case AVH_WEAPON_WELDER: - theWeight += 4; - break; - -// case AVH_WEAPON_NUKE: -// theWeight += 18; -// theWeight += (inNumRounds/5.0f)*8; -// break; - - //case AVH_WEAPON_FLAMER: - // theWeight += 10; - // break; - - case AVH_WEAPON_MINE: - // Only judge by amount of ammo - theWeight += inNumRounds; - break; - } - - return theWeight; -} - -BOOL AvHGamerules::IsMultiplayer( void ) -{ - return TRUE; -} - -BOOL AvHGamerules::IsDeathmatch( void ) -{ - return FALSE; -} - -BOOL AvHGamerules::IsCoOp( void ) -{ - return FALSE; -} - -void AvHGamerules::InitHUD( CBasePlayer *pPlayer ) -{ - // Call down to base class first (this may need to be changed later but make sure to update spectator cam) - CHalfLifeTeamplay::InitHUD(pPlayer); - // notify other clients of player joining the game - UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs( "%s has joined the game\n", ( pPlayer->pev->netname && STRING(pPlayer->pev->netname)[0] != 0 ) ? STRING(pPlayer->pev->netname) : "unconnected" ) ); -} - - -void GetMapNamesFromMapCycle(StringList& outMapNameList); - -void AvHGamerules::InitializeMapVoteList() -{ - // Add each map - this->mMapVoteList.clear(); - this->mPlayersVoted.clear(); - this->mPlayersVoteTime.clear(); - - StringList theMapNames; - GetMapNamesFromMapCycle(theMapNames); - - // Traverse list - for(StringList::iterator theIter = theMapNames.begin(); theIter != theMapNames.end(); theIter++) - { - this->mMapVoteList.push_back( make_pair(*theIter, 0) ); - } -} - -void AvHGamerules::PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) -{ - ItemInfo theItemInfo; - pWeapon->GetItemInfo(&theItemInfo); - int theWeaponFlags = theItemInfo.iFlags; - //bool theDroppedWeapon = false; - - for(int i = 0; i < 2; i++) - { - int theCurrentFlag = (i == 0 ? PRIMARY_WEAPON : SECONDARY_WEAPON); - - CBasePlayerItem* theCurrentItem = NULL; - bool theHasWeaponWithFlag = pPlayer->HasItemWithFlag(theCurrentFlag, theCurrentItem); - - if(((theWeaponFlags & theCurrentFlag) && pPlayer->HasItemWithFlag(theCurrentFlag, theCurrentItem))) - { - // Discard the one we have - char theWeaponName[256]; - const char* theCurrentWeaponName = theCurrentItem->pszName(); - ASSERT(theCurrentWeaponName); - - strcpy(theWeaponName, theCurrentWeaponName); - AvHPlayer* thePlayer = dynamic_cast(pPlayer); - if(thePlayer) - { - if(GetGameRules()->GetIsCombatMode()) - { - thePlayer->RemovePlayerItem(theCurrentItem); - theCurrentItem->DestroyItem(); - } - else - { - thePlayer->DropItem(theWeaponName); - } - } - //theDroppedWeapon = true; - } - } - - //if(!theDroppedWeapon) - //{ - CHalfLifeTeamplay::PlayerGotWeapon(pPlayer, pWeapon); - //} - - if(GetGameRules()->GetIsCombatMode()) - { - if(!AvHSUGetIsExternalClassName(STRING(pWeapon->pev->classname))) - { - AvHBasePlayerWeapon* theWeapon = dynamic_cast(pWeapon); - if(theWeapon) - { - // Spawn with default ammo - ItemInfo theItemInfo; - if(theWeapon->UsesAmmo() && theWeapon->GetItemInfo(&theItemInfo) && theWeapon->GetCanBeResupplied()) - { - //int theMaxPrimary = theItemInfo.iMaxAmmo1; - int theStartAmmo = theItemInfo.iMaxClip*BALANCE_VAR(kCombatSpawnClips); - - // Add some to primary store - pPlayer->m_rgAmmo[theWeapon->m_iPrimaryAmmoType] = theStartAmmo; - } - } - } - } -} - -Vector AvHGamerules::GetSpawnAreaCenter(AvHTeamNumber inTeamNumber) const -{ - Vector theCenter(0, 0, 0); - int theNumSpawns = 0; - - for(SpawnListType::const_iterator theIter = this->mSpawnList.begin(); theIter != this->mSpawnList.end(); theIter++) - { - if(theIter->GetTeamNumber() == inTeamNumber) - { - VectorAdd(theIter->GetOrigin(), theCenter, theCenter); - theNumSpawns++; - } - } - - if(theNumSpawns > 0) - { - VectorScale(theCenter, 1.0f/theNumSpawns, theCenter); - } - - return theCenter; -} - -void AvHGamerules::PerformMapValidityCheck() -{ - // Perform check to see that we have enough of all the entities - int theNumReadyRoomSpawns = 0; - int theNumTeamASpawns = 0; - int theNumTeamBSpawns = 0; -// CBaseEntity* theSpawn = NULL; -// while((theSpawn = UTIL_FindEntityByClassname(theSpawn, kesReadyRoomStart)) != NULL) -// { -// theNumReadyRoomSpawns++; -// } - - for(SpawnListType::const_iterator theIter = this->mSpawnList.begin(); theIter != this->mSpawnList.end(); theIter++) - { - if(!strcmp(theIter->GetClassName().c_str(), kesReadyRoomStart)) - { - theNumReadyRoomSpawns++; - } - else if(theIter->GetTeamNumber() == this->mTeamA.GetTeamNumber()) - { - theNumTeamASpawns++; - } - else if(theIter->GetTeamNumber() == this->mTeamB.GetTeamNumber()) - { - theNumTeamBSpawns++; - } - } - - int theNumDesiredReadyRoomSpawns = 32; - int theNumDesiredTeamASpawns = this->mTeamA.GetDesiredSpawnCount(); - int theNumDesiredTeamBSpawns = this->mTeamB.GetDesiredSpawnCount(); - - if((theNumReadyRoomSpawns < theNumDesiredReadyRoomSpawns) || (theNumTeamASpawns < theNumDesiredTeamASpawns) || (theNumTeamBSpawns < theNumDesiredTeamBSpawns)) - { - ALERT(at_logged, "Map validity check failure: %d/%d ready room spawns, %d/%d team A spawns, %d/%d team B spawns.\n", theNumReadyRoomSpawns, theNumDesiredReadyRoomSpawns, theNumTeamASpawns, theNumDesiredTeamASpawns, theNumTeamBSpawns, theNumDesiredTeamBSpawns); - } - else - { - ALERT(at_logged, "Map validity check success.\n"); - } -} - -void AvHGamerules::PostWorldPrecacheInitParticles() -{ - // When a player first connects, send down all particle info - ASSERT(gParticleTemplateList.GetCreatedTemplates()); - - // Make sure client clears out all particle systems first - int theNumberTemplates = gParticleTemplateList.GetNumberTemplates(); - for(int i = 0; i < theNumberTemplates; i++) - { - AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(i); - gParticleTemplateList.LinkToEntities(theTemplate); - } -} - -void AvHGamerules::PreWorldPrecacheInitParticles() -{ - // Reset templates and everything - AvHParticleSystemManager::Instance()->Reset(); - - // Load up the particle systems from modname.ps then levelname.ps - TRDescriptionList theDescriptionList; - string theLevelBaseSystemFile = string(getModDirectory()) + "/" + kBasePSName; - if(TRFactory::ReadDescriptions(theLevelBaseSystemFile, theDescriptionList)) - { - gParticleTemplateList.CreateTemplates(theDescriptionList); - } - theDescriptionList.clear(); - - // TODO: the level name isn't populated yet for some reason - const char* theCStrLevelName = STRING(gpGlobals->mapname); - if(theCStrLevelName && !FStrEq(theCStrLevelName, "")) - { - string theLevelName = theCStrLevelName; - string theLevelParticleSystemFile = string(getModDirectory()) + string("/") + theLevelName + string(".ps"); - if(TRFactory::ReadDescriptions(theLevelParticleSystemFile, theDescriptionList)) - { - gParticleTemplateList.CreateTemplates(theDescriptionList); - } - } -} - -// Emit line to log indicating victory status -void AvHGamerules::TallyVictoryStats() const -{ - int theGameLength = (int)(gpGlobals->time - this->mTimeGameStarted); - int theGameLengthMinutes = theGameLength/60; - int theGameLengthSeconds = theGameLength % 60; - - const char* thePrintableMapName = "ns_?"; - const char* theMapName = STRING(gpGlobals->mapname); - if(theMapName) - { - thePrintableMapName = theMapName; - } - - const AvHTeam* theVictoryTeam = &this->mTeamA; - const AvHTeam* theLosingTeam = &this->mTeamB; - - if(this->mVictoryTeam == this->mTeamB.GetTeamNumber()) - { - theVictoryTeam = &this->mTeamB; - theLosingTeam = &this->mTeamA; - } - - ALERT(at_logged, "Team \"%d\" scored \"%d\" with \"%d\" players\n", this->mTeamA.GetTeamNumber(), this->mTeamA.GetTotalResourcesGathered(), this->mTeamA.GetPlayerCount()); - ALERT(at_logged, "Team \"%d\" scored \"%d\" with \"%d\" players\n", this->mTeamB.GetTeamNumber(), this->mTeamB.GetTotalResourcesGathered(), this->mTeamB.GetPlayerCount()); - - if(!this->mVictoryDraw) - { - ALERT(at_logged, "(map_name \"%s\") (game_version \"%s\") (game_time \"%02d:%02d\") (victory_team \"%s\") (losing_team \"%s\") (winning_teamsize \"%d\") (losing_teamsize \"%d\")\n", thePrintableMapName, AvHSUGetGameVersionString(), theGameLengthMinutes, theGameLengthSeconds, theVictoryTeam->GetTeamTypeString(), theLosingTeam->GetTeamTypeString(), theVictoryTeam->GetPlayerCount(), theLosingTeam->GetPlayerCount()); - } - else - { - ALERT(at_logged, "(map_name \"%s\") (game_version \"%s\") (game_time \"%02d:%02d\") (victory_team \"draw\") (losing_team \"draw\")\n", thePrintableMapName, AvHSUGetGameVersionString(), theGameLengthMinutes, theGameLengthSeconds); - } -} - -void AvHGamerules::InitializeTechNodes() -{ - this->mTeamA.InitializeTechNodes(); - this->mTeamB.InitializeTechNodes(); -} - -void AvHGamerules::PlayerDeathEnd(AvHPlayer* inPlayer) -{ - ASSERT(inPlayer); - - if(inPlayer->GetPlayMode() == PLAYMODE_PLAYING) - { - inPlayer->pev->nextthink = -1; - inPlayer->SetPlayMode(PLAYMODE_AWAITINGREINFORCEMENT, true); - } -} - -void AvHGamerules::PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ) -{ - // Tell gamerules - CHalfLifeTeamplay::PlayerKilled(pVictim, pKiller, pInflictor); - - CBaseEntity* theBaseKiller = CBaseEntity::Instance(pKiller); - AvHPlayer* theKiller = dynamic_cast(theBaseKiller); - if(!theKiller) - { - CBaseEntity* theOwner = CBaseEntity::Instance(theBaseKiller->pev->owner); - theKiller = dynamic_cast(theOwner); - } - - if(theKiller) - { - this->RewardPlayerForKill(theKiller, pVictim, pInflictor); - } -} - - -// From CHalfLifeMultiplay::PlayerSpawn -void AvHGamerules::PlayerSpawn( CBasePlayer *pPlayer ) -{ - AvHPlayer* theAvHPlayer = dynamic_cast(pPlayer); - BOOL addDefault; - CBaseEntity* pWeaponEntity = NULL; - - pPlayer->pev->weapons |= (1<Touch( pPlayer ); - addDefault = FALSE; - } - - if ( addDefault ) - { - theAvHPlayer->pev->team = theAvHPlayer->GetTeam(); - //theAvHPlayer->InitializeFromTeam(); - //this->GetTeam(theAvHPlayer->GetTeam())->AddPlayer(theAvHPlayer->entindex(), theAvHPlayer->GetRole()); - } -} - -void AvHGamerules::PlayerThink( CBasePlayer *pPlayer ) -{ - AvHPlayer* thePlayer = dynamic_cast(pPlayer); - - // Update base - CHalfLifeTeamplay::PlayerThink(pPlayer); -} - -// Reset all players, remove weapons off the ground, etc. -void AvHGamerules::PostWorldPrecacheReset(bool inNewMap) -{ - EntityListType theJoinedTeamA; - EntityListType theJoinedTeamB; - EntityListType theSpectating; - - // If we're preserving teams - if(this->mPreserveTeams) - { - // Remember the players that had already joined - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - int theEntityIndex = theEntity->entindex(); - - if(theEntity->pev->team == this->mTeamA.GetTeamNumber()) - { - theJoinedTeamA.push_back(theEntityIndex); - } - else if(theEntity->pev->team == this->mTeamB.GetTeamNumber()) - { - theJoinedTeamB.push_back(theEntityIndex); - } - else if(theEntity->pev->team == TEAM_IND) - { - if(theEntity->GetPlayMode() == PLAYMODE_OBSERVER) - { - theSpectating.push_back(theEntityIndex); - } - } - - theEntity->SendMessageNextThink(kGameStarting); - - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - } - - // Stop running all scripts - AvHScriptManager::Instance()->Reset(); - - this->DeleteAndResetEntities(); - - gServerTick = 0; - gServerUpdate = false; - this->mTimeUpdatedScripts = -1; - - // Reset game on teams - this->mGameplay.Reset(); - - // Find gameplay entity if it exists - FOR_ALL_ENTITIES(kwsGameplayClassName, AvHGameplay*) - this->mGameplay = *theEntity; - END_FOR_ALL_ENTITIES(kwsMapInfoClassName) - - this->mTeamA.ResetGame(); - this->mTeamB.ResetGame(); - - this->mTeamAEntityHierarchy.Clear(); - this->mTeamBEntityHierarchy.Clear(); - - // Set up both sides - this->mTeamA.SetTeamType(this->mGameplay.GetTeamAType()); - this->mTeamB.SetTeamType(this->mGameplay.GetTeamBType()); - - // Set team numbers differently if teams are the same, so their colors change - // Marines vs. marines will be team 1 vs, team 3, aliens vs. aliens will be team 2 vs. team 4 - if(this->mTeamA.GetTeamType() == this->mTeamB.GetTeamType()) - { - if(this->mTeamA.GetTeamType() == AVH_CLASS_TYPE_MARINE) - { - this->mTeamA.SetTeamNumber(TEAM_ONE); - this->mTeamB.SetTeamNumber(TEAM_THREE); - this->mTeamB.SetTeamName(kMarine2Team); - this->mTeamB.SetTeamPrettyName(kMarinePrettyName); - } - else - { - this->mTeamA.SetTeamNumber(TEAM_TWO); - this->mTeamA.SetTeamName(kAlien1Team); - this->mTeamA.SetTeamPrettyName(kAlienPrettyName); - this->mTeamB.SetTeamNumber(TEAM_FOUR); - this->mTeamB.SetTeamName(kAlien2Team); - this->mTeamB.SetTeamPrettyName(kAlienPrettyName); - } - } - - this->InitializeTechNodes(); - - this->PostWorldPrecacheInitParticles(); - - // Set round as not started - this->InternalResetGameRules(); - - this->mEntitiesUnderAttack.clear(); - - this->mMapExtents.ResetMapExtents(); - - this->mCalculatedMapGamma = false; - - // TODO: Clear min/max map sizes? Others? - - // Find info location entities, store our own internal representation, then delete them for efficiency - if(inNewMap || (this->mSpawnList.size() == 0)) - { - this->mInfoLocations.clear(); - - FOR_ALL_ENTITIES(kesInfoLocation, AvHInfoLocation*) - AvHBaseInfoLocation theLocation(*theEntity); - this->mInfoLocations.push_back(theLocation); - UTIL_Remove(theEntity); - END_FOR_ALL_ENTITIES(kesInfoLocation); - - // Clear spawns on a new map only - this->mSpawnList.clear(); - if(this->mSpawnEntity) - { - UTIL_Remove(this->mSpawnEntity); - } - - FOR_ALL_ENTITIES(kesReadyRoomStart, CPointEntity*) - string theClassName(STRING(theEntity->pev->classname)); - this->RegisterSpawnPoint(theClassName, theEntity->pev->origin, theEntity->pev->angles, TEAM_IND); - UTIL_Remove(theEntity); - END_FOR_ALL_ENTITIES(kesReadyRoomStart); - - FOR_ALL_ENTITIES(kesTeamStart, AvHTeamStartEntity*) - string theClassName(STRING(theEntity->pev->classname)); - this->RegisterSpawnPoint(theClassName, theEntity->pev->origin, theEntity->pev->angles, theEntity->GetTeamNumber()); - UTIL_Remove(theEntity); - END_FOR_ALL_ENTITIES(kesTeamStart); - - // Create dummy entity we can use to return from SelectSpawnPoint - this->mSpawnEntity = CBaseEntity::Create(kesReadyRoomStart, Vector(), Vector()); - ASSERT(this->mSpawnEntity); - - // Added by mmcguire. - this->mSpawnEntity->pev->effects |= EF_NODRAW; - - // Clear map voting on a level change - this->mMapVoteList.clear(); - this->mPlayersVoted.clear(); - this->mPlayersVoteTime.clear(); - } - - // Must happen after processing spawn entities - this->RecalculateMapMode(); - - // Loop through all players that are playing and respawn them - - bool theJustResetGameAtCountdownStart = false; - - // Now tell those players to all join again - EntityListType::iterator theIter, end = theJoinedTeamA.end(); - for(theIter = theJoinedTeamA.begin(); theIter != end; ++theIter) - { - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter))); - if(thePlayer) - { - GetGameRules()->JoinTeam(thePlayer, this->mTeamA.GetTeamNumber(), false, true); - } - theJustResetGameAtCountdownStart = true; - } - theJoinedTeamA.clear(); - - // Now tell those players to all join again - end = theJoinedTeamB.end(); - for(theIter = theJoinedTeamB.begin(); theIter != end; ++theIter) - { - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter))); - if(thePlayer) - { - GetGameRules()->JoinTeam(thePlayer, this->mTeamB.GetTeamNumber(), false, true); - } - theJustResetGameAtCountdownStart = true; - } - theJoinedTeamB.clear(); - - // Rejoin spectators - for(theIter = theSpectating.begin(); theIter != theSpectating.end(); theIter++) - { - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter))); - if(thePlayer) - { - thePlayer->SetPlayMode(PLAYMODE_OBSERVER); - } - theJustResetGameAtCountdownStart = true; - } - theSpectating.clear(); - - if(theJustResetGameAtCountdownStart) - { - this->mTimeCountDownStarted = this->mSavedTimeCountDownStarted; - this->mStartedCountdown = true; - } - - // SCRIPTENGINE: Load map and execute OnLoad - gLUA->Init(); - if (gLUA->LoadLUAForMap(STRING(gpGlobals->mapname))) - gLUA->OnLoad(); - - //gVoiceHelper.Reset(); -} - -void AvHGamerules::JoinTeam(AvHPlayer* inPlayer, AvHTeamNumber inTeamToJoin, bool inDisplayErrorMessage, bool inForce) -{ - // Report new player status - int thePrevTeam = inPlayer->pev->team; - inPlayer->pev->team = inTeamToJoin; - - inPlayer->InitializeFromTeam(); - - AvHServerPlayerData* theServerPlayerData = inPlayer->GetServerPlayerData(); - - bool thePlayerCanJoinImmediately = this->GetArePlayersAllowedToJoinImmediately() && this->SelectSpawnPoint(inPlayer) || inForce; - - if(theServerPlayerData - && theServerPlayerData->GetHasJoinedTeam() - && GetGameRules()->GetGameStarted() - && !inForce - && inTeamToJoin != TEAM_IND - && !GetGameRules()->GetCheatsEnabled()) - thePlayerCanJoinImmediately = false; - - if(thePlayerCanJoinImmediately) - { - inPlayer->SetPlayMode(PLAYMODE_PLAYING); - } - else - { - inPlayer->SetPlayMode(PLAYMODE_AWAITINGREINFORCEMENT); - inPlayer->SendMessage(kJoinSoon); - } - - if(inTeamToJoin != TEAM_IND) - { - UTIL_LogPrintf( "%s joined team \"%s\"\n", GetLogStringForPlayer( inPlayer->edict() ).c_str(), AvHSUGetTeamName(inPlayer->pev->team) ); - if(theServerPlayerData) - theServerPlayerData->SetHasJoinedTeam(true); - } - - // SCRIPTENGINE: Join team - if (this->GetIsScriptedMode()) - if (thePrevTeam != inTeamToJoin) - gLUA->OnJointeam(inPlayer->entindex(), inTeamToJoin); - // :SCRIPTENGINE -} - -// This is called before any entities are spawned, every time the map changes, including the first time -void AvHGamerules::PreWorldPrecacheReset() -{ - gParticleTemplateList.Clear(); - AvHMP3Audio::ClearSoundNameList(); - this->PreWorldPrecacheInitParticles(); -} - -void AvHGamerules::ProcessRespawnCostForPlayer(AvHPlayer* inPlayer) -{ - AvHTeam* theTeam = inPlayer->GetTeamPointer(); - if(theTeam) - { - AvHClassType theTeamType = theTeam->GetTeamType(); - if(theTeamType == AVH_CLASS_TYPE_ALIEN) - { - } - else if(theTeamType == AVH_CLASS_TYPE_MARINE) - { - this->MarkDramaticEvent(kReinforcementsPriority, inPlayer); - } - } - // No team? Player died in the ready room...fix? -} - -void AvHGamerules::ProcessTeamUpgrade(AvHMessageID inUpgrade, AvHTeamNumber inNumber, int inEntity, bool inGive) -{ - AvHTeam* theTeam = this->GetTeam(inNumber); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) - { - // Add or remove the upgrade from every entity that's on the team - FOR_ALL_BASEENTITIES(); - if(theBaseEntity->pev->team == inNumber) - { - AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); - if(thePlayer) - { - thePlayer->GiveTeamUpgrade(inUpgrade, inGive); - } - else - { - ProcessGenericUpgrade(theBaseEntity->pev->iuser4, inUpgrade, inGive); - } - } - END_FOR_ALL_BASEENTITIES(); - - // Store it in the team too, so new players and entities can be initialized properly - theTeam->ProcessTeamUpgrade(inUpgrade, inGive); - } -} - -bool AvHGamerules::ReadyToStartCountdown() -{ - bool theReadyToStartCountdown = false; - - bool theAtLeastOneOnTeamA = this->mTeamA.GetPlayerCount() > 0; - bool theAtLeastOneOnTeamB = this->mTeamB.GetPlayerCount() > 0; - - if(this->GetCheatsEnabled()) - { - if(theAtLeastOneOnTeamA || theAtLeastOneOnTeamB) - { - theReadyToStartCountdown = true; - } - } - else if(theAtLeastOneOnTeamA && theAtLeastOneOnTeamB) - { - //need to ready up for tournament mode; otherwise just need players on each team - if( !this->GetIsTournamentMode() || ( this->mTeamA.GetIsReady() && this->mTeamB.GetIsReady() ) ) - { - theReadyToStartCountdown = true; - } - } - - return theReadyToStartCountdown; -} - -void AvHGamerules::RecalculateHandicap() -{ - const float kHandicapMax = 100.0f; - - // Teams can enforce their own handicaps if they want (cap to valid values) - float handicaps[4]; - handicaps[0] = max(min(kHandicapMax, avh_team1damagepercent.value), 0.0f); - handicaps[1] = max(min(kHandicapMax, avh_team2damagepercent.value), 0.0f); - handicaps[2] = max(min(kHandicapMax, avh_team3damagepercent.value), 0.0f); - handicaps[3] = max(min(kHandicapMax, avh_team4damagepercent.value), 0.0f); - - // Set handicap scalars - this->mTeamA.SetHandicap(handicaps[this->mTeamA.GetTeamNumber()-1]/kHandicapMax); - this->mTeamB.SetHandicap(handicaps[this->mTeamB.GetTeamNumber()-1]/kHandicapMax); - - avh_team1damagepercent.value = handicaps[0]; - avh_team2damagepercent.value = handicaps[1]; - avh_team3damagepercent.value = handicaps[2]; - avh_team4damagepercent.value = handicaps[3]; -} - -// Called when dedicated server exits -void AvHGamerules::ServerExit() -{ - AvHNexus::shutdown(); -} - -void AvHGamerules::VoteMap(int inPlayerIndex, int inMapIndex) -{ - // check to remove votemap spam - map< int, float >::iterator thePlayerVoteTime = this->mPlayersVoteTime.find(inPlayerIndex); - - if (thePlayerVoteTime != this->mPlayersVoteTime.end()) - { - if (thePlayerVoteTime->second + 3.0f > gpGlobals->time) - return; - - thePlayerVoteTime->second = gpGlobals->time; - } - else - { - this->mPlayersVoteTime.insert(pair < int, float >(inPlayerIndex, gpGlobals->time)); - } - - if(avh_mapvoteratio.value != -1) - { - if(this->mMapVoteList.size() == 0) - { - this->InitializeMapVoteList(); - } - - // If this is a valid map - if((inMapIndex > 0) && (inMapIndex <= (signed)this->mMapVoteList.size())) - { - - PlayerMapVoteListType::iterator theMappedPlayer = this->mPlayersVoted.find(inPlayerIndex); - - // Increment votes for map - MapVoteListType::iterator theIter = (MapVoteListType::iterator)&this->mMapVoteList[inMapIndex-1]; - int theVotes = ++theIter->second; - - // If player has already voted, decrement previous map and update which map the player has voted - if(theMappedPlayer != this->mPlayersVoted.end()) { - ((MapVoteListType::iterator)&this->mMapVoteList[theMappedPlayer->second - 1])->second--; - theMappedPlayer->second = inMapIndex; - } - // else, remember the "new" player's vote - else - { - this->mPlayersVoted.insert(pair < int, int >(inPlayerIndex, inMapIndex)); - } - - // Tell everyone - CBaseEntity* theVotingPlayer = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex)); - ASSERT(theVotingPlayer); - char* theMessage = UTIL_VarArgs("%s executed votemap %d (%s %d/%d)\n", STRING(theVotingPlayer->pev->netname), inMapIndex, theIter->first.c_str(), theIter->second, this->GetVotesNeededForMapChange()); - UTIL_ClientPrintAll(HUD_PRINTTALK, theMessage); - UTIL_LogPrintf( "%s executed votemap %d (%s %d/%d)\n", GetLogStringForPlayer( theVotingPlayer->edict() ).c_str(), inMapIndex, theIter->first.c_str(), theIter->second, this->GetVotesNeededForMapChange()); - - // Does this map enough votes to change? - if(theVotes >= this->GetVotesNeededForMapChange()) - { - // Changelevel now - char theLevelName[256]; - strcpy(theLevelName, theIter->first.c_str()); - CHANGE_LEVEL(theLevelName, NULL); - } - - } - } -} - -// remove a player's vote from the votemap list and trigger a mapchange check -void AvHGamerules::RemovePlayerFromVotemap(int inPlayerIndex) -{ - PlayerMapVoteListType::iterator theMappedPlayer = this->mPlayersVoted.find(inPlayerIndex); - - // If player has voted, decrement the map voted for - if(theMappedPlayer != this->mPlayersVoted.end()) { - ((MapVoteListType::iterator)&this->mMapVoteList[theMappedPlayer->second - 1])->second--; - this->mPlayersVoted.erase(inPlayerIndex); - } - - // trigger mapchange check - int theVotesNeeded = this->GetVotesNeededForMapChange(); - for(MapVoteListType::iterator theMapIterator = this->mMapVoteList.begin(); - theMapIterator != this->mMapVoteList.end(); - theMapIterator++) - { - if (theMapIterator->second >= theVotesNeeded) - { - // Changelevel now - char theLevelName[256]; - strcpy(theLevelName, theMapIterator->first.c_str()); - CHANGE_LEVEL(theLevelName, NULL); - break; - } - } -} - -bool AvHGamerules::GetMapVoteStrings(StringList& outMapVoteList) -{ - bool theSuccess = false; - - if(avh_mapvoteratio.value != -1) - { - if(this->mMapVoteList.size() == 0) - { - this->InitializeMapVoteList(); - } - - int theMapIndex = 1; - outMapVoteList.clear(); - - for(MapVoteListType::iterator theIter = this->mMapVoteList.begin(); theIter != this->mMapVoteList.end(); theIter++) - { - string theOutputString = MakeStringFromInt(theMapIndex) + ") " + theIter->first + " (" + MakeStringFromInt(theIter->second) + "/" + MakeStringFromInt(this->GetVotesNeededForMapChange()) + ")\n"; - outMapVoteList.push_back(theOutputString); - theMapIndex++; - } - - theSuccess = true; - } - - return theSuccess; -} - -int AvHGamerules::GetVotesNeededForMapChange() const -{ - int theNumVotes = -1; - - float theMapVoteRatio = avh_mapvoteratio.value; - if(theMapVoteRatio > 0) - { - theMapVoteRatio = min(max(0.0f, theMapVoteRatio), 1.0f); - theNumVotes = max(theMapVoteRatio*this->GetNumberOfPlayers(), 1.0f); - } - - return theNumVotes; -} - -void AvHGamerules::RegisterSpawnPoint(const string& inClassName, const Vector& inOrigin, const Vector& inAngles, const AvHTeamNumber& inTeamNumber) -{ - AvHSpawn theSpawnPoint(inClassName, inOrigin, inAngles, inTeamNumber); - this->mSpawnList.push_back(theSpawnPoint); -} - -void AvHGamerules::RespawnPlayer(AvHPlayer* inPlayer) -{ - bool thePlayerCanRespawn = this->FPlayerCanRespawn(inPlayer); - ASSERT(thePlayerCanRespawn); - - this->ProcessRespawnCostForPlayer(inPlayer); - - // Clear alien upgrades, they must be repurchased. Soldier upgrades stick. - AvHTeam* theTeam = inPlayer->GetTeamPointer(); - if(theTeam) - { - if(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - inPlayer->pev->iuser4 = 0; - } - - respawn(inPlayer->pev, !(inPlayer->m_afPhysicsFlags & PFLAG_OBSERVER));// don't copy a corpse if we're in deathcam. - inPlayer->pev->nextthink = -1; - - // Bring us back in the game - if(theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) - { - inPlayer->SetUser3(AVH_USER3_MARINE_PLAYER, true); - } - else if(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - inPlayer->SetUser3(AVH_USER3_ALIEN_PLAYER1, true); - } - } -} - -void AvHGamerules::ResetGame(bool inPreserveTeams) -{ - // Reset game rules - this->mFirstUpdate = true; - this->mPreserveTeams = inPreserveTeams; - gSvCheatsLastUpdateTime = -1.0f; -} - -void AvHGamerules::RecalculateMapMode( void ) -{ - // Recalculate map mode we're in. MAP_MODE_UNDEFINED means just try to spawn somewhere. - - // Check for AVH entities - if((this->GetEntityExists(kesReadyRoomStart)) && (this->GetEntityExists(kesTeamStart))) - { - const char* theCStrLevelName = STRING(gpGlobals->mapname); - if(theCStrLevelName && (strlen(theCStrLevelName) > 3)) - { - if(!strnicmp(theCStrLevelName, "ns_", 3)) - { - if(this->GetEntityExists(kesTeamHive)) - { - if(this->GetEntityExists(kwsTeamCommand)) - { - this->mMapMode = MAP_MODE_NS; - this->PerformMapValidityCheck(); - } - } - } - else if(!strnicmp(theCStrLevelName, "co_", 3)) - { - this->mMapMode = MAP_MODE_CO; - } - else if(!strnicmp(theCStrLevelName, "nsc_", 4)) - { - this->mMapMode = MAP_MODE_NSC; - } - } - } - // Check for CS entities - else if(this->GetEntityExists(kesTerroristStart) && this->GetEntityExists(kesCounterTerroristStart)) - { - this->mMapMode = MAP_MODE_CS; - } - else if(this->GetEntityExists(kesDeathMatchStart)) - { - this->mMapMode = MAP_MODE_DM; - } -} - -bool AvHGamerules::RoamingAllowedForPlayer(CBasePlayer* inPlayer) const -{ - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - bool theRoamingAllowed = false; - - if(thePlayer) - { - if((thePlayer->GetPlayMode() == PLAYMODE_OBSERVER)) - { - theRoamingAllowed = true; - } - - const AvHTeam* theTeam = this->GetTeam(thePlayer->GetTeam()); - if(theTeam) - { - if(theTeam->GetPlayerCount() == 0) - { - theRoamingAllowed = true; - } - } - else - { - AvHTeamNumber teamA = this->mTeamA.GetTeamNumber(); - AvHTeamNumber teamB = this->mTeamB.GetTeamNumber(); - theTeam = this->GetTeam( (thePlayer->GetTeam() == teamA ? teamB : teamA) ); - if(theTeam) - { - if(theTeam->GetPlayerCount() == 0) - { - theRoamingAllowed = true; - } - } - } - } - - return theRoamingAllowed; -} - -BOOL AvHGamerules::FShouldSwitchWeapon(CBasePlayer* inPlayer, CBasePlayerItem* inWeapon) -{ - BOOL theShouldSwitch = CHalfLifeTeamplay::FShouldSwitchWeapon(inPlayer, inWeapon); - if(theShouldSwitch) - { - AvHBasePlayerWeapon* theWeapon = dynamic_cast(inWeapon); - if(theWeapon && !theWeapon->GetIsCapableOfFiring()) - { - theShouldSwitch = false; - } - } - return theShouldSwitch; -} - -void AvHGamerules::MarkDramaticEvent(int inPriority, CBaseEntity* inPrimaryEntity, bool inDramatic, CBaseEntity* inSecondaryEntity) const -{ - ASSERT(inPrimaryEntity); - short inPrimaryEntityIndex = ENTINDEX(inPrimaryEntity->edict()); - short inSecondaryEntityIndex = !inSecondaryEntity ? 0 : ENTINDEX(inSecondaryEntity->edict()); - - this->MarkDramaticEvent(inPriority, inPrimaryEntityIndex, inDramatic, inSecondaryEntityIndex); -} - -void AvHGamerules::MarkDramaticEvent(int inPriority, short inPrimaryEntityIndex, bool inDramatic, short inSecondaryEntityIndex) const -{ - ASSERT(inPriority >= kMinDramaticPriority); - ASSERT(inPriority <= kMaxDramaticPriority); - - MESSAGE_BEGIN(MSG_SPEC, SVC_DIRECTOR); - WRITE_BYTE(DIRECTOR_MESSAGE_SIZE); - WRITE_BYTE(DRC_CMD_EVENT); - WRITE_SHORT(inPrimaryEntityIndex); - WRITE_SHORT(inSecondaryEntityIndex); - - int thePriority = inPriority; - if(inDramatic) - { - thePriority |= DRC_FLAG_DRAMATIC; - } - - WRITE_LONG(thePriority); - MESSAGE_END(); -} - -void AvHGamerules::MarkDramaticEvent(int inPriority, short inPrimaryEntityIndex, bool inDramatic, entvars_t* inSecondaryEntity) const -{ - // Changed by mmcguire. Sometimes the attacker is NULL (e.g. when a - // player creates a turret and then leaves the game), so we need to - // handle that case. - short secondaryEntityIndex = 0; - - if (inSecondaryEntity != NULL) - { - secondaryEntityIndex = OFFSET(inSecondaryEntity); - } - - this->MarkDramaticEvent(inPriority, inPrimaryEntityIndex, secondaryEntityIndex); -} - -void AvHGamerules::ResetEntities() -{ - // Now reset all the world entities and mark useable ones with AVH_USER3_USEABLE - //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::ResetEntities\n"); - - FOR_ALL_BASEENTITIES(); - - // Reset the entity. Assumes that players in the ready room have already been reset recently. - AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); - if(thePlayer && (thePlayer->GetPlayMode() == PLAYMODE_READYROOM)) - { - int theUser3 = thePlayer->pev->iuser3; - int theUser4 = thePlayer->pev->iuser4; - int thePlayMode = thePlayer->pev->playerclass; - int thePlayerTeam = thePlayer->pev->team; - int theSolidType = thePlayer->pev->solid; - - thePlayer->ResetEntity(); - - thePlayer->pev->iuser3 = theUser3; - thePlayer->pev->iuser4 = theUser4; - thePlayer->pev->playerclass = thePlayMode; - thePlayer->pev->team = thePlayerTeam; - thePlayer->pev->solid = theSolidType; - } - else - { - theBaseEntity->ResetEntity(); - } - - // Don't mark commander stations as useable in this case - AvHCommandStation* theCommandStation = dynamic_cast(theBaseEntity); - if(!theCommandStation) - { - int theObjectCaps = theBaseEntity->ObjectCaps(); - if(theObjectCaps & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE)) - { - // After playing once, this is no longer zero - if(theBaseEntity->pev->iuser3 == 0) - { - theBaseEntity->pev->iuser3 = AVH_USER3_USEABLE; - - // Now also mark the target entity as useable! - if (!FStringNull(theBaseEntity->pev->target)) - { - CBaseEntity* theTarget = NULL; - - while(theTarget = UTIL_FindEntityByTargetname(theTarget, STRING(theBaseEntity->pev->target))) - { - theTarget->pev->iuser3 = AVH_USER3_USEABLE; - } - } - } - } - } - END_FOR_ALL_BASEENTITIES(); -} - -void AvHGamerules::InternalResetGameRules() -{ - if(AvHNexus::isRecordingGame()) - { - AvHNexus::cancelGame(); - } - this->mGameStarted = false; - this->mTimeCountDownStarted = 0; - this->mTimeGameStarted = -1; - this->mTimeOfLastGameTimeUpdate = -1; - this->mTimeOfLastHLTVProxyUpdate = -1; - this->mTimeSentCountdown = 0; - this->mTimeLastWontStartMessageSent = 0; - this->mStartedCountdown = false; - this->mSentCountdownMessage = false; - this->mTimeWorldReset = gpGlobals->time; - this->mCombatAttackingTeamNumber = TEAM_IND; - gSvCheatsLastUpdateTime = -1.0f; - if(this->mLastMapChange == -1) - { - this->mLastMapChange = gpGlobals->time; - } - - this->mVictoryTeam = TEAM_IND; - this->mVictoryDraw = false; - this->mVictoryTime = 0; - this->mCheats.clear(); - this->mLastParticleUpdate = -1; - this->mLastNetworkUpdate = -1; - this->mLastWorldEntityUpdate = -1; - this->mLastCloakableUpdate = -1; - this->mLastVictoryUpdate = -1; -// puzl: 0001073 -#ifdef USE_OLDAUTH //under UPP, we don't want to reestablish a connection with each new game - this->mUpdatedUplink = false; -#endif - -} - -void AvHGamerules::CopyDataToSpawnEntity(const AvHSpawn& inSpawnEntity) const -{ - VectorCopy(inSpawnEntity.GetOrigin(), this->mSpawnEntity->pev->origin); - VectorCopy(inSpawnEntity.GetAngles(), this->mSpawnEntity->pev->angles); - VectorCopy(inSpawnEntity.GetAngles(), this->mSpawnEntity->pev->v_angle); - this->mSpawnEntity->pev->team = inSpawnEntity.GetTeamNumber(); -} - -// Look for spawns in radius -CBaseEntity* AvHGamerules::GetRandomHiveSpawnPoint(CBaseEntity* inPlayer, const Vector& inOrigin, float inMaxDistance) const -{ - CBaseEntity* theSpawnPoint = NULL; - - typedef vector EntityIndexListType; - EntityIndexListType theSpawnIndexList; - - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - ASSERT(thePlayer); - - // Loop through all spawns, looking for valid spawns within range - int theIndex = 0; - SpawnListType::const_iterator theIter; - for(theIter = this->mSpawnList.begin(); theIter != this->mSpawnList.end(); theIter++, theIndex++) - { - // Found one in range? Add it's index to the list - if(theIter->GetTeamNumber() == AvHTeamNumber(thePlayer->pev->team)) - { - Vector theSpawnOrigin = theIter->GetOrigin(); - float theDistanceToSpawn = VectorDistance(theSpawnOrigin, inOrigin); - if(theDistanceToSpawn <= inMaxDistance) - { - int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(thePlayer->pev->iuser3)); - theSpawnOrigin.z += theOffset; - - if(AvHSUGetIsEnoughRoomForHull(theSpawnOrigin, AvHMUGetHull(false, inPlayer->pev->iuser3), inPlayer->edict())) - { - theSpawnIndexList.push_back(theIndex); - } - } - } - } - - // If index list has more then one entry - int theNumValidSpawns = theSpawnIndexList.size(); - if(theNumValidSpawns > 0) - { - // Pick random index - int theRandomIndex = RANDOM_LONG(0, theNumValidSpawns - 1); - ASSERT(theRandomIndex >= 0); - ASSERT(theRandomIndex < theNumValidSpawns); - ASSERT(this->mSpawnEntity); - - // Copy data into spawn entity - int theSpawnIndex = theSpawnIndexList[theRandomIndex]; - const AvHSpawn& theSpawn = this->mSpawnList[theSpawnIndex]; - this->CopyDataToSpawnEntity(theSpawn); - - // Return it - theSpawnPoint = this->mSpawnEntity; - } - - return theSpawnPoint; -} -bool AvHGamerules::CanPlayerBeacon(CBaseEntity *inPlayer) -{ - bool result=true; - SpawnListType::const_iterator theSpawnIter; - - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - ASSERT(thePlayer); - AvHTeamNumber thePlayerTeamNumber = thePlayer->GetTeam(); - for(theSpawnIter = this->mSpawnList.begin(); theSpawnIter != this->mSpawnList.end(); theSpawnIter++) - { - string theClassName = theSpawnIter->GetClassName(); - if(theClassName == kesTeamStart) - { - if(theSpawnIter->GetTeamNumber() == thePlayerTeamNumber && - VectorDistance(theSpawnIter->GetOrigin(), inPlayer->pev->origin) < BALANCE_VAR(kDistressBeaconRange)) - { - result=false; - } - } - } - return result; -} - -// puzl: 0001073 -#ifdef USE_OLDAUTH -unsigned int gTimeLastUpdatedUplink = 0; -void AvHGamerules::UpdateUplink() -{ - #ifdef AVH_SECURE_PRERELEASE_BUILD - avh_uplink.value = 1; - #endif - - // If authentication is enabled - if(!this->mUpdatedUplink && (avh_uplink.value > 0)) - { - // Only allow it once every day -> 500 servers == num queries per hour = 500*75k = 1,500k per hour -> just over a 1 gig a month - unsigned int theCurrentSystemTime = AvHSUTimeGetTime(); - int theUpdateUplinkInterval = (60*60*1000)*24; - - #ifdef AVH_SECURE_PRERELEASE_BUILD - theUpdateUplinkInterval = (60*60*1000)/30; // every 30 minutes or so while testing - #endif - - #ifdef DEBUG - theUpdateUplinkInterval = 0; - #endif - - if((gTimeLastUpdatedUplink == 0) || (theCurrentSystemTime > (gTimeLastUpdatedUplink + theUpdateUplinkInterval))) - { - // Initialize it - ALERT(at_logged, "Contacting www.natural-selection.org...\n"); - this->InitializeAuthentication(); - //this->DisplayVersioning(); - } - else - { - //ALERT(at_logged, "You must wait longer before uplinking again.\n"); - } - } - - // If it just turned off, clear auth masks - if(this->mUpdatedUplink && !avh_uplink.value) - { - gAuthMaskList.clear(); - ALERT(at_logged, "Authentication disabled.\n"); - } - - this->mUpdatedUplink = avh_uplink.value; -} -#endif - - -edict_t* AvHGamerules::SelectSpawnPoint(CBaseEntity* inPlayer, const string& inSpawnEntityName) const -{ - bool theDone = false; - edict_t* theResult = NULL; - CBaseEntity* theSpot = NULL; - - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - ASSERT(thePlayer); - - // Handle alien spawns differently than others. - AvHTeamNumber theTeamNumber = thePlayer->GetTeam(); - const AvHTeam* theTeam = this->GetTeam(theTeamNumber); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && (inSpawnEntityName == kesTeamStart)) - { - // Get a random active hive - AvHHive* theHive = AvHSUGetRandomActiveHive(theTeamNumber); - if(theHive) - { - theSpot = this->GetRandomHiveSpawnPoint(inPlayer, theHive->pev->origin, theHive->GetMaxSpawnDistance()); - } - - // If we still didn't find one and cheats are on, try a spawn not near a hive - if(!theSpot && this->GetCheatsEnabled()) - { - theSpot = this->SelectRandomSpawn(inPlayer, inSpawnEntityName); - } - } - else - { - theSpot = this->SelectRandomSpawn(inPlayer, inSpawnEntityName); - } - - if(!FNullEnt(theSpot)) - { - int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(thePlayer->pev->iuser3)); - Vector theSpawnOrigin = theSpot->pev->origin; - theSpawnOrigin.z += theOffset; - - if(AvHSUGetIsEnoughRoomForHull(theSpawnOrigin, AvHMUGetHull(false, inPlayer->pev->iuser3), inPlayer->edict())) - { - theResult = theSpot->edict(); - theDone = true; - } - } - - if(!theDone) - { - char theErrorString[256]; - sprintf(theErrorString, "AvHGamerules::SelectSpawnPoint(): all spawns full\n"); - ALERT(at_logged, theErrorString); - theResult = NULL; - } - - return theResult; -} - -void AvHGamerules::UpdateHLTVProxy() -{ - // Check if HLTV proxy is connected to server - bool theHLTVProxyConnected = false; - - // Every so often, send out team for all players (needed for late joiners) - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( i ); - if ( plr && (plr->pev->flags & FL_PROXY)) - { - theHLTVProxyConnected = true; - break; - } - } - - // Update proxy if connected - if(theHLTVProxyConnected) - { - const float kHLTVProxyUpdateTime = 6.0f; - - if((this->mTimeOfLastHLTVProxyUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastHLTVProxyUpdate + kHLTVProxyUpdateTime))) - { - //char theMessage[256]; - //sprintf(theMessage, "AvHGamerules::UpdateHLTVProxy...\n"); - //ALERT(at_console, theMessage); - - // Every so often, send out team for all players (needed for late joiners) - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( i ); - if ( plr && GetGameRules()->IsValidTeam( plr->TeamID() ) ) - { - NetMsgSpec_TeamInfo( plr->entindex(), plr->TeamID() ); - } - } - - // Send messages to HLTV viewers - NetMsgSpec_SetGammaRamp( GetGameRules()->GetMapGamma() ); - - this->mTimeOfLastHLTVProxyUpdate = gpGlobals->time; - } - } -} - -void AvHGamerules::UpdatePlaytesting() -{ - if(this->GetGameStarted() && !this->GetIsCombatMode()) - { - const int theActiveNodesMessageUpdateTime = BALANCE_VAR(kActiveNodesMessageUpdateTime); - if((theActiveNodesMessageUpdateTime > 0) && !this->GetIsTournamentMode()) - { - if((this->mTimeOfLastPlaytestUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastPlaytestUpdate + theActiveNodesMessageUpdateTime))) - { - int theTeamATowers = 0; - int theTeamBTowers = 0; - - FOR_ALL_BASEENTITIES(); - AvHResourceTower* theResourceTower = dynamic_cast(theBaseEntity); - if(theResourceTower && theResourceTower->GetIsActive()) - { - if(theResourceTower->pev->team == this->mTeamA.GetTeamNumber()) - { - theTeamATowers++; - } - else if(theResourceTower->pev->team == this->mTeamB.GetTeamNumber()) - { - theTeamBTowers++; - } - } - END_FOR_ALL_BASEENTITIES(); - - // Count how many active towers each team has, and tell the world - char* theMessage = UTIL_VarArgs( "Active resource nodes: TeamA: %d TeamB: %d\n", theTeamATowers, theTeamBTowers); - UTIL_ClientPrintAll(HUD_PRINTTALK, theMessage); - UTIL_LogPrintf(theMessage); - - this->mTimeOfLastPlaytestUpdate = gpGlobals->time; - } - } - } -} - -edict_t* AvHGamerules::SelectSpawnPoint(CBaseEntity* inPlayer) const -{ - const char* theSpawnName = kesReadyRoomStart; - edict_t* theResult = NULL; - - // Get name of spawn entity from player - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - if(thePlayer) - { - // Set name of spawn point accordingly - theSpawnName = this->GetSpawnEntityName(thePlayer); - } - - theResult = this->SelectSpawnPoint(inPlayer, theSpawnName); - - return theResult; -} - -const char* AvHGamerules::SetDefaultPlayerTeam(CBasePlayer *pPlayer) -{ - // By default, do nothing. We start in the ready room, we don't join a team - return NULL; -} - -CBaseEntity* AvHGamerules::SelectRandomSpawn(CBaseEntity* inPlayer, const string& inSpawnName) const -{ - CBaseEntity* theSpawn = NULL; - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - ASSERT(thePlayer); - AvHTeamNumber thePlayerTeamNumber = thePlayer->GetTeam(); - - // Count how many spawns of this type - int theNumSpawns = 0; - - SpawnListType::const_iterator theSpawnIter; - - for(theSpawnIter = this->mSpawnList.begin(); theSpawnIter != this->mSpawnList.end(); theSpawnIter++) - { - string theClassName = theSpawnIter->GetClassName(); - if(theClassName == inSpawnName) - { - if(theSpawnIter->GetTeamNumber() == thePlayerTeamNumber) - { - theNumSpawns++; - } - } - } - - bool theSuccess = false; - int theSpawnListSize = this->mSpawnList.size(); - if(theSpawnListSize > 0) - { - int theNumTries = 0; - - do - { - int theOffset = RANDOM_LONG(0, theSpawnListSize-1); - - //theSpawnIter = &this->mSpawnList[theCurrentOffset]; - const AvHSpawn& theSpawn = this->mSpawnList[theOffset]; - if(theSpawn.GetClassName() == inSpawnName) - { - if(theSpawn.GetTeamNumber() == thePlayerTeamNumber) - { - // Copy data into current spawn point - ASSERT(this->mSpawnEntity); - - this->CopyDataToSpawnEntity(theSpawn); - - if(IsSpawnPointValid(inPlayer, this->mSpawnEntity)) - { - theSuccess = true; - } - } - } - - // Put a limit to the number of tries, to avoid an infinite loop when mapper didn't create the right type of spawns - } while((theNumTries++ < 100) && !theSuccess); - } - - if(theSuccess) - { - theSpawn = this->mSpawnEntity; - } - else - { - theSpawn = NULL; - } - - return theSpawn; -} - -void AvHGamerules::SetGameStarted(bool inGameStarted) -{ - if(!this->mGameStarted && inGameStarted) - { - FireTargets(ktGameStartedStatus, NULL, NULL, USE_TOGGLE, 0.0f); - AvHNexus::startGame(); - } - this->mGameStarted = inGameStarted; - this->mTimeGameStarted = gpGlobals->time; - - // Choose a random defending team in Combat - if(this->GetIsCombatMode()) - { - int theTeamIndex = 1;// + (rand()%2); - AvHTeamNumber theAttackingTeamNumber = AvHTeamNumber(theTeamIndex); - this->mCombatAttackingTeamNumber = theAttackingTeamNumber; - } - - this->mTeamA.SetGameStarted(inGameStarted); - this->mTeamB.SetGameStarted(inGameStarted); - - // SCRIPTENGINE: OnStart - if (this->GetIsScriptedMode()) - gLUA->OnStarted(); - // :SCRIPTENGINE -} - -void AvHGamerules::SendMOTDToClient( edict_t *client ) -{ - // read from the MOTD.txt file - int length, char_count = 0; - char* pFileList; - char* aFileList = pFileList = (char*)LOAD_FILE_FOR_ME( kMOTDName, &length ); - - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(client)); - ASSERT(thePlayer); - - // send the server name - NetMsg_ServerName( thePlayer->pev, string( CVAR_GET_STRING("hostname") ) ); - - UTIL_ShowMessage(pFileList, thePlayer); - - FREE_FILE( aFileList ); -} - -int AvHGamerules::GetNumberOfPlayers() const -{ - int theNumberOfPlayers = 0; - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - // GetIsRelevant()? - //if(!inPlayingGame || (inEntity->GetPlayMode() == PLAYMODE_PLAYING)) - //{ - if(UTIL_IsValidEntity(theEntity->edict())) - { - theNumberOfPlayers++; - } - //} - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - - return theNumberOfPlayers; -} - -char gLastKnownMapName[256] = ""; - -void AvHGamerules::Think(void) -{ - PROFILE_START() - - float theTime = gpGlobals->time; - float theTimePassed = gpGlobals->frametime; - - if(this->mFirstUpdate) - { - const char* theCStrLevelName = STRING(gpGlobals->mapname); - bool theNewMap = !gLastKnownMapName || !theCStrLevelName || strcmp(theCStrLevelName, gLastKnownMapName); - - PROFILE_START(); - this->PostWorldPrecacheReset(theNewMap); - PROFILE_END(kUpdatePrecacheResetProfile); - - strcpy(gLastKnownMapName, theCStrLevelName); - - // Tell all HUDs to reset - NetMsg_GameStatus_State( kGameStatusReset, this->mMapMode ); - - // SCRIPTENGINE: Execute OnStart - if (this->GetIsScriptedMode()) - gLUA->OnStart(); - - this->mFirstUpdate = false; - } - - // Handle queued network messages - #ifdef USE_NETWORK_METERING - const float kNetworkUpdateInterval = .1f; - if((this->mLastNetworkUpdate == -1) || (theTime > (this->mLastNetworkUpdate + kNetworkUpdateInterval))) - { - PROFILE_START(); - int theBytesPerSecond = 100000;//(int)(avh_networkmeterrate.value); - NetworkMeter::Instance()->SetBufferAmount(theBytesPerSecond); - NetworkMeter::Instance()->ProcessQueuedMessages(); - PROFILE_END(kUpdateNetworkMeterProfile); - } - #endif - - #ifdef PROFILE_BUILD - kProfileRunConfig = (int)(avh_performance.value); - #endif - - PROFILE_START(); - AvHNexus::processResponses(); - this->RecalculateHandicap(); -// puzl: 0001073 -#ifdef USE_OLDAUTH - this->UpdateUplink(); -#endif - this->UpdatePlaytesting(); - this->UpdateHLTVProxy(); - PROFILE_END(kUpdateMisc); - - const float kWorldEntityUpdateInterval = 1.0f; - if((this->mLastWorldEntityUpdate == -1) || (theTime > (this->mLastWorldEntityUpdate + kWorldEntityUpdateInterval))) - { - if(GET_RUN_CODE(1)) - { - // Update world entities - PROFILE_START() - this->UpdateWorldEntities(); - this->mLastWorldEntityUpdate = theTime; - PROFILE_END(kUpdateWorldEntitiesProfile) - } - - // Don't need to update cheats every tick, as they can be expensive - this->UpdateCheats(); - } - - this->mMiniMap.Process(); - - PROFILE_START(); - g_VoiceGameMgr.Update(gpGlobals->frametime); - PROFILE_END(kUpdateVoiceManagerProfile); - - const float kParticleUpdateInterval = 2.0f; - if((this->mLastParticleUpdate == -1) || (theTime > (this->mLastParticleUpdate + kParticleUpdateInterval))) - { - if(GET_RUN_CODE(2)) - { - PROFILE_START(); - AvHParticleSystemManager::Instance()->Update(theTimePassed); - this->mLastParticleUpdate = theTime; - PROFILE_END(kUpdateParticleSystemManager) - } - - #ifdef DEBUG - int theUseCaching = BALANCE_VAR(kDebugServerCaching); - if(theUseCaching) - { - PROFILE_START() - - char theMessage[128]; - int theReturnTotal = kNumReturn0 + kNumReturn1; - float theNumReturnedPercentage = 0.0f; - float theNumCachedPercentage = 0.0f; - float theComputedPercentage = 0.0f; - - if(theReturnTotal > 0) - { - theNumReturnedPercentage = kNumReturn1/((float)theReturnTotal); - theNumCachedPercentage = kNumCached/((float)theReturnTotal); - theComputedPercentage = kNumComputed/((float)theReturnTotal); - } - - kPVSCoherencyTime = BALANCE_VAR(kDebugPVSCoherencyTime); - - sprintf(theMessage, "Percentage entities propagated: %f (%d/%d) (compute percentage: %f) (pvs time: %f)\n", theNumReturnedPercentage, kNumReturn1, theReturnTotal, theComputedPercentage, kPVSCoherencyTime); - UTIL_LogPrintf(theMessage); - - PROFILE_END(kUpdateDebugShowEntityLog) - } - #endif - - kServerFrameRate = 0; - kNumReturn0 = kNumReturn1 = kNumCached = kNumComputed = 0; - } - else - { - int a = 0; - } - - this->UpdateServerCommands(); - this->UpdateGameTime(); - - if(GET_RUN_CODE(4)) - { - if(!this->GetGameStarted()) - { - if(!this->GetIsTournamentMode()) - { - this->UpdateTimeLimit(); - } - - this->UpdateCountdown(theTime); - - PROFILE_START(); - // Try to join any players that are waiting for a spot (bypass cost of reinforcements before game starts) - // Players are put into reinforcement mode if there are no available spawns - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) - { - AvHTeamNumber theTeamNumber = theEntity->GetTeam(); - if(theTeamNumber != TEAM_IND) - { - this->AttemptToJoinTeam(theEntity, theTeamNumber, false); - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - PROFILE_END(kUpdateReinforcementsProfile); - } - else - { - //this->ProcessTeamUpgrades(); - this->UpdateEntitiesUnderAttack(); - - // Has a side won? - if(this->mVictoryTeam != TEAM_IND) - { - // Let players bask in their victory, don't update the world normally - PROFILE_START() - this->UpdateVictory(); - PROFILE_END(kUpdateVictoryProfile) - } - else - { - PROFILE_START() - this->mTeamA.Update(); - PROFILE_END(kTeamOneUpdate) - - PROFILE_START() - this->mTeamB.Update(); - PROFILE_END(kTeamTwoUpdate) - } - - PROFILE_START() - // Don't update every frame for performance reasons - const float kVictoryUpdateInterval = 1.0f; - if((this->mLastVictoryUpdate == -1) || (theTime > (this->mLastVictoryUpdate + kVictoryUpdateInterval))) - { - this->UpdateVictoryStatus(); - this->mLastVictoryUpdate = theTime; - } - PROFILE_END(kUpdateVictoryStatusProfile) - } - } - - gServerTick++; - if((gServerTick % 5) == 0) - { - gServerUpdate = true; - } - else - { - gServerUpdate = false; - } - PROFILE_END(kUpdateGamerulesProfile) - -} - -void AvHGamerules::RegisterServerVariable(const char* inName) -{ - mServerVariableList.push_back(inName); -} - -int AvHGamerules::GetNumServerVariables() const -{ - return mServerVariableList.size(); -} - -const std::string& AvHGamerules::GetServerVariable(int i) const -{ - return mServerVariableList[i]; -} - -bool AvHGamerules::GetIsEntityUnderAttack(int inEntityIndex) const -{ - bool theEntityIsUnderAttack = false; - - // If entity is in list, it's being attacked - for(EntityUnderAttackListType::const_iterator theIter = this->mEntitiesUnderAttack.begin(); theIter != this->mEntitiesUnderAttack.end(); theIter++) - { - if(inEntityIndex == theIter->first) - { - theEntityIsUnderAttack = true; - break; - } - } - - return theEntityIsUnderAttack; -} - -void AvHGamerules::TriggerAlert(AvHTeamNumber inTeamNumber, AvHAlertType inAlertType, int inEntIndex, AvHMessageID inMessageID) -{ - AvHTeam* theTeam = this->GetTeam(inTeamNumber); - if(theTeam) - { - // Don't play audio alerts too often. This also allows neat tactics where players can time strikes to prevent the other team from instant notification of an alert, ala RTS - float theTimeOfLastAlert = -1; - AvHAlertType theLastAlertType = ALERT_NONE; - - const AvHAlert* theLastAlert = theTeam->GetLastAudioAlert(); - if(theLastAlert) - { - theTimeOfLastAlert = theLastAlert->GetTime(); - theLastAlertType = theLastAlert->GetAlertType(); - } - - // Play alerts when enough time has passed - bool theIsRepeatAlert = (inAlertType == theLastAlertType); - const float kBaseAlertInterval = theTeam->GetAudioAlertInterval(); - float theAlertInterval = theIsRepeatAlert ? 2.0f*kBaseAlertInterval : kBaseAlertInterval; - - bool theAlertIntervalTimePassed = (gpGlobals->time - theTimeOfLastAlert > theAlertInterval); - bool thePlayAlertSound = ((theTimeOfLastAlert == -1) || theAlertIntervalTimePassed); - - // Always play urgent alerts, but only when they're new (so you don't get 'hive under attack' twice in quick succession) - if(AvHSUGetIsUrgentAlert(inAlertType) && (!AvHSUGetIsOftRepeatedAlert(inAlertType) && !theIsRepeatAlert)) - { - thePlayAlertSound = true; - } - - // Add it, remembering if we triggered sound for it - AvHAlert theNewAlert(inAlertType, gpGlobals->time, inEntIndex); - theNewAlert.SetPlayedAudio(thePlayAlertSound); - theTeam->AddAlert(theNewAlert, inMessageID); - - if(thePlayAlertSound) - { - AvHHUDSound theSound = HUD_SOUND_INVALID; - CBaseEntity* theAlertEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inEntIndex)); - - // If team type is marines, play sound for commander - AvHClassType theTeamType = theTeam->GetTeamType(); - if(theTeamType == AVH_CLASS_TYPE_MARINE) - { - // Get commander for team, if any - int theCommanderIndex = theTeam->GetCommander(); - - if(this->GetIsTesting()) - { - theCommanderIndex = 1; - } - - if(theCommanderIndex != -1) - { - CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theCommanderIndex)); - AvHPlayer* theCommander = dynamic_cast(theBaseEntity); - if(theCommander /*&& thePlayAudioAlert*/) - { - if(inAlertType == ALERT_RESEARCH_COMPLETE) - { - theSound = HUD_SOUND_MARINE_RESEARCHCOMPLETE; - } - else if(inAlertType == ALERT_UPGRADE_COMPLETE) - { - theSound = HUD_SOUND_MARINE_UPGRADE_COMPLETE; - } - else if(inAlertType == ALERT_LOW_RESOURCES) - { - theSound = HUD_SOUND_MARINE_RESOURCES_LOW; - } - // Special sound for the CC, always play it no matter where the commander is - else if(inAlertType == ALERT_UNDER_ATTACK) - { - if(dynamic_cast(theAlertEntity)) - { - theSound = HUD_SOUND_MARINE_CCUNDERATTACK; - } - else - { - theSound = HUD_SOUND_MARINE_BASE_UNDER_ATTACK; - } - } - else if(inAlertType == ALERT_ORDER_COMPLETE) - { - theSound = HUD_SOUND_ORDER_COMPLETE; - } - // Check for positional alerts (only send them if they are out of commander's sight) - else - { - if(theAlertEntity) - { - vec3_t theDistanceToAlert; - VectorSubtract(theCommander->pev->origin, theAlertEntity->pev->origin, theDistanceToAlert); - float theXYDistanceToAlert = theDistanceToAlert.Length2D(); - - if(theXYDistanceToAlert > this->GetMapExtents().GetTopDownCullDistance()) - { - if(inAlertType == ALERT_PLAYER_ENGAGE) - { - theSound = HUD_SOUND_MARINE_SOLDIER_UNDER_ATTACK; - } - else if(inAlertType == ALERT_UNDER_ATTACK) - { - theSound = HUD_SOUND_MARINE_BASE_UNDER_ATTACK; - } - else if(inAlertType == ALERT_SOLDIER_NEEDS_AMMO) - { - theSound = HUD_SOUND_MARINE_NEEDS_AMMO; - } - else if(inAlertType == ALERT_SOLDIER_NEEDS_HEALTH) - { - theSound = HUD_SOUND_MARINE_NEEDS_HEALTH; - } - else if(inAlertType == ALERT_ORDER_NEEDED) - { - theSound = HUD_SOUND_MARINE_NEEDS_ORDER; - } - else if(inAlertType == ALERT_PLAYER_DIED) - { - theSound = HUD_SOUND_MARINE_SOLDIER_LOST; - } - else if(inAlertType == ALERT_SENTRY_FIRING) - { - theSound = HUD_SOUND_MARINE_SENTRYFIRING; - } - else if(inAlertType == ALERT_SENTRY_DAMAGED) - { - theSound = HUD_SOUND_MARINE_SENTRYDAMAGED; - } - } - } - } - - if(theSound != HUD_SOUND_INVALID) - { - if (theAlertEntity != NULL) - { - // Added by mmcguire. - theCommander->PlayHUDSound(theSound, theAlertEntity->pev->origin[0], theAlertEntity->pev->origin[1]); - } - else - { - theCommander->PlayHUDSound(theSound); - } - } - } - } - else - { - // Play "command station under attack" sound for all players on team - if(inAlertType == ALERT_UNDER_ATTACK) - { - if(dynamic_cast(theAlertEntity)) - { - // For all players on this team, play the sound - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if((theEntity->pev->team == inTeamNumber) || this->GetIsTesting() || (theEntity->GetIsSpectatingTeam(inTeamNumber))) - { - theEntity->PlayHUDSound(HUD_SOUND_MARINE_CCUNDERATTACK); - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - } - } - } - } - // Else it's aliens, play hive under attack sound - else if(theTeamType == AVH_CLASS_TYPE_ALIEN) - { - // When adding alien alerts, don't always play audio notification, it gets annoying - if(thePlayAlertSound) - { - // Tell everyone where attack is - if(inAlertType == ALERT_PLAYER_ENGAGE) - { - theSound = HUD_SOUND_ALIEN_LIFEFORM_ATTACK; - } - else if(inAlertType == ALERT_UNDER_ATTACK) - { - // If it's a resource tower - if(theAlertEntity) - { - switch(theAlertEntity->pev->iuser3) - { - case AVH_USER3_ALIENRESTOWER: - theSound = HUD_SOUND_ALIEN_RESOURCES_ATTACK; - break; - case AVH_USER3_HIVE: - theSound = HUD_SOUND_ALIEN_HIVE_ATTACK; - break; - default: - theSound = HUD_SOUND_ALIEN_STRUCTURE_ATTACK; - break; - } - } - } - else if(inAlertType == ALERT_HIVE_COMPLETE) - { - theSound = HUD_SOUND_ALIEN_HIVE_COMPLETE; - } - else if(inAlertType == ALERT_HIVE_DYING) - { - theSound = HUD_SOUND_ALIEN_HIVE_DYING; - } - else if(inAlertType == ALERT_LOW_RESOURCES) - { - theSound = HUD_SOUND_ALIEN_RESOURCES_LOW; - } - else if(inAlertType == ALERT_NEW_TRAIT) - { - theSound = HUD_SOUND_ALIEN_NEW_TRAIT; - } - - if(theSound != HUD_SOUND_INVALID) - { - int a = 0; - - // For all players on this team, play the sound - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if((theEntity->pev->team == inTeamNumber) || this->GetIsTesting() || (theEntity->GetIsSpectatingTeam(inTeamNumber))) - { - theEntity->PlayHUDSound(theSound); - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - } - } - } - else - { - ASSERT(false); - } - } - - // Add entity to our list of entities that are under attack - if(((inAlertType == ALERT_UNDER_ATTACK) || (inAlertType == ALERT_PLAYER_ENGAGE) || (inAlertType == ALERT_HIVE_DYING)) && (inEntIndex > 0)) - { - // This will update current time longer if continually attacked - const float kUnderAttackDuration = 5.0f; - this->mEntitiesUnderAttack[inEntIndex] = gpGlobals->time + kUnderAttackDuration; - } - } -} - -bool AvHGamerules::GetIsCheatEnabled(const string& inCheatName) const -{ - bool theCheatIsEnabled = false; - - bool theAllowCheats = this->GetCheatsEnabled(); - - #ifdef AVH_PLAYTEST_BUILD - theAllowCheats = true; - #endif - - if(theAllowCheats) - { - StringList::const_iterator theIter = std::find(this->mCheats.begin(), this->mCheats.end(), inCheatName); - if(theIter != this->mCheats.end()) - { - theCheatIsEnabled = true; - } - } - - return theCheatIsEnabled; -} - -void AvHGamerules::SetCheatEnabled(const string& inCheatName, bool inEnabledState) -{ - if(this->GetCheatsEnabled()) - { - StringList::iterator theIter = std::find(this->mCheats.begin(), this->mCheats.end(), inCheatName); - if(theIter == this->mCheats.end()) - { - if(inEnabledState) - { - this->mCheats.push_back(inCheatName); - } - } - else - { - if(!inEnabledState) - { - this->mCheats.erase(theIter); - } - } - } -} - -void AvHGamerules::UpdateCheats() -{ - if(this->GetCheatsEnabled()) - { - // If bigdig is enabled - if(this->GetIsCheatEnabled(kcBigDig)) - { - // Run through all buildables, set them to complete - //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::UpdateCheats\n"); - - FOR_ALL_BASEENTITIES(); - AvHBuildable* theBuildable = dynamic_cast(theBaseEntity); - if(theBuildable && !theBuildable->GetHasBeenBuilt() && !GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_RECYCLING) && (theBaseEntity->pev->team != TEAM_IND)) - { - theBuildable->SetConstructionComplete(true); - } -// AvHDeployedTurret* theTurret = dynamic_cast(theBaseEntity); -// if(theTurret && !theTurret->GetIsBuilt()) -// { -// theTurret->SetConstructionComplete(); -// } - END_FOR_ALL_BASEENTITIES(); - } - } -} - -void AvHGamerules::UpdateCountdown(float inTime) -{ - if (this->GetIsScriptedMode()) - { - // this->SetGameStarted(true); - // TODO: SCRIPTENGINE START - return; - } - - const float kTimeWontStartInterval = 8.0f; - int kSecondsToCountdown = 5; - - #ifdef DEBUG - kSecondsToCountdown = 1; - #endif - - // Time to start counting down? - if(this->ReadyToStartCountdown()) - { - if(!this->mStartedCountdown) - { - //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - // theEntity->SendMessageNextThink(kGameStarting); - //END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - // If there is at least one person on one team, start counting - this->mTimeCountDownStarted = inTime; - this->mStartedCountdown = true; - - // Reset server and respawn everyone - this->ResetGame(true); - - this->mSavedTimeCountDownStarted = this->mTimeCountDownStarted; - } - } - else - { - if(!this->GetIsTrainingMode()) - { - if((this->mTimeLastWontStartMessageSent == 0) || (inTime > (this->mTimeLastWontStartMessageSent + kTimeWontStartInterval))) - { - const char* theMessage = kGameWontStart; - if(this->GetIsTournamentMode()) - { - theMessage = kGameWontStartUntilReady; - } - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - theEntity->SendMessageOnce(theMessage, TOOLTIP); - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - this->mTimeLastWontStartMessageSent = inTime; - } - } - } - - if(this->mStartedCountdown) - { - if(inTime - this->mTimeCountDownStarted > (avh_countdowntime.value*60 - kSecondsToCountdown) && !this->mSentCountdownMessage) - { - // Send update to everyone - NetMsg_UpdateCountdown( kSecondsToCountdown ); - - this->mTimeSentCountdown = gpGlobals->time; - this->mSentCountdownMessage = true; - } - - if(this->mSentCountdownMessage) - { - if(gpGlobals->time - this->mTimeSentCountdown >= kSecondsToCountdown) - { - this->SetGameStarted(true); - } - } - } -} - -void AvHGamerules::UpdateEntitiesUnderAttack() -{ - // Expire entities under attack - for(EntityUnderAttackListType::iterator theIter = this->mEntitiesUnderAttack.begin(); theIter != this->mEntitiesUnderAttack.end(); /* no increment*/) - { - if(gpGlobals->time >= theIter->second) - { - EntityUnderAttackListType::iterator theTempIter = theIter; - theTempIter++; - this->mEntitiesUnderAttack.erase(theIter); - theIter = theTempIter; - } - else - { - theIter++; - } - } -} - -void AvHGamerules::SendGameTimeUpdate(bool inReliable) -{ - // Send down game time to clients periodically - NetMsg_GameStatus_Time( kGameStatusGameTime, this->mMapMode, this->GetGameTime(), this->GetTimeLimit(), this->mCombatAttackingTeamNumber, inReliable ); - - this->mTimeOfLastGameTimeUpdate = gpGlobals->time; -} - -void AvHGamerules::UpdateGameTime() -{ - // Send periodic game time updates to everyone (needed for HLTV spectators) - if(this->GetGameStarted() && (this->GetVictoryTeam() == TEAM_IND)) - { - // Only reason why this isn't longer is to make sure late-joiners see time on scoreboard after reasonable period, without causing needless bandwidth - const float kGameTimeUpdateInterval = 8; - if((this->mTimeOfLastGameTimeUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastGameTimeUpdate + kGameTimeUpdateInterval))) - { - this->SendGameTimeUpdate(); - } - } -} - - -void AvHGamerules::UpdateScripts() -{ - const float kUpdateScriptsInterval = 1.0f; - - // If server scripts are enabled - if(CVAR_GET_FLOAT(kvServerScripts)) - { -// const char* theCStrLevelName = STRING(gpGlobals->mapname); -// if(theCStrLevelName && !FStrEq(theCStrLevelName, "")) -// { -// string theLevelName = theCStrLevelName; -// string theLevelScript = string(kModDirectory) + string("/") + kScriptsDirectory + string("/") + theLevelName + string(".bin"); -// -// if((this->mTimeUpdatedScripts == -1) || (gpGlobals->time > this->mTimeUpdatedScripts + kUpdateScriptsInterval)) -// { -// // Execute the script -// AvHLUADoFile(theLevelScript.c_str()); -// } -// -// } - AvHScriptManager::Instance()->Update(gpGlobals->time); - } -} - -void AvHGamerules::UpdateServerCommands() -{ - //float theAirAccelerate = CVAR_GET_FLOAT("sv_airaccelerate"); - //float theAirMove = CVAR_GET_FLOAT("sv_airmove"); - - // TODO: Disguises these strings somehow to prevent hacking? - SERVER_COMMAND("sv_airaccelerate 10\n"); - SERVER_COMMAND("sv_airmove 1\n"); -} - -void AvHGamerules::UpdateTimeLimit() -{ - // Only update time limit after world has been reset. Give some time to make sure all clients get the game status message. - #ifdef DEBUG - const float kIntermissionTime = 2; - #else - const float kIntermissionTime = 5; - #endif - - if(gpGlobals->time > (this->mTimeWorldReset + kIntermissionTime)) - { - float theTimeLimit = timelimit.value * 60; - - if((theTimeLimit > 0) && (gpGlobals->time >= (theTimeLimit + this->mLastMapChange))) - { - this->ChangeLevel(); - } - } -} - -void AvHGamerules::UpdateVictory() -{ - if((gpGlobals->time - this->mVictoryTime) > (kVictoryIntermission + kMaxPlayers/kResetPlayersPerSecond)) - { - this->ResetGame(); - } -} - -void AvHGamerules::UpdateVictoryStatus(void) -{ - bool theCheckVictoryWithCheats = !this->GetCheatsEnabled() || this->GetIsCheatEnabled(kcEndGame1) || this->GetIsCheatEnabled(kcEndGame2); - - // SCRIPTENGINE VICTORY - if (this->GetIsScriptedMode()) - { - AvHObjectiveState teamAstate, teamBstate; - teamAstate = this->mTeamA.GetObjectiveManager()->GetObjectivesState(); - teamBstate = this->mTeamB.GetObjectiveManager()->GetObjectivesState(); - if (teamAstate != OBJECTIVE_INDETERMINED || teamBstate != OBJECTIVE_INDETERMINED) - { - // one team is victorious - this->mVictoryTime = gpGlobals->time; - if (teamAstate == teamBstate) - { - this->mVictoryTeam = TEAM_SPECT; - this->mVictoryDraw = true; - } - else if (teamAstate == OBJECTIVE_COMPLETED || teamBstate == OBJECTIVE_FAILED) - this->mVictoryTeam = this->mTeamA.GetTeamNumber(); - else if (teamAstate == OBJECTIVE_FAILED || teamBstate == OBJECTIVE_COMPLETED) - this->mVictoryTeam = this->mTeamB.GetTeamNumber(); - } - else - { - // Execute LUA callback OnVictoryCheck - AvHTeamNumber vicTeam = gLUA->OnVictoryCheck(); - if (vicTeam != TEAM_IND) - { - this->mVictoryTime = gpGlobals->time; - this->mVictoryTeam = vicTeam; - } - } - // Execute LUA callback OnVictory - if (this->mVictoryTeam != TEAM_IND) - gLUA->OnVictory(this->mVictoryTeam); - - } - else - if((this->mVictoryTeam == TEAM_IND) && this->mGameStarted && theCheckVictoryWithCheats && !this->GetIsTrainingMode()) - { - char* theVictoryMessage = NULL; - bool theTeamALost = this->mTeamA.GetHasTeamLost(); - bool theTeamBLost = this->mTeamB.GetHasTeamLost(); - bool theDrawGame = false; - - int theTeamAPlayers = this->mTeamA.GetPlayerCount(); - int theTeamBPlayers = this->mTeamB.GetPlayerCount(); - - bool theTimeLimitHit = false; - int theTimeLimitSeconds = this->GetTimeLimit(); - - if(this->GetIsCombatMode()) - { - if(this->GetIsIronMan()) - { - theTimeLimitSeconds = CVAR_GET_FLOAT(kvIronManTime)*60; - } - } - - if((theTimeLimitSeconds > 0) && ((gpGlobals->time - GetGameRules()->GetTimeGameStarted()) > theTimeLimitSeconds)) - { - theTimeLimitHit = true; - } - - if(this->GetIsCheatEnabled(kcEndGame1)) - { - theTeamBLost = true; - } - else if(this->GetIsCheatEnabled(kcEndGame2)) - { - theTeamALost = true; - } - else if(this->GetIsCombatMode()) - { - // Alien victory if time limit is hit - if(theTimeLimitHit) - { - if(this->mCombatAttackingTeamNumber == this->mTeamA.GetTeamNumber()) - { - theTeamALost = true; - } - else if(this->mCombatAttackingTeamNumber == this->mTeamB.GetTeamNumber()) - { - theTeamBLost = true; - } - } - } - else if(this->GetIsTournamentMode() && !this->GetIsCombatMode()) - { - // If timelimit has elapsed in tourny mode, the victor is the team with the most resources - if(theTimeLimitHit) - { - // Don't count fractional resources. If it's that close, it was a tie. - int theTeamAResources = this->mTeamA.GetTotalResourcesGathered(); - int theTeamBResources = this->mTeamB.GetTotalResourcesGathered(); - if(theTeamAResources > theTeamBResources) - { - theTeamBLost = true; - } - else if(theTeamBResources > theTeamAResources) - { - theTeamALost = true; - } - else - { - // It's a draw - theDrawGame = true; - } - } - } - else if(this->GetIsIronMan()) - { - if(theTimeLimitHit) - { - // Aliens win. If both teams are the same, it's a draw. - if(this->mTeamA.GetTeamType() == this->mTeamB.GetTeamType()) - { - theDrawGame = true; - } - else - { - if(this->mTeamA.GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - theTeamBLost = true; - } - else if(this->mTeamB.GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - theTeamALost = true; - } - } - } - else if(!this->mTeamA.GetHasAtLeastOneActivePlayer()) - { - theTeamALost = true; - } - else if(!this->mTeamB.GetHasAtLeastOneActivePlayer()) - { - theTeamBLost = true; - } - } - else if(!this->GetIsTournamentMode()) - { - // Check for automatic concession, if teams are suddenly unbalanced (generally because a team realizes it's futile and leaves) - const int kAutoConcedeNumPlayers = (int)(avh_autoconcede.value); - if(kAutoConcedeNumPlayers > 0) - { - if((theTeamAPlayers - theTeamBPlayers) >= kAutoConcedeNumPlayers) - { - theTeamBLost = true; - } - else if((theTeamBPlayers - theTeamAPlayers) >= kAutoConcedeNumPlayers) - { - theTeamALost = true; - } - } - } - - if((theTeamALost && theTeamBLost) || theDrawGame) - { - // Draw! - this->mVictoryTime = gpGlobals->time; - this->mVictoryTeam = TEAM_SPECT; - this->mVictoryDraw = true; - theVictoryMessage = kGameDraw; - theDrawGame = true; - } - - if(theTeamALost) - { - // If there is a victory, set mVictoryTime and mVictoryTeam - this->mVictoryTime = gpGlobals->time; - this->mVictoryTeam = this->mTeamB.GetTeamNumber(); - theVictoryMessage = kTeamTwoWon; - } - else if(theTeamBLost) - { - // If there is a victory, set mVictoryTime and mVictoryTeam - this->mVictoryTime = gpGlobals->time; - this->mVictoryTeam = this->mTeamA.GetTeamNumber(); - theVictoryMessage = kTeamOneWon; - } - - //if(!theAtLeastOneTeamOneMemberLeft || !theAtLeastOneTeamTwoMemberLeft) - if(this->mVictoryTeam != TEAM_IND) - { - this->TallyVictoryStats(); - AvHNexus::finishGame(); - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - AvHTeam* theTeam = theEntity->GetTeamPointer(); - AvHHUDSound theHUDSound = HUD_SOUND_YOU_LOSE; - if((theTeam && (theTeam->GetTeamNumber() == this->mVictoryTeam)) || (theEntity->GetPlayMode() == PLAYMODE_OBSERVER) || (theEntity->pev->team == TEAM_IND) || theDrawGame) - { - theHUDSound = HUD_SOUND_YOU_WIN; - } - else if(!this->GetIsCombatMode()) - { - // Fade losers out to black - Vector theFadeColor; - theFadeColor.x = 0; - theFadeColor.y = 0; - theFadeColor.z = 0; - UTIL_ScreenFade(theEntity, theFadeColor, 1.0f, 0.0f, 255, FFADE_OUT | FFADE_STAYOUT); - } - theEntity->PlayHUDSound(theHUDSound); - theEntity->SendMessage(theVictoryMessage, CENTER); - - // Final game time update to all clients have same winning time - this->SendGameTimeUpdate(true); - - // Send final score to everyone if needed - this->mTeamA.SendResourcesGatheredScore(false); - this->mTeamB.SendResourcesGatheredScore(false); - - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - // Tell everyone that the game ended - NetMsg_GameStatus_State( kGameStatusEnded, this->mMapMode ); - } - } -} - -int AvHGamerules::GetBaseHealthForMessageID(AvHMessageID inMessageID) const -{ - int health = 100; - - switch(inMessageID) - { - case BUILD_INFANTRYPORTAL: health = BALANCE_VAR(kInfantryPortalHealth); break; - case BUILD_RESOURCES: health = BALANCE_VAR(kResourceTowerHealth); break; - case BUILD_TURRET_FACTORY: health = BALANCE_VAR(kTurretFactoryHealth); break; - case TURRET_FACTORY_UPGRADE: health = BALANCE_VAR(kTurretFactoryHealth); break; - case BUILD_ARMSLAB: health = BALANCE_VAR(kArmsLabHealth); break; - case BUILD_PROTOTYPE_LAB: health = BALANCE_VAR(kArmsLabHealth); break; - case BUILD_ARMORY: health = BALANCE_VAR(kArmoryHealth); break; - case ARMORY_UPGRADE: health = BALANCE_VAR(kAdvArmoryHealth); break; - case BUILD_OBSERVATORY: health = BALANCE_VAR(kObservatoryHealth); break; - case BUILD_PHASEGATE: health = BALANCE_VAR(kPhaseGateHealth); break; - case BUILD_TURRET: health = BALANCE_VAR(kSentryHealth); break; - case BUILD_SIEGE: health = BALANCE_VAR(kSiegeHealth); break; - case BUILD_COMMANDSTATION: health = BALANCE_VAR(kCommandStationHealth); break; - case ALIEN_BUILD_RESOURCES: health = BALANCE_VAR(kAlienResourceTowerHealth); break; - case ALIEN_BUILD_OFFENSE_CHAMBER: health = BALANCE_VAR(kOffenseChamberHealth); break; - case ALIEN_BUILD_DEFENSE_CHAMBER: health = BALANCE_VAR(kDefenseChamberHealth); break; - case ALIEN_BUILD_SENSORY_CHAMBER: health = BALANCE_VAR(kSensoryChamberHealth); break; - case ALIEN_BUILD_MOVEMENT_CHAMBER: health = BALANCE_VAR(kMovementChamberHealth); break; - case ALIEN_BUILD_HIVE: health = BALANCE_VAR(kHiveHealth); break; - } - return health; -} - - -int AvHGamerules::GetBuildTimeForMessageID(AvHMessageID inMessageID) const -{ - float time = 0.0f; - const float CO_Scalar = this->GetIsCombatMode() ? BALANCE_VAR(kCombatModeTimeScalar) : 1.0f; - const float CO_GScalar = this->GetIsCombatMode() ? BALANCE_VAR(kCombatModeGestationTimeScalar) : 1.0f; - - switch(inMessageID) - { - // Marine Research - case RESEARCH_ELECTRICAL: time = BALANCE_VAR(kElectricalUpgradeResearchTime); break; - case RESEARCH_ARMOR_ONE: time = BALANCE_VAR(kArmorOneResearchTime); break; - case RESEARCH_ARMOR_TWO: time = BALANCE_VAR(kArmorTwoResearchTime); break; - case RESEARCH_ARMOR_THREE: time = BALANCE_VAR(kArmorThreeResearchTime); break; - case RESEARCH_WEAPONS_ONE: time = BALANCE_VAR(kWeaponsOneResearchTime); break; - case RESEARCH_WEAPONS_TWO: time = BALANCE_VAR(kWeaponsTwoResearchTime); break; - case RESEARCH_WEAPONS_THREE: time = BALANCE_VAR(kWeaponsThreeResearchTime); break; - case RESEARCH_CATALYSTS: time = BALANCE_VAR(kCatalystResearchTime); break; - case RESEARCH_GRENADES: time = BALANCE_VAR(kGrenadesResearchTime); break; - case RESEARCH_JETPACKS: time = BALANCE_VAR(kJetpacksResearchTime); break; - case RESEARCH_HEAVYARMOR: time = BALANCE_VAR(kHeavyArmorResearchTime); break; - case RESEARCH_DISTRESSBEACON: time = BALANCE_VAR(kDistressBeaconTime); break; - case RESEARCH_HEALTH: time = BALANCE_VAR(kHealthResearchTime); break; - case RESEARCH_MOTIONTRACK: time = BALANCE_VAR(kMotionTrackingResearchTime); break; - case RESEARCH_PHASETECH: time = BALANCE_VAR(kPhaseTechResearchTime); break; - - // Marine Structures - case BUILD_INFANTRYPORTAL: time = BALANCE_VAR(kInfantryPortalBuildTime); break; - case BUILD_RESOURCES: time = BALANCE_VAR(kResourceTowerBuildTime); break; - case BUILD_TURRET_FACTORY: time = BALANCE_VAR(kTurretFactoryBuildTime); break; - case BUILD_ARMSLAB: time = BALANCE_VAR(kArmsLabBuildTime); break; - case BUILD_PROTOTYPE_LAB: time = BALANCE_VAR(kPrototypeLabBuildTime); break; - case BUILD_ARMORY: time = BALANCE_VAR(kArmoryBuildTime); break; - case ARMORY_UPGRADE: time = BALANCE_VAR(kArmoryUpgradeTime); break; - case BUILD_OBSERVATORY: time = BALANCE_VAR(kObservatoryBuildTime); break; - case BUILD_PHASEGATE: time = BALANCE_VAR(kPhaseGateBuildTime); break; - case BUILD_TURRET: time = BALANCE_VAR(kSentryBuildTime); break; - case BUILD_SIEGE: time = BALANCE_VAR(kSiegeBuildTime); break; - case BUILD_COMMANDSTATION: time = BALANCE_VAR(kCommandStationBuildTime); break; - case TURRET_FACTORY_UPGRADE: time = BALANCE_VAR(kTurretFactoryUpgradeTime); break; - - // Scan Duration - case BUILD_SCAN: time = BALANCE_VAR(kScanDuration); break; - - // Alien Structures - case ALIEN_BUILD_RESOURCES: time = BALANCE_VAR(kAlienResourceTowerBuildTime); break; - case ALIEN_BUILD_OFFENSE_CHAMBER: time = BALANCE_VAR(kOffenseChamberBuildTime); break; - case ALIEN_BUILD_DEFENSE_CHAMBER: time = BALANCE_VAR(kDefenseChamberBuildTime); break; - case ALIEN_BUILD_SENSORY_CHAMBER: time = BALANCE_VAR(kSensoryChamberBuildTime); break; - case ALIEN_BUILD_MOVEMENT_CHAMBER: time = BALANCE_VAR(kMovementChamberBuildTime); break; - case ALIEN_BUILD_HIVE: time = BALANCE_VAR(kHiveBuildTime); break; - - // Alien Evolutions - case ALIEN_EVOLUTION_ONE: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_EVOLUTION_TWO: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_EVOLUTION_THREE: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_EVOLUTION_SEVEN: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_EVOLUTION_EIGHT: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_EVOLUTION_NINE: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_EVOLUTION_TEN: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_EVOLUTION_ELEVEN: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_EVOLUTION_TWELVE: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_HIVE_TWO_UNLOCK: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - case ALIEN_HIVE_THREE_UNLOCK: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; - - // Alien Lifeforms - case ALIEN_LIFEFORM_ONE: time = BALANCE_VAR(kSkulkGestateTime)*CO_GScalar; break; - case ALIEN_LIFEFORM_TWO: time = BALANCE_VAR(kGorgeGestateTime)*CO_GScalar; break; - case ALIEN_LIFEFORM_THREE: time = BALANCE_VAR(kLerkGestateTime)*CO_GScalar; break; - case ALIEN_LIFEFORM_FOUR: time = BALANCE_VAR(kFadeGestateTime)*CO_GScalar; break; - case ALIEN_LIFEFORM_FIVE: time = BALANCE_VAR(kOnosGestateTime)*CO_GScalar; break; - } - - if( time > 0 ) - { - time = max( time, 1.0f ); //for cases where combat scalars would result in fractional seconds - } - - if(this->GetCheatsEnabled() && !this->GetIsCheatEnabled(kcSlowResearch)) - { - time = min( time, 2.0f ); - } - - return floor( time ); -} - -int AvHGamerules::GetCostForMessageID(AvHMessageID inMessageID) const -{ - // This is point cost or energy cost in NS, or number of levels in Combat - int cost = 0; - - if(this->GetIsCombatMode()) - { - switch(inMessageID) - { - case ALIEN_LIFEFORM_TWO: - case ALIEN_EVOLUTION_ONE: - case ALIEN_EVOLUTION_TWO: - case ALIEN_EVOLUTION_THREE: - case ALIEN_EVOLUTION_SEVEN: - case ALIEN_EVOLUTION_EIGHT: - case ALIEN_EVOLUTION_NINE: - case ALIEN_EVOLUTION_TEN: - case ALIEN_EVOLUTION_TWELVE: - case BUILD_RESUPPLY: - case RESEARCH_ARMOR_ONE: - case RESEARCH_ARMOR_TWO: - case RESEARCH_ARMOR_THREE: - case RESEARCH_WEAPONS_ONE: - case RESEARCH_WEAPONS_TWO: - case RESEARCH_WEAPONS_THREE: - case BUILD_CAT: - case RESEARCH_MOTIONTRACK: - case RESEARCH_GRENADES: - case BUILD_SCAN: - case BUILD_MINES: - case BUILD_WELDER: - case BUILD_SHOTGUN: - case BUILD_HMG: - case BUILD_GRENADE_GUN: - case ALIEN_HIVE_TWO_UNLOCK: - cost = 1; - break; - - case ALIEN_LIFEFORM_THREE: - case BUILD_HEAVY: - case BUILD_JETPACK: - case ALIEN_HIVE_THREE_UNLOCK: - case ALIEN_EVOLUTION_ELEVEN: - cost = 2; - break; - - case ALIEN_LIFEFORM_FOUR: - cost = 3; - break; - - case ALIEN_LIFEFORM_FIVE: - cost = 4; - break; - } - } - else - { - switch(inMessageID) - { - // Marine Research - case RESEARCH_ELECTRICAL: cost = BALANCE_VAR(kElectricalUpgradeResearchCost); break; - case RESEARCH_ARMOR_ONE: cost = BALANCE_VAR(kArmorOneResearchCost); break; - case RESEARCH_ARMOR_TWO: cost = BALANCE_VAR(kArmorTwoResearchCost); break; - case RESEARCH_ARMOR_THREE: cost = BALANCE_VAR(kArmorThreeResearchCost); break; - case RESEARCH_WEAPONS_ONE: cost = BALANCE_VAR(kWeaponsOneResearchCost); break; - case RESEARCH_WEAPONS_TWO: cost = BALANCE_VAR(kWeaponsTwoResearchCost); break; - case RESEARCH_WEAPONS_THREE: cost = BALANCE_VAR(kWeaponsThreeResearchCost); break; - case RESEARCH_CATALYSTS: cost = BALANCE_VAR(kCatalystResearchCost); break; - case RESEARCH_GRENADES: cost = BALANCE_VAR(kGrenadesResearchCost); break; - case TURRET_FACTORY_UPGRADE: cost = BALANCE_VAR(kTurretFactoryUpgradeCost); break; - case RESEARCH_JETPACKS: cost = BALANCE_VAR(kJetpacksResearchCost); break; - case RESEARCH_HEAVYARMOR: cost = BALANCE_VAR(kHeavyArmorResearchCost); break; - case RESEARCH_HEALTH: cost = BALANCE_VAR(kHealthResearchCost); break; - case RESEARCH_MOTIONTRACK: cost = BALANCE_VAR(kMotionTrackingResearchCost); break; - case RESEARCH_PHASETECH: cost = BALANCE_VAR(kPhaseTechResearchCost); break; - case RESEARCH_DISTRESSBEACON: cost = BALANCE_VAR(kDistressBeaconCost); break; - - // Marine Structures - case BUILD_HEAVY: cost = BALANCE_VAR(kHeavyArmorCost); break; - case BUILD_JETPACK: cost = BALANCE_VAR(kJetpackCost); break; - case BUILD_INFANTRYPORTAL: cost = BALANCE_VAR(kInfantryPortalCost); break; - case BUILD_RESOURCES: cost = BALANCE_VAR(kResourceTowerCost); break; - case BUILD_TURRET_FACTORY: cost = BALANCE_VAR(kTurretFactoryCost); break; - case BUILD_ARMSLAB: cost = BALANCE_VAR(kArmsLabCost); break; - case BUILD_PROTOTYPE_LAB: cost = BALANCE_VAR(kPrototypeLabCost); break; - case BUILD_ARMORY: cost = BALANCE_VAR(kArmoryCost); break; - case ARMORY_UPGRADE: cost = BALANCE_VAR(kArmoryUpgradeCost); break; - case BUILD_OBSERVATORY: cost = BALANCE_VAR(kObservatoryCost); break; - case BUILD_PHASEGATE: cost = BALANCE_VAR(kPhaseGateCost); break; - case BUILD_TURRET: cost = BALANCE_VAR(kSentryCost); break; - case BUILD_SIEGE: cost = BALANCE_VAR(kSiegeCost); break; - case BUILD_COMMANDSTATION: cost = BALANCE_VAR(kCommandStationCost); break; - - // Marine Equipment - case BUILD_HEALTH: cost = BALANCE_VAR(kHealthCost); break; - case BUILD_AMMO: cost = BALANCE_VAR(kAmmoCost); break; - case BUILD_CAT: cost = BALANCE_VAR(kCatalystCost); break; - case BUILD_MINES: cost = BALANCE_VAR(kMineCost); break; - case BUILD_WELDER: cost = BALANCE_VAR(kWelderCost); break; - case BUILD_SHOTGUN: cost = BALANCE_VAR(kShotgunCost); break; - case BUILD_HMG: cost = BALANCE_VAR(kHMGCost); break; - case BUILD_GRENADE_GUN: cost = BALANCE_VAR(kGrenadeLauncherCost); break; - - // Alien Structures - case ALIEN_BUILD_RESOURCES: cost = BALANCE_VAR(kAlienResourceTowerCost); break; - case ALIEN_BUILD_OFFENSE_CHAMBER: cost = BALANCE_VAR(kOffenseChamberCost); break; - case ALIEN_BUILD_DEFENSE_CHAMBER: cost = BALANCE_VAR(kDefenseChamberCost); break; - case ALIEN_BUILD_SENSORY_CHAMBER: cost = BALANCE_VAR(kSensoryChamberCost); break; - case ALIEN_BUILD_MOVEMENT_CHAMBER: cost = BALANCE_VAR(kMovementChamberCost); break; - case ALIEN_BUILD_HIVE: cost = BALANCE_VAR(kHiveCost); break; - - // Alien Evolutions - case ALIEN_EVOLUTION_ONE: cost = BALANCE_VAR(kEvolutionCost); break; - case ALIEN_EVOLUTION_TWO: cost = BALANCE_VAR(kEvolutionCost); break; - case ALIEN_EVOLUTION_THREE: cost = BALANCE_VAR(kEvolutionCost); break; - case ALIEN_EVOLUTION_SEVEN: cost = BALANCE_VAR(kEvolutionCost); break; - case ALIEN_EVOLUTION_EIGHT: cost = BALANCE_VAR(kEvolutionCost); break; - case ALIEN_EVOLUTION_NINE: cost = BALANCE_VAR(kEvolutionCost); break; - case ALIEN_EVOLUTION_TEN: cost = BALANCE_VAR(kEvolutionCost); break; - case ALIEN_EVOLUTION_ELEVEN: cost = BALANCE_VAR(kEvolutionCost); break; - case ALIEN_EVOLUTION_TWELVE: cost = BALANCE_VAR(kEvolutionCost); break; - - // Alien Lifeforms - case ALIEN_LIFEFORM_ONE: cost = BALANCE_VAR(kSkulkCost); break; - case ALIEN_LIFEFORM_TWO: cost = BALANCE_VAR(kGorgeCost); break; - case ALIEN_LIFEFORM_THREE: cost = BALANCE_VAR(kLerkCost); break; - case ALIEN_LIFEFORM_FOUR: cost = BALANCE_VAR(kFadeCost); break; - case ALIEN_LIFEFORM_FIVE: cost = BALANCE_VAR(kOnosCost); break; - - // Energy Costs - case BUILD_SCAN: cost = BALANCE_VAR(kScanEnergyCost); break; - } - } - - return cost; -} - -void AvHGamerules::BalanceChanged() -{ - // Tell all players to update their weapons - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - theEntity->SendWeaponUpdate(); - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) -} - - +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: NS high-level game rules +// +// $Workfile: AvHGamerules.cpp $ +// $Date: 2002/11/22 21:23:55 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHGamerules.cpp,v $ +// Revision 1.79 2002/11/22 21:23:55 Flayra +// - Players can't switch teams after seeing a team +// - Adminmod fixes +// - Fixed bug where players were only doing friendly-fire damage to world entities (ie, 33% to doors and breakables) +// - Changed allowed team discrepancy from 2 to 1. Affects casual model only. +// - Fixes for timelimit +// - A team now automatically wins if they have 4 or more players then another team (generally because the losing team starts quitting). Affects casual mode only. +// +// Revision 1.78 2002/11/15 23:41:52 Flayra +// - Spectators get end-game victory sound and message +// +// Revision 1.77 2002/11/15 23:31:12 Flayra +// - Added "ready" verification for tourny mode +// +// Revision 1.76 2002/11/15 04:46:18 Flayra +// - Changes to for profiling and for improving AddToFullPack performance +// +// Revision 1.75 2002/11/12 18:44:10 Flayra +// - Allow team unbalancing in tourny mode (assume everyone knows what they're doing) +// +// Revision 1.74 2002/11/12 06:21:12 Flayra +// - More AdminMod fixes +// +// Revision 1.73 2002/11/12 02:24:50 Flayra +// - HLTV updates +// - Remove babblers on level clean up +// - Don't dynamic_cast all entities, it's causes AdminMod to crash +// - Log end-game stats in standard way +// - Potential fix for server overflow after big games +// +// Revision 1.72 2002/11/06 01:40:01 Flayra +// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) +// +// Revision 1.71 2002/11/05 06:17:25 Flayra +// - Balance changes +// +// Revision 1.70 2002/11/03 04:49:44 Flayra +// - Moved constants into .dll out of .cfg +// - Particle systems update much less often, big optimization +// - Team balance fixes +// +// Revision 1.69 2002/10/28 20:34:53 Flayra +// - Reworked game reset slightly, to meter out network usage during game reset +// +// Revision 1.68 2002/10/24 21:25:42 Flayra +// - Skin fixes (random number generator returning all 0s at some stages of the engine) +// - Builders don't get points for building anymore +// - Removed code that was never getting called in ClientConnected (AvHPlayer isn't built yet) +// - Remove armor and jetpacks on round reset +// - Update particle systems again, for particle culling +// - Unbuilt hives show up on hive sight +// +// Revision 1.67 2002/10/20 21:10:41 Flayra +// - Optimizations +// +// Revision 1.66 2002/10/20 17:24:41 Flayra +// - Redid performance improvement (agh) +// +// Revision 1.65 2002/10/20 16:36:09 Flayra +// - Added #ifdef for network metering +// - Added particles back in +// +// Revision 1.64 2002/10/19 22:33:44 Flayra +// - Various server optimizations +// +// Revision 1.63 2002/10/18 22:19:11 Flayra +// - Add sensory chamber saying +// +// Revision 1.62 2002/10/16 20:52:46 Flayra +// - Fixed bug introduced with secondary weapons +// +// Revision 1.61 2002/10/16 00:56:22 Flayra +// - Prototyped curl support for NS stats +// - Added player auth stuff (server string) +// - Added concept of secondary weapons +// - Removed disconnect sound +// - Fixed MOTD +// - Added "order needed" alert +// +// Revision 1.60 2002/10/07 17:49:50 Flayra +// - Play marine building alerts properly +// +// Revision 1.59 2002/10/03 18:44:03 Flayra +// - Play disconnected sound quieter because of mass exodus effect +// - Added functionality to allow players to join a team and run around freely before game countdown starts. Game resets when countdown starts. +// - Added new alien alerts +// +// Revision 1.58 2002/09/25 20:44:22 Flayra +// - Removed old LOS code +// - Added extra alerts, allow alien code to peacefully coexist +// +// Revision 1.57 2002/09/23 22:16:01 Flayra +// - Added game victory status logging +// - Fixed commander alerts +// - Added new alerts +// - Particle system changes, only send them down when connecting or map changes +// +// Revision 1.56 2002/09/09 19:51:13 Flayra +// - Removed gamerules dictating alien respawn time, now it's in server variable +// +// Revision 1.55 2002/08/31 18:01:01 Flayra +// - Work at VALVe +// +// Revision 1.54 2002/08/16 02:35:09 Flayra +// - Blips now update once per second, instead of once per player per second +// +// Revision 1.53 2002/08/09 00:58:35 Flayra +// - Removed error condition for map validity, allow marines to have only one primary weapon, adjust weapon weights +// +// Revision 1.52 2002/08/02 22:00:24 Flayra +// - New alert system that's not so annoying and is more helpful. Tweaks for new help system. +// +// Revision 1.51 2002/07/25 16:57:59 flayra +// - Linux changes +// +// Revision 1.50 2002/07/24 18:45:41 Flayra +// - Linux and scripting changes +// +// Revision 1.49 2002/07/23 17:03:20 Flayra +// - Resource model changes, refactoring spawning to fix level 5 redemption bug without code duplication +// +// Revision 1.48 2002/07/10 14:40:48 Flayra +// - Profiling and performance tweaks +// +// Revision 1.47 2002/07/08 16:59:14 Flayra +// - Don't pick up empty weapons, added handicapping, check map validity when loaded, reset players just like all other entities, fixed spawn bug code where it was counting non-team spawns +// +// Revision 1.46 2002/07/01 21:32:43 Flayra +// - Visibility update now updates world for primal scream and umbra, don't update reliable network messages every tick (big optimization) +// +// Revision 1.45 2002/06/25 17:59:03 Flayra +// - Added timelimit (switches map after a game finishes), added info_locations, tried adding team balance (not sure it works yet), give resources for kills +// +// Revision 1.44 2002/06/10 19:54:29 Flayra +// - New minimap support, more attempts to fix picking up of alien weapons +// +// Revision 1.43 2002/06/03 16:45:20 Flayra +// - Breakables and buttons take damage like they should, points added for buildables correctly, weapon weights tweaked +// +// Revision 1.42 2002/05/28 17:40:32 Flayra +// - Minimap refactoring, hive sight "under attack", reinforcement refactoring, don't delete entities marked as permanent (it was deleting hives!), allow players to join the opposite team until this can be fixed for real (server retry gets around this code so this is just an inconvenience), things build fast with cheats +// +// Revision 1.41 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "util/nowarnings.h" +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "dlls/cbase.h" +#include "dlls/player.h" +#include "dlls/weapons.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHConstants.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "mod/AvHEntities.h" +#include "mod/AvHParticleTemplateServer.h" +#include "mod/AvHPlayer.h" +#include "dlls/client.h" +#include "dlls/game.h" +#include "dlls/util.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHMessage.h" +#include "mod/AvHSoundListManager.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHTitles.h" +#include "mod/AvHParticleSystemManager.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHEntities.h" +#include "mod/AvHVoiceHelper.h" +#include "common/director_cmds.h" +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHDramaticPriority.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHHulls.h" +#include "textrep/TRFactory.h" +#include +#include "mod/NetworkMeter.h" +#include "mod/AvHScriptManager.h" +#include "mod/AvHCloakable.h" +#include "mod/AvHCommandConstants.h" +#include "mod/AvHAlert.h" +#include "mod/AvHParticleConstants.h" +#include "util/MathUtil.h" +#include "mod/AvHNetworkMessages.h" +#include "mod/AvHNexusServer.h" +#include "mod/AvHParticleTemplateClient.h" + +// : 0001073 +#ifdef USE_OLDAUTH +AuthMaskListType gAuthMaskList; +extern const char* kSteamIDPending; +extern const char* kSteamIDLocal; +extern const char* kSteamIDBot; +extern const char* kSteamIDInvalidID; +extern const char* kSteamIDDefault; +#endif + +AvHSoundListManager gSoundListManager; +extern AvHVoiceHelper gVoiceHelper; +CVoiceGameMgr g_VoiceGameMgr; +extern cvar_t allow_spectators; +extern cvar_t avh_autoconcede; +extern cvar_t avh_limitteams; +//extern cvar_t avh_teamsizehandicapping; +extern cvar_t avh_team1damagepercent; +extern cvar_t avh_team2damagepercent; +extern cvar_t avh_team3damagepercent; +extern cvar_t avh_team4damagepercent; +extern cvar_t avh_drawinvisible; +extern cvar_t avh_uplink; +extern cvar_t avh_gametime; +extern cvar_t avh_ironman; +extern cvar_t avh_mapvoteratio; +extern cvar_t avh_structurelimit; + +BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ); +inline int FNullEnt( CBaseEntity *ent ) { return (ent == NULL) || FNullEnt( ent->edict() ); } +//extern void ResetCachedEntities(); + +// Allow assignment within conditional +#pragma warning (disable: 4706) + +// Quick way to define or undefine security +extern AvHParticleTemplateListServer gParticleTemplateList; +extern cvar_t avh_deathmatchmode; +extern cvar_t avh_countdowntime; +extern int gCommanderPointsAwardedEventID; +extern cvar_t avh_networkmeterrate; + +std::string gPlayerNames[128]; +cvar_t* cocName; +cvar_t* cocExp; +int gStartPlayerID = 0; +int gServerTick = 0; +int gUpdateEntitiesTick = 0; +bool gServerUpdate = false; +float kPVSCoherencyTime = 1.0f; +extern int kNumReturn0; +extern int kNumReturn1; +extern int kNumCached; +extern int kNumComputed; +extern int kMaxE; +int kServerFrameRate = 0; + +#ifdef PROFILE_BUILD +extern cvar_t avh_performance; +int kProfileRunConfig = 0xFFFFFFFF; +#endif + +std::string GetLogStringForPlayer( edict_t *pEntity ); + +const AvHMapExtents& GetMapExtents() +{ + return GetGameRules()->GetMapExtents(); +} + +void InstallGameRules( void ) +{ + SERVER_COMMAND( "exec game.cfg\n" ); + SERVER_EXECUTE( ); + + AvHGamerules* theNewGamerules = new AvHGamerules; + SetGameRules(theNewGamerules); +} + +static AvHGamerules* sGameRules = NULL; + +AvHGamerules* GetGameRules() +{ + if(!g_pGameRules) + { + InstallGameRules(); + } + + if(!sGameRules) + { + sGameRules = dynamic_cast(g_pGameRules); + } + ASSERT(sGameRules); + + return sGameRules; +} + +void SetGameRules(AvHGamerules* inGameRules) +{ + sGameRules = inGameRules; + g_pGameRules = inGameRules; +} + +static float gSvCheatsLastUpdateTime; +AvHGamerules::AvHGamerules() : mTeamA(TEAM_ONE), mTeamB(TEAM_TWO) +{ + this->mLastJoinMessage = 0.0f; + this->mGameStarted = false; + this->mPreserveTeams = false; + + this->mTeamA.SetTeamType(AVH_CLASS_TYPE_MARINE); + this->mTeamB.SetTeamType(AVH_CLASS_TYPE_ALIEN); + gSvCheatsLastUpdateTime = -1.0f; + this->mVictoryTime = -1; + this->mMapMode = MAP_MODE_UNDEFINED; + this->mLastParticleUpdate = -1; + this->mLastNetworkUpdate = -1; + this->mLastWorldEntityUpdate = -1; + this->mLastMapChange = -1; + this->mTimeOfLastPlaytestUpdate = -1; + this->mTimeOfLastHandicapUpdate = -1; + this->mMapGamma = kDefaultMapGamma; + this->mCombatAttackingTeamNumber = TEAM_IND; + this->mCheats.clear(); + this->mSpawnEntity = NULL; + + RegisterServerVariable(&avh_blockscripts); + RegisterServerVariable(&avh_tournamentmode); + RegisterServerVariable(&avh_team1damagepercent); + RegisterServerVariable(&avh_team2damagepercent); + RegisterServerVariable(&avh_team3damagepercent); + RegisterServerVariable(&avh_team4damagepercent); + RegisterServerVariable(avh_cheats); + RegisterServerVariable(&avh_structurelimit); + + g_VoiceGameMgr.Init(&gVoiceHelper, gpGlobals->maxClients); + + #ifdef DEBUG + avh_drawinvisible.value = 1; + #endif + + this->mGameInReset = false; + + this->ResetGame(); +} + +AvHGamerules::~AvHGamerules() +{ + int a = 0; +} + + +int AvHGamerules::AmmoShouldRespawn( CBasePlayerAmmo *pAmmo ) +{ + return GR_AMMO_RESPAWN_NO; +} + +int AvHGamerules::WeaponShouldRespawn(CBasePlayerItem *pWeapon) +{ + return GR_WEAPON_RESPAWN_NO; +} + +// : 0001073 +#ifdef USE_OLDAUTH //players are authorized by UPP now. +const AuthIDListType& AvHGamerules::GetServerOpList() const +{ + return this->mServerOpList; +} + +bool AvHGamerules::PerformHardAuthorization(AvHPlayer* inPlayer) const +{ + bool theAuthorized = true; + + #ifdef AVH_SECURE_PRERELEASE_BUILD + if(!this->GetIsClientAuthorizedToPlay(inPlayer->edict(), false, true)) + { + char* theMessage = UTIL_VarArgs( + "%s<%s> is not authorized to play on beta NS servers.\n", + STRING(inPlayer->pev->netname), + AvHSUGetPlayerAuthIDString(inPlayer->edict()).c_str() + ); + ALERT(at_logged, theMessage); + + // Boot player off server + inPlayer->Kick(); + + theAuthorized = false; + } + #endif + + return theAuthorized; +} +#endif + +// Sets the player up to join the team, though they may not respawn in immediately depending +// on the ruleset and the state of the game. Assumes 1 or a 2 for team number +bool AvHGamerules::AttemptToJoinTeam(AvHPlayer* inPlayer, AvHTeamNumber inTeamToJoin, bool inDisplayErrorMessage) +{ + bool theSuccess = false; + string theErrorString; + + // Check authorization in secure build + if(!inPlayer->GetIsAuthorized(AUTH_ACTION_JOIN_TEAM,inTeamToJoin)) + { + AvHNexus::handleUnauthorizedJoinTeamAttempt(inPlayer->edict(),inTeamToJoin); + } + else +// : 0001073 +#ifdef USE_OLDAUTH + if(this->PerformHardAuthorization(inPlayer)) +#endif + { + int teamA = this->mTeamA.GetTeamNumber(); + int teamB = this->mTeamB.GetTeamNumber(); + if( inTeamToJoin != teamA && inTeamToJoin != teamB && inTeamToJoin != TEAM_IND ) + { + theErrorString = kTeamNotAvailable; + } + else if(inPlayer->GetTeam() != TEAM_IND) + { + theErrorString = kAlreadyOnTeam; + } + // Can't join teams during the victory intermission (required by AvHPlayer::InternalCommonThink() for gradual player reset) + else if(this->mVictoryTeam != TEAM_IND) + { + int a = 0; + } + else if(this->GetCanJoinTeamInFuture(inPlayer, inTeamToJoin, theErrorString)) + { + this->JoinTeam(inPlayer, inTeamToJoin, inDisplayErrorMessage, false); + this->MarkDramaticEvent(kJoinTeamPriority, inPlayer->entindex()); + theSuccess = true; + } + + // Print error message to HUD + if(!theSuccess) + { + if(inDisplayErrorMessage) + { + // Display error string + inPlayer->SendMessage(theErrorString.c_str()); + } + } + else + { + // : Bug 0000767 + // Tell the other players that this player is joining a team. + if (!this->GetCheatsEnabled() && this->mGameStarted == true && ( gpGlobals->time > this->mLastJoinMessage + 0.2f ) ) { + + AvHTeam* theTeam = GetTeam(inTeamToJoin); + // ensure that the sound only plays if the game already has started + theTeam->PlayHUDSoundForAlivePlayers(HUD_SOUND_PLAYERJOIN); + char* theMessage = UTIL_VarArgs("%s has joined the %s\n",STRING(inPlayer->pev->netname),theTeam->GetTeamPrettyName()); + UTIL_ClientPrintAll(HUD_PRINTTALK, theMessage); + UTIL_LogPrintf( "%s joined team \"%s\"\n", GetLogStringForPlayer( inPlayer->edict() ).c_str(), AvHSUGetTeamName(inPlayer->pev->team) ); + this->mLastJoinMessage=gpGlobals->time; + } + // : + } + } + return theSuccess; +} + +// Pick a team for the player to join. Choose the team with less players. +// If both teams have the same number of player, pick a team randomly +void AvHGamerules::AutoAssignPlayer(AvHPlayer* inPlayer) +{ + int theTeamACount = this->mTeamA.GetPlayerCount(); + int theTeamBCount = this->mTeamB.GetPlayerCount(); + + bool joinTeamA = (theTeamACount < theTeamBCount) || ( (theTeamACount == theTeamBCount) && RANDOM_LONG(0,1) ); + + AvHTeamNumber theTeam = (joinTeamA ? this->mTeamA.GetTeamNumber() : this->mTeamB.GetTeamNumber()); + + // Try to join the first team, but don't emit an error if it fails + if(!this->AttemptToJoinTeam(inPlayer, theTeam, false)) + { + // If it failed, try the other team and emit an error if it too fails + theTeam = (joinTeamA ? this->mTeamB.GetTeamNumber() : this->mTeamA.GetTeamNumber()); + this->AttemptToJoinTeam(inPlayer, theTeam, true); + } +} + +void AvHGamerules::RewardPlayerForKill(AvHPlayer* inPlayer, CBaseEntity* inTarget, entvars_t* inInflictor) +{ + ASSERT(inPlayer); + ASSERT(inTarget); + + // Doesn't count targets of TEAM_IND + if(inPlayer->pev->team != inTarget->pev->team && (inTarget->pev->team != TEAM_IND)) + { + // Team could be NULL if spectating and using cheats + AvHTeam* theTeamPointer = inPlayer->GetTeamPointer(); + + // Only award resources for killing players + if(theTeamPointer && inTarget->IsPlayer()) + { + if(!this->GetIsCombatMode()) + { + int theResourceValue = 0; + int theMin = BALANCE_VAR(kKillRewardMin); + int theMax = BALANCE_VAR(kKillRewardMax); + theResourceValue = RANDOM_LONG(theMin, theMax); + + if(theResourceValue > 0) + { + // Send killing player a message and sound, telling him he just got the kill and is getting the points + AvHClassType theClassType = inPlayer->GetClassType(); + if(theClassType == AVH_CLASS_TYPE_MARINE) + { + inPlayer->SendMessageOnce(kMarinePointsAwarded, TOOLTIP); + } + else if(theClassType == AVH_CLASS_TYPE_ALIEN) + { + inPlayer->SendMessageOnce(kAlienPointsAwarded, TOOLTIP); + } + + // Increment resource score in tourny mode + theTeamPointer->AddResourcesGathered(theResourceValue); + + AvHSUPlayNumericEvent(theResourceValue, inTarget->edict(), inTarget->pev->origin, 0, kNumericalInfoResourcesEvent, inPlayer->pev->team); + + inPlayer->SetResources(inPlayer->GetResources() + theResourceValue, true); + } + } + else + { + // Mine kills don't share experience + bool theShareExperience = true; + if(inInflictor) + { + const char* theClassName = STRING(inInflictor->classname); + if(theClassName && FStrEq(theClassName, kwsDeployedMine)) + { + theShareExperience = false; + } + } + + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + ASSERT(thePlayer); + this->AwardExperience(inPlayer, thePlayer->GetExperienceLevel(), theShareExperience); + } + } + + // Give points or frags for kill + int thePointReward = inTarget->GetPointValue(); + if(thePointReward != 0) + { + inPlayer->SetScore(inPlayer->GetScore() + thePointReward); + + if(inTarget->IsPlayer()) + inPlayer->pev->frags += 1; + inPlayer->EffectivePlayerClassChanged(); + } + } +} + +int AvHGamerules::IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled) +{ + // Return 0 because we increment our points via RewardPlayerForKill() + return 0; +} + +void AvHGamerules::BuildableBuilt(AvHBuildable* inBuildable) +{ + // Get player owner from buildable + edict_t* theEdict = g_engfuncs.pfnPEntityOfEntIndex(inBuildable->GetBuilder()); + if(theEdict != NULL) + { + CBaseEntity* theEntity = CBaseEntity::Instance(theEdict); + AvHPlayer* thePlayer = dynamic_cast(theEntity); + if(thePlayer) + { + AvHBaseBuildable* theBaseBuildable = dynamic_cast(inBuildable); + if(theBaseBuildable) + { + thePlayer->AddPoints(theBaseBuildable->GetPointValue(), TRUE); + } + } + } +} + +void AvHGamerules::BuildableKilled(AvHBuildable* inBuildable) +{ +} + +void AvHGamerules::BuildMiniMap(AvHPlayer* inPlayer) +{ + const char* theCStrLevelName = STRING(gpGlobals->mapname); + if(theCStrLevelName && !FStrEq(theCStrLevelName, "")) + { + this->mMiniMap.BuildMiniMap(theCStrLevelName, inPlayer, this->mMapExtents); + } +} + +BOOL AvHGamerules::CanHaveAmmo( CBasePlayer *pPlayer, const char *pszAmmoName, int iMaxCarry ) +{ + BOOL theCanHaveIt = CHalfLifeTeamplay::CanHaveAmmo(pPlayer, pszAmmoName, iMaxCarry); + return theCanHaveIt; +} + +// The player is touching an CBasePlayerItem, do I give it to him? +BOOL AvHGamerules::CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) +{ + BOOL theCanHaveIt = FALSE; + + // Allow it if we don't already have it + if(CHalfLifeTeamplay::CanHavePlayerItem(pPlayer, pWeapon) && !pPlayer->HasItem(pWeapon)) + { + // Don't allow players to have more then one primary weapon + ItemInfo theItemInfo; + pWeapon->GetItemInfo(&theItemInfo); + int theWeaponFlags = theItemInfo.iFlags; + + // Check primary and secondary weapons (assumes we only have one, but should work even with both) + int theCurrentFlag = (theWeaponFlags & (PRIMARY_WEAPON | SECONDARY_WEAPON)); + CBasePlayerItem* theCurrentItem = NULL; + bool theHasWeaponWithFlag = pPlayer->HasItemWithFlag(theCurrentFlag, theCurrentItem); + + if(theHasWeaponWithFlag) + { + if(theCurrentItem->iWeight() < pWeapon->iWeight()) + { + theCanHaveIt = TRUE; + } + else if(this->GetIsCombatMode()) + { + theCanHaveIt = TRUE; + } + } + else + { + theCanHaveIt = TRUE; + } + } + + return theCanHaveIt; +} + +bool AvHGamerules::CanPlayerBeKilled(CBasePlayer* inPlayer) +{ + bool theCanBeKilled = false; + + // Don't allow players that are being digested to suicide + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + if(thePlayer) + { + if(thePlayer->GetCanBeAffectedByEnemies() && this->GetGameStarted() && !thePlayer->GetIsBeingDigested()) + { + theCanBeKilled = true; + } + } + + return theCanBeKilled; +} + +void AvHGamerules::CalculateMapGamma() +{ + // Set defaults + this->mCalculatedMapGamma = kDefaultMapGamma; + + // Fetch from map extents entity if the map has one + FOR_ALL_ENTITIES(kwsGammaClassName, AvHGamma*) + this->mMapGamma = theEntity->GetGamma(); + END_FOR_ALL_ENTITIES(kwsGammaClassName) + + this->mCalculatedMapGamma = true; +} + +// : 0001073 +#ifdef USE_OLDAUTH +BOOL AvHGamerules::GetIsClientAuthorizedToPlay(edict_t* inEntity, bool inDisplayMessage, bool inForcePending) const +{ + BOOL theIsAuthorized = false; + + #ifndef AVH_SECURE_PRERELEASE_BUILD + theIsAuthorized = true; + #endif + + #ifdef AVH_SECURE_PRERELEASE_BUILD + string theAuthID = AvHSUGetPlayerAuthIDString(inEntity); + const char* thePlayerName = STRING(inEntity->v.netname); + if(!strcmp(thePlayerName, "")) + { + thePlayerName = "unknown"; + } + + // Allow only select players to play + int theSecurityMask = PLAYERAUTH_DEVELOPER | PLAYERAUTH_PLAYTESTER | PLAYERAUTH_CONTRIBUTOR; + + // If any of these bits are set, allow them to play + int theAuthMask = 0; + + // Get the auth mask the cheap way if possible + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(inEntity)); + if(thePlayer) + { + theAuthMask = thePlayer->GetAuthenticationMask(); + } + else + { + theAuthMask = GetGameRules()->GetAuthenticationMask(theAuthID); + } + + if ( inEntity->v.flags & FL_PROXY ) + { + if(inDisplayMessage) + { + char theAuthenticateString[512]; + sprintf(theAuthenticateString, "Allowing HLTV proxy to join\n"); + ALERT(at_logged, theAuthenticateString); + } + + theIsAuthorized = true; + } + else if(theAuthMask & theSecurityMask) + { + if(inDisplayMessage) + { + char theAuthenticateString[512]; + sprintf(theAuthenticateString, "Player (%s -> %s) authenticated as privileged player.\r\n", thePlayerName, theAuthID.c_str()); + ALERT(at_logged, theAuthenticateString); + } + + theIsAuthorized = true; + } + // Pending + else if(theAuthID == kSteamIDPending) + { + if(!inForcePending) + { + // The player is authorized + theIsAuthorized = true; + } + } + // Local players or bots are always allowed + else if((theAuthID == kSteamIDLocal) || (theAuthID == kSteamIDBot) || strncmp(theAuthID.c_str(), "HLTV", 4) == 0 ) + { + theIsAuthorized = true; + } + + // Display message on failure + if(!theIsAuthorized && inDisplayMessage) + { + char theAuthenticateString[512]; + sprintf(theAuthenticateString, "Player (%s -> %s) failed authentication.\r\n", thePlayerName, theAuthID.c_str()); + ALERT(at_logged, theAuthenticateString); + } + + #endif + + return theIsAuthorized; +} +#endif + +// : 0001073 +#ifdef USE_OLDAUTH +BOOL AvHGamerules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) +{ + bool theAllowedToConnect = true; + BOOL theSuccess = false; + + #ifdef AVH_SECURE_PRERELEASE_BUILD + this->UpdateUplink(); + #endif + + #ifdef AVH_SECURE_PRERELEASE_BUILD + theAllowedToConnect = false; + theAllowedToConnect = this->GetIsClientAuthorizedToPlay(pEntity, true, false); + #endif + + if(theAllowedToConnect) + { + g_VoiceGameMgr.ClientConnected(pEntity); + + // Play connect sound + EMIT_SOUND(pEntity, CHAN_AUTO, kConnectSound, 0.8, ATTN_NORM); + + theSuccess = CHalfLifeTeamplay::ClientConnected(pEntity, pszName, pszAddress, szRejectReason); + } + else + { + sprintf(szRejectReason, "Only authorized players can join beta NS servers.\n"); + } + + return theSuccess; +} + +#else + +BOOL AvHGamerules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) +{ + BOOL theSuccess = false; + + g_VoiceGameMgr.ClientConnected(pEntity); + + // Play connect sound + EMIT_SOUND(pEntity, CHAN_AUTO, kConnectSound, 0.8, ATTN_NORM); + + theSuccess = CHalfLifeTeamplay::ClientConnected(pEntity, pszName, pszAddress, szRejectReason); + return theSuccess; +} +#endif + + +void AvHGamerules::ClientDisconnected( edict_t *pClient ) +{ + // Call down to base class + CHalfLifeTeamplay::ClientDisconnected(pClient); + + // Play disconnect sound (don't play, it encourages other people to leave) + //EMIT_SOUND(pClient, CHAN_AUTO, kDisconnectSound, 0.5, ATTN_NORM); + + // Remove him from whatever team he was on (does the player get killed first?) + CBaseEntity* theEntity = CBaseEntity::Instance(ENT(pClient)); + AvHPlayer* theAvHPlayer = dynamic_cast(theEntity); + if(theAvHPlayer) + { + theAvHPlayer->ClientDisconnected(); + } + + // trigger a votemap check + this->RemovePlayerFromVotemap(theEntity->entindex()); +} + +void AvHGamerules::ClientKill( edict_t *pEntity ) +{ +} + +void AvHGamerules::ClientUserInfoChanged(CBasePlayer *pPlayer, char *infobuffer) +{ + // NOTE: Not currently calling down to parent CHalfLifeTeamplay +} + +void AvHGamerules::ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib ) +{ + CHalfLifeTeamplay::ChangePlayerTeam(pPlayer, pTeamName, bKill, bGib); +} + + +int AvHGamerules::DeadPlayerAmmo( CBasePlayer *pPlayer ) +{ + return GR_PLR_DROP_AMMO_NO; +} + +int AvHGamerules::DeadPlayerWeapons(CBasePlayer* inPlayer) +{ + int theReturnCode = GR_PLR_DROP_GUN_NO; + + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + ASSERT(thePlayer); + if(thePlayer->GetIsMarine()) + { + theReturnCode = GR_PLR_DROP_GUN_ACTIVE; + } + + return theReturnCode; +} + +void AvHGamerules::DeathNotice(CBasePlayer* pVictim, entvars_t* pKiller, entvars_t* pInflictor) +{ + // TODO: do something here? Call CHalfLifeMultiplay::DeathNotice() to get normal death notices back + CHalfLifeTeamplay::DeathNotice(pVictim, pKiller, pInflictor); +} + +void AvHGamerules::DeleteAndResetEntities() +{ + this->mGameInReset = true; + + mHLTVEntityHierarchy.Clear(); + + // Print reset message at console + char theResetString[128]; + + sprintf(theResetString, "Game reset started.\n"); + ALERT(at_logged, theResetString); + + //ResetCachedEntities(); + + //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + // theEntity->Reset(); + //END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + // Clear out alien weapons + AvHSURemoveAllEntities(kwsBiteGun); + AvHSURemoveAllEntities(kwsParasiteGun); + AvHSURemoveAllEntities(kwsLeap); + AvHSURemoveAllEntities(kwsDivineWind); + AvHSURemoveAllEntities(kwsSpitGun); + AvHSURemoveAllEntities(kwsHealingSpray); + AvHSURemoveAllEntities(kwsBileBombGun); + AvHSURemoveAllEntities(kwsWebSpinner); + AvHSURemoveAllEntities(kwsSpikeGun); + AvHSURemoveAllEntities(kwsSporeGun); + AvHSURemoveAllEntities(kwsUmbraGun); + AvHSURemoveAllEntities(kwsPrimalScream); + AvHSURemoveAllEntities(kwsSwipe); + AvHSURemoveAllEntities(kwsBlinkGun); + AvHSURemoveAllEntities(kwsAcidRocketGun); + AvHSURemoveAllEntities(kwsMetabolize); + AvHSURemoveAllEntities(kwsClaws); + AvHSURemoveAllEntities(kwsDevour); + AvHSURemoveAllEntities(kwsStomp); + AvHSURemoveAllEntities(kwsCharge); + + // Clear out marine weapons + AvHSURemoveAllEntities(kwsMachineGun); + AvHSURemoveAllEntities(kwsPistol); + AvHSURemoveAllEntities(kwsShotGun); + AvHSURemoveAllEntities(kwsHeavyMachineGun); + AvHSURemoveAllEntities(kwsGrenadeGun); + AvHSURemoveAllEntities(kwsMine); + AvHSURemoveAllEntities(kwsWelder); + AvHSURemoveAllEntities(kwsKnife); + + // Remove alien items and projectiles + AvHSURemoveAllEntities(kwsAcidRocket); + AvHSURemoveAllEntities(kwsBileBomb); + AvHSURemoveAllEntities(kwsBabblerProjectile); + AvHSURemoveAllEntities(kwsSpitProjectile); + AvHSURemoveAllEntities(kwsSporeProjectile); + AvHSURemoveAllEntities(kwsStomp); + AvHSURemoveAllEntities(kwsUmbraCloud); + AvHSURemoveAllEntities(kwsUmbraProjectile); + AvHSURemoveAllEntities(kesTeamWebStrand); + + // Remove marine items and projectiles + AvHSURemoveAllEntities(kwsDeployedMine); + AvHSURemoveAllEntities(kwsHeavyArmor); + AvHSURemoveAllEntities(kwsJetpack); + AvHSURemoveAllEntities(kwsGrenade); + AvHSURemoveAllEntities(kwsScan); + AvHSURemoveAllEntities(kwsGenericAmmo); + AvHSURemoveAllEntities(kwsHealth); + AvHSURemoveAllEntities(kwsWelder); + AvHSURemoveAllEntities(kwsCatalyst); + AvHSURemoveAllEntities(kwsAmmoPack); + + // Remove all non-persistent entities marked buildable + //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::DeleteAndResetEntities\n"); + + FOR_ALL_BASEENTITIES(); + AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); + if(GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_BUILDABLE) && (!theBuildable || !theBuildable->GetIsPersistent())) + { + ASSERT(theBaseEntity->pev->iuser3 != AVH_USER3_HIVE); + UTIL_Remove(theBaseEntity); + } + END_FOR_ALL_BASEENTITIES(); + + // Delete all non-persistent base buildables. + //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::DeleteAndResetEntities#2\n"); + + FOR_ALL_BASEENTITIES(); + AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); + if(theBuildable && !theBuildable->GetIsPersistent()) + { + UTIL_Remove(theBuildable); + } + END_FOR_ALL_BASEENTITIES(); + + // TODO: Remove decals + + this->ResetEntities(); + + FireTargets(ktGameReset, NULL, NULL, USE_TOGGLE, 0.0f); + + sprintf(theResetString, "Game reset complete.\n"); + ALERT(at_logged, theResetString); + + this->mGameInReset = false; + +} + +BOOL AvHGamerules::FAllowMonsters( void ) +{ + return TRUE; +} + +BOOL AvHGamerules::FPlayerCanRespawn( CBasePlayer *pPlayer ) +{ + bool theCanRespawn = false; + + return theCanRespawn; +} + +bool AvHGamerules::CanEntityDoDamageTo(const CBaseEntity* inAttacker, const CBaseEntity* inReceiver, float* outScalar) +{ + bool theCanDoDamage = false; + + if(inAttacker && inReceiver) + { + AvHTeamNumber theAttackerTeam = (AvHTeamNumber)inAttacker->pev->team; + AvHTeamNumber theReceiverTeam = (AvHTeamNumber)inReceiver->pev->team; + bool theTeamsAreDifferent = (theAttackerTeam != theReceiverTeam); + bool theTeamsAreOpposing = theTeamsAreDifferent && (theAttackerTeam != TEAM_IND) && (theReceiverTeam != TEAM_IND); + bool theGameHasStarted = this->GetGameStarted(); + bool theIsBreakable = (inReceiver->pev->iuser3 == AVH_USER3_BREAKABLE); + bool theIsDoor = !strcmp(STRING(inReceiver->pev->classname), kesFuncDoor) || !strcmp(STRING(inReceiver->pev->classname), "func_door_rotating") || !strcmp(STRING(inReceiver->pev->classname), "func_button"); + bool theAttackerIsSiege = inAttacker->pev->classname == MAKE_STRING(kwsSiegeTurret); + bool theAttackerIsMine = inAttacker->pev->classname == MAKE_STRING(kwsDeployedMine); + bool theReceiverIsMine = inReceiver->pev->classname == MAKE_STRING(kwsDeployedMine); + bool theIsFriendlyFireEnabled = friendlyfire.value; + bool theAttackerIsWorld = false; + bool theReceiverIsPlayer = false; + bool theReceiverIsWeb = false; + bool theAttackerIsReceiver = false; + float theScalar = 1.0f; + bool theReceiverIsWorld = false; + + // Never dynamic_cast any entities that could be non-NS entities + if(!AvHSUGetIsExternalClassName(STRING(inAttacker->pev->classname))) + { + theAttackerIsWorld = (dynamic_cast(inAttacker) != NULL); + } + + if(!AvHSUGetIsExternalClassName(STRING(inAttacker->pev->classname))) + { + theReceiverIsPlayer = (dynamic_cast(inReceiver) != NULL); + } + + if(!AvHSUGetIsExternalClassName(STRING(inReceiver->pev->classname))) + { + theReceiverIsWorld = (dynamic_cast(inReceiver) != NULL); + } + + if(!AvHSUGetIsExternalClassName(STRING(inReceiver->pev->classname))) + { + theReceiverIsWeb = (dynamic_cast(inReceiver) != NULL); + } + + if(!theReceiverIsWorld) + { + if((inAttacker == inReceiver) || (CBaseEntity::Instance(inAttacker->pev->owner) == inReceiver)) + { + theAttackerIsReceiver = true; + } + + // If we're in tournament mode and two teams are different, yes + if(theGameHasStarted) + { + if(theTeamsAreOpposing || theIsFriendlyFireEnabled || theIsBreakable || theIsDoor || theAttackerIsWorld || theAttackerIsReceiver || ( theAttackerIsMine && theTeamsAreDifferent )) + { + theCanDoDamage = true; + // Do less damage with friendly fire + if(theAttackerTeam == theReceiverTeam) + { + theScalar = .33f; + } + + if(theAttackerIsReceiver) + { + theScalar = .5f; + } + } + } + } + + // Webs are immune to friendly fire + if(theReceiverIsWeb) + { + // Check if teams are the same + if( inAttacker->pev->team == inReceiver->pev->team ) + { + theCanDoDamage = false; + } + } + + // Mines never blow up friendly mines or anything else + if(theAttackerIsMine) + { + // Check if teams are the same + if( inAttacker->pev->team == inReceiver->pev->team ) + { + theCanDoDamage = false; + } + } + + // Mines can't be blown up by anonymous sources (prevents mines from blowing up other mines) + if(theReceiverIsMine && theAttackerIsWorld) + { + theCanDoDamage = false; + } + + if(inReceiver->pev->takedamage == DAMAGE_NO) + { + theCanDoDamage = false; + } + if ( theAttackerIsSiege && theReceiverIsPlayer ) + { + theCanDoDamage = false; + } + if(theCanDoDamage && outScalar) + { + *outScalar = theScalar; + } + } + return theCanDoDamage; +} + + +BOOL AvHGamerules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ) +{ + BOOL theCanTakeDamage = TRUE; + + if(!this->GetDeathMatchMode() && pAttacker) + { + AvHPlayer* thePlayer = dynamic_cast(pPlayer); + if(thePlayer->GetInReadyRoom()) + { + theCanTakeDamage = FALSE; + } + // Friendly fire only in tournament mode (allow players to damage themselves) + else if((pPlayer != pAttacker) && this->PlayerRelationship(pPlayer, pAttacker) == GR_TEAMMATE) + { + if(!(friendlyfire.value)) + { + theCanTakeDamage = FALSE; + } + } + } + + return theCanTakeDamage; +} + +void AvHGamerules::ComputeWorldChecksum(Checksum& outChecksum) const +{ + // Run through all entities in the world, adding their checksum + //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::ComputeWorldChecksum\n"); + + FOR_ALL_BASEENTITIES(); + theBaseEntity->AddChecksum(outChecksum); + END_FOR_ALL_BASEENTITIES(); +} + +bool AvHGamerules::GetArePlayersAllowedToJoinImmediately(void) const +{ + bool thePlayerIsAllowedToJoinImmediately = false; + + // if the game hasn't started + if(!this->mGameStarted) + { + thePlayerIsAllowedToJoinImmediately = true; + } + // if cheats are enabled + else if(this->GetCheatsEnabled()) + { + thePlayerIsAllowedToJoinImmediately = true; + } + else + { + // if it's not tournament mode and it's within x seconds of game start + float theLateJoinSeconds = ns_cvar_float(&avh_latejointime)*60; + + if(!this->GetIsTournamentMode() && (gpGlobals->time < (this->mTimeGameStarted + theLateJoinSeconds))) + { + thePlayerIsAllowedToJoinImmediately = true; + } + } + + return thePlayerIsAllowedToJoinImmediately; +} + +bool AvHGamerules::GetCanJoinTeamInFuture(AvHPlayer* inPlayer, AvHTeamNumber inTeamNumber, string& outString) const +{ + // You can switch teams, unless + bool theCanJoinTeam = false; + + // You can always go back to ready room + if(inTeamNumber == TEAM_IND) + { + theCanJoinTeam = true; + } + + // You can always join before the game has started + // Don't allow team switching once the game has started, or ever in tourny mode + else if(inPlayer->GetTeam() != inTeamNumber) + { + // Server ops can ignore team balance + if( inPlayer->GetIsMember(PLAYERAUTH_SERVEROP) ) + { + theCanJoinTeam = true; + } + else + { + // Check to see if teams are wildly unbalanced + AvHTeamNumber teamA = this->mTeamA.GetTeamNumber(); + AvHTeamNumber teamB = this->mTeamB.GetTeamNumber(); + AvHTeamNumber theOtherTeamNumber = (inTeamNumber == teamA) ? teamB : teamA; + + const AvHTeam* theTeam = this->GetTeam(inTeamNumber); + const AvHTeam* theOtherTeam = this->GetTeam(theOtherTeamNumber); + + if( theTeam && theOtherTeam ) + { + int theWouldBeNumPlayersOnTeam = theTeam->GetPlayerCount() + 1; + int theWouldBeNumPlayersOnOtherTeam = theOtherTeam->GetPlayerCount(); + + // Subtract ourselves off + if(inPlayer->GetTeam() == theOtherTeamNumber) + { + theWouldBeNumPlayersOnOtherTeam--; + } + + int theDiscrepancyAllowed = max(1.0f, avh_limitteams.value); + if(((theWouldBeNumPlayersOnTeam - theWouldBeNumPlayersOnOtherTeam) <= theDiscrepancyAllowed) || this->GetIsTournamentMode() || this->GetCheatsEnabled()) + { + // : 0000953 + if (!(this->GetCheatsEnabled()) && !(inPlayer->JoinTeamCooledDown(BALANCE_VAR(kJoinTeamCooldown)))) + outString = kJoinTeamTooFast; + else + // : + theCanJoinTeam = true; + } + else + { + outString = kTooManyPlayersOnTeam; + } + } + } + } + + return theCanJoinTeam; +} + +const AvHBaseInfoLocationListType& AvHGamerules::GetInfoLocations() const +{ + return mInfoLocations; +} + +bool AvHGamerules::GetCheatsEnabled(void) const +{ + static float theCheatsEnabled = ns_cvar_float( avh_cheats ); + if (gpGlobals->time > (gSvCheatsLastUpdateTime + 0.5f)) + { + theCheatsEnabled = ns_cvar_float( avh_cheats ); + gSvCheatsLastUpdateTime = gpGlobals->time; + } + return (theCheatsEnabled == 1.0f); +} + +float AvHGamerules::GetFirstScanThinkTime() const +{ + return this->GetIsCombatMode() ? 0.25f : 0.5f; +} + +bool AvHGamerules::GetDrawInvisibleEntities() const +{ + bool theDrawInvisible = false; + + #ifdef DEBUG + if(CVAR_GET_FLOAT(kvDrawInvisible) > 0) + { + theDrawInvisible = true; + } + #endif + + return theDrawInvisible; +} + +bool AvHGamerules::GetDeathMatchMode(void) const +{ + bool theInDMMode = false; + + // deathmatch mode means there is no ready room + if(avh_deathmatchmode.value == 1.0f) + { + theInDMMode = true; + } + + return theInDMMode; +} + +bool AvHGamerules::GetEntityExists(const char* inName) const +{ + bool theSuccess = false; + + edict_t* theEnt = FIND_ENTITY_BY_CLASSNAME(NULL, inName); + if(!FNullEnt(theEnt)) + { + theSuccess = true; + } + else + { + // Look in spawns + for(SpawnListType::const_iterator theIter = this->mSpawnList.begin(); theIter != this->mSpawnList.end(); theIter++) + { + if(!strcmp(theIter->GetClassName().c_str(), inName)) + { + theSuccess = true; + break; + } + } + } + + return theSuccess; +} + +const char* AvHGamerules::GetGameDescription(void) +{ + static char sGameDescription[512]; + + sprintf(sGameDescription, "%s %s", kAvHGameAcronymn, AvHSUGetGameVersionString()); + + return sGameDescription; +} + +//edict_t* AvHGamerules::GetPlayerSpawnSpot( CBasePlayer *pPlayer ) +//{ +//} + +bool AvHGamerules::GetCountdownStarted() const +{ + return this->mStartedCountdown; +} + +bool AvHGamerules::GetGameStarted() const +{ + return this->mGameStarted; +} + +int AvHGamerules::GetGameTime() const +{ + int theGameTime = 0; + + if(this->GetGameStarted()) + { + theGameTime = (gpGlobals->time - this->mTimeGameStarted); + } + + return theGameTime; +} + +float AvHGamerules::GetMapGamma() +{ + if(!this->mCalculatedMapGamma) + { + this->CalculateMapGamma(); + } + + return this->mMapGamma; +} + +const AvHGameplay& AvHGamerules::GetGameplay() const +{ + return this->mGameplay; +} + +bool AvHGamerules::GetIsTesting(void) const +{ +#ifdef DEBUG + return ns_cvar_float(&avh_testing) > 0; +#else + return false; +#endif +} + +bool AvHGamerules::GetIsTournamentMode(void) const +{ + return (ns_cvar_float(&avh_tournamentmode) == 1.0f); +} + +bool AvHGamerules::GetIsCombatMode(void) const +{ + return (this->GetMapMode() == MAP_MODE_CO); +} + +AvHTeamNumber AvHGamerules::GetCombatAttackingTeamNumber() const +{ + return this->mCombatAttackingTeamNumber; +} + +bool AvHGamerules::GetIsNSMode(void) const +{ + return (this->GetMapMode() == MAP_MODE_NS); +} + +bool AvHGamerules::GetIsHamboneMode() const +{ + return this->GetIsCombatMode() && (ns_cvar_int(&avh_ironman) == 2); +} + +bool AvHGamerules::GetIsIronMan(void) const +{ + return this->GetIsCombatMode() && (ns_cvar_int(&avh_ironman) == 1); +} + +bool AvHGamerules::GetIsTrainingMode(void) const +{ +#ifdef DEBUG + return (ns_cvar_float(&avh_trainingmode) == 1.0f); +#else + return false; +#endif +} + +AvHMapMode AvHGamerules::GetMapMode(void) const +{ + return this->mMapMode; +} + +const AvHMapExtents& AvHGamerules::GetMapExtents() +{ + this->mMapExtents.CalculateMapExtents(); + + return this->mMapExtents; +} + +AvHEntityHierarchy& AvHGamerules::GetEntityHierarchy(AvHTeamNumber inTeam) +{ + if(inTeam == this->mTeamA.GetTeamNumber()) + { + return this->mTeamAEntityHierarchy; + } + else if(inTeam == this->mTeamB.GetTeamNumber()) + { + return this->mTeamBEntityHierarchy; + } + else if (inTeam == TEAM_SPECT) { + return this->mSpecEntityHierarchy; + } + else + { + ASSERT(false); + } + return this->mTeamAEntityHierarchy; +} + +bool AvHGamerules::GetIsPlayerSelectableByPlayer(AvHPlayer* inTargetPlayer, AvHPlayer* inByPlayer) +{ + ASSERT(inTargetPlayer); + ASSERT(inByPlayer); + + bool thePlayerIsSelectable = false; + + if(inTargetPlayer && inTargetPlayer->IsAlive() && (inTargetPlayer != inByPlayer)) + { + if(inTargetPlayer->pev->team == inByPlayer->pev->team) + { + if(!inTargetPlayer->IsObserver()) + { + thePlayerIsSelectable = true; + } + } + } + + return thePlayerIsSelectable; +} + +int AvHGamerules::GetServerTick() const +{ + return gServerTick; +} + +const char* AvHGamerules::GetSpawnEntityName(AvHPlayer* inPlayer) const +{ + // Come back to ready room by default (in case there is a bug, go back to ready room, don't crash) + const char* theSpawnEntityName = kesReadyRoomStart; + + AvHClassType theClassType = inPlayer->GetClassType(); + + // If there is no no avh start points, try to start up as CS. If that doesn't look + // right, always return DM spawns. + if((this->mMapMode == MAP_MODE_NS) || (this->mMapMode == MAP_MODE_CO)) + { + // The different cases: + // Player just connected to server and hasn't done anything yet OR + // Player is leaving game and going back to ready room + if(theClassType == AVH_CLASS_TYPE_UNDEFINED) + { + // Use ready room spawn + theSpawnEntityName = kesReadyRoomStart; + } + else + { + theSpawnEntityName = kesTeamStart; + } + } + else if(this->mMapMode == MAP_MODE_CS) + { + switch(theClassType) + { + // "CT" + case AVH_CLASS_TYPE_MARINE: + theSpawnEntityName = kesCounterTerroristStart; + inPlayer->SetPendingCommand(kcJoinTeamOne); + break; + + // "T" + case AVH_CLASS_TYPE_ALIEN: + theSpawnEntityName = kesTerroristStart; + inPlayer->SetPendingCommand(kcJoinTeamTwo); + break; + + // Random + case AVH_CLASS_TYPE_UNDEFINED: + case AVH_CLASS_TYPE_AUTOASSIGN: + if(RANDOM_LONG(0, 1) == 0) + { + theSpawnEntityName = kesCounterTerroristStart; + inPlayer->SetPendingCommand(kcJoinTeamOne); + } + else + { + theSpawnEntityName = kesTerroristStart; + inPlayer->SetPendingCommand(kcJoinTeamTwo); + } + break; + } + } + else if(this->mMapMode == MAP_MODE_DM) + { + theSpawnEntityName = kesDeathMatchStart; + } + + return theSpawnEntityName; +} + +float AvHGamerules::GetTimeGameStarted() const +{ + return this->mTimeGameStarted; +} + +int AvHGamerules::GetTimeLimit() const +{ + float theMinutes = ns_cvar_float(&timelimit); + + if(this->GetIsCombatMode()) + { + theMinutes = ns_cvar_float(&avh_combattime); + } + + int theTimeLimit = (int)(theMinutes*60); + + return theTimeLimit; +} + +const AvHTeam* AvHGamerules::GetTeam(AvHTeamNumber inTeamNumber) const +{ + const AvHTeam* theTeam = NULL; + + if( this->mTeamA.GetTeamNumber() == inTeamNumber ) + { theTeam = &this->mTeamA; } + if( this->mTeamB.GetTeamNumber() == inTeamNumber ) + { theTeam = &this->mTeamB; } + return theTeam; +} + +const AvHTeam* AvHGamerules::GetTeamA(void) const +{ + return &this->mTeamA; +} + +const AvHTeam* AvHGamerules::GetTeamB(void) const +{ + return &this->mTeamB; +} + +AvHTeam* AvHGamerules::GetTeam(AvHTeamNumber inTeamNumber) +{ + AvHTeam* theTeam = NULL; + + if( this->mTeamA.GetTeamNumber() == inTeamNumber ) + { theTeam = &this->mTeamA; } + if( this->mTeamB.GetTeamNumber() == inTeamNumber ) + { theTeam = &this->mTeamB; } + return theTeam; +} +AvHTeam* AvHGamerules::GetTeamA(void) +{ + return &this->mTeamA; +} + +AvHTeam* AvHGamerules::GetTeamB(void) +{ + return &this->mTeamB; +} + +AvHTeamNumber AvHGamerules::GetVictoryTeam() const +{ + return this->mVictoryTeam; +} + +float AvHGamerules::GetVictoryTime() const +{ + return this->mVictoryTime; +} + +// Assumes full encumbered weight around 50 +int AvHGamerules::GetMaxWeight(void) const +{ + return 30; +} + +int AvHGamerules::GetNumCommandersOnTeam(AvHTeamNumber inTeam) +{ + const AvHTeam* theTeam = this->GetTeam(inTeam); + ASSERT(theTeam); + + int theNumCommanders = (theTeam->GetCommander() == -1 ? 0 : 1); + return theNumCommanders; +} + +int AvHGamerules::GetNumActiveHives(AvHTeamNumber inTeam) const +{ + int theNumHives = 0; + + const AvHTeam* theTeam = this->GetTeam(inTeam); + if(theTeam) + { + theNumHives = theTeam->GetNumActiveHives(); + } + + return theNumHives; +} + +int AvHGamerules::GetNumEntities() const +{ + int theNumEntities = 0; + + //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::GetNumEntities\n"); + +// FOR_ALL_BASEENTITIES(); +// theNumEntities++; +// END_FOR_ALL_BASEENTITIES(); + + theNumEntities = g_engfuncs.pfnNumberOfEntities(); + + return theNumEntities; +} + +int AvHGamerules::GetWeightForItemAndAmmo(AvHWeaponID inWeapon, int inNumRounds) const +{ + // Assumes full encumbered weight around 50 + + int theWeight = 0; + ASSERT(inNumRounds >= 0); + + switch(inWeapon) + { + case AVH_WEAPON_NONE: + break; + + case AVH_WEAPON_MG: + theWeight += 4; + theWeight += (inNumRounds/100.0f)*1; + break; + + case AVH_WEAPON_PISTOL: + theWeight += 2; + theWeight += (inNumRounds/8.0f)*.5; + break; + + case AVH_WEAPON_KNIFE: + // No weight for the knife + break; + + case AVH_WEAPON_SONIC: + theWeight += 5; + theWeight += (inNumRounds/10.0f)*2; + break; + + case AVH_WEAPON_HMG: + theWeight += 7; + theWeight += (inNumRounds/150.0f)*2; + break; + + case AVH_WEAPON_GRENADE_GUN: + theWeight += 7; + theWeight += (inNumRounds/4.0f)*1; + break; + + case AVH_WEAPON_WELDER: + theWeight += 4; + break; + +// case AVH_WEAPON_NUKE: +// theWeight += 18; +// theWeight += (inNumRounds/5.0f)*8; +// break; + + //case AVH_WEAPON_FLAMER: + // theWeight += 10; + // break; + + case AVH_WEAPON_MINE: + // Only judge by amount of ammo + theWeight += inNumRounds; + break; + } + + return theWeight; +} + +BOOL AvHGamerules::IsMultiplayer( void ) +{ + return TRUE; +} + +BOOL AvHGamerules::IsDeathmatch( void ) +{ + return FALSE; +} + +BOOL AvHGamerules::IsCoOp( void ) +{ + return FALSE; +} + +void AvHGamerules::InitHUD( CBasePlayer *pPlayer ) +{ + // Call down to base class first (this may need to be changed later but make sure to update spectator cam) + CHalfLifeTeamplay::InitHUD(pPlayer); + // notify other clients of player joining the game + UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs( "%s has joined the game\n", ( pPlayer->pev->netname && STRING(pPlayer->pev->netname)[0] != 0 ) ? STRING(pPlayer->pev->netname) : "unconnected" ) ); +} + + +void GetMapNamesFromMapCycle(StringList& outMapNameList); + +void AvHGamerules::InitializeMapVoteList() +{ + // Add each map + this->mMapVoteList.clear(); + this->mPlayersVoted.clear(); + this->mPlayersVoteTime.clear(); + + StringList theMapNames; + GetMapNamesFromMapCycle(theMapNames); + + // Traverse list + for(StringList::iterator theIter = theMapNames.begin(); theIter != theMapNames.end(); theIter++) + { + this->mMapVoteList.push_back( make_pair(*theIter, 0) ); + } +} + +void AvHGamerules::PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) +{ + ItemInfo theItemInfo; + pWeapon->GetItemInfo(&theItemInfo); + int theWeaponFlags = theItemInfo.iFlags; + //bool theDroppedWeapon = false; + + for(int i = 0; i < 2; i++) + { + int theCurrentFlag = (i == 0 ? PRIMARY_WEAPON : SECONDARY_WEAPON); + + CBasePlayerItem* theCurrentItem = NULL; + bool theHasWeaponWithFlag = pPlayer->HasItemWithFlag(theCurrentFlag, theCurrentItem); + + if(((theWeaponFlags & theCurrentFlag) && pPlayer->HasItemWithFlag(theCurrentFlag, theCurrentItem))) + { + // Discard the one we have + char theWeaponName[256]; + const char* theCurrentWeaponName = theCurrentItem->pszName(); + ASSERT(theCurrentWeaponName); + + strcpy(theWeaponName, theCurrentWeaponName); + AvHPlayer* thePlayer = dynamic_cast(pPlayer); + if(thePlayer) + { + if(GetGameRules()->GetIsCombatMode()) + { + thePlayer->RemovePlayerItem(theCurrentItem); + theCurrentItem->DestroyItem(); + } + else + { + thePlayer->DropItem(theWeaponName); + } + } + //theDroppedWeapon = true; + } + } + + //if(!theDroppedWeapon) + //{ + CHalfLifeTeamplay::PlayerGotWeapon(pPlayer, pWeapon); + //} + + if(GetGameRules()->GetIsCombatMode()) + { + if(!AvHSUGetIsExternalClassName(STRING(pWeapon->pev->classname))) + { + AvHBasePlayerWeapon* theWeapon = dynamic_cast(pWeapon); + if(theWeapon) + { + // Spawn with default ammo + ItemInfo theItemInfo; + if(theWeapon->UsesAmmo() && theWeapon->GetItemInfo(&theItemInfo) && theWeapon->GetCanBeResupplied()) + { + //int theMaxPrimary = theItemInfo.iMaxAmmo1; + int theStartAmmo = theItemInfo.iMaxClip*BALANCE_VAR(kCombatSpawnClips); + + // Add some to primary store + pPlayer->m_rgAmmo[theWeapon->m_iPrimaryAmmoType] = theStartAmmo; + } + } + } + } +} + +int AvHGamerules::GetStructureLimit() +{ + return avh_structurelimit.value; +} + +Vector AvHGamerules::GetSpawnAreaCenter(AvHTeamNumber inTeamNumber) const +{ + Vector theCenter(0, 0, 0); + int theNumSpawns = 0; + + for(SpawnListType::const_iterator theIter = this->mSpawnList.begin(); theIter != this->mSpawnList.end(); theIter++) + { + if(theIter->GetTeamNumber() == inTeamNumber) + { + VectorAdd(theIter->GetOrigin(), theCenter, theCenter); + theNumSpawns++; + } + } + + if(theNumSpawns > 0) + { + VectorScale(theCenter, 1.0f/theNumSpawns, theCenter); + } + + return theCenter; +} + +void AvHGamerules::PerformMapValidityCheck() +{ + // Perform check to see that we have enough of all the entities + int theNumReadyRoomSpawns = 0; + int theNumTeamASpawns = 0; + int theNumTeamBSpawns = 0; +// CBaseEntity* theSpawn = NULL; +// while((theSpawn = UTIL_FindEntityByClassname(theSpawn, kesReadyRoomStart)) != NULL) +// { +// theNumReadyRoomSpawns++; +// } + + for(SpawnListType::const_iterator theIter = this->mSpawnList.begin(); theIter != this->mSpawnList.end(); theIter++) + { + if(!strcmp(theIter->GetClassName().c_str(), kesReadyRoomStart)) + { + theNumReadyRoomSpawns++; + } + else if(theIter->GetTeamNumber() == this->mTeamA.GetTeamNumber()) + { + theNumTeamASpawns++; + } + else if(theIter->GetTeamNumber() == this->mTeamB.GetTeamNumber()) + { + theNumTeamBSpawns++; + } + } + + int theNumDesiredReadyRoomSpawns = 32; + int theNumDesiredTeamASpawns = this->mTeamA.GetDesiredSpawnCount(); + int theNumDesiredTeamBSpawns = this->mTeamB.GetDesiredSpawnCount(); + + if((theNumReadyRoomSpawns < theNumDesiredReadyRoomSpawns) || (theNumTeamASpawns < theNumDesiredTeamASpawns) || (theNumTeamBSpawns < theNumDesiredTeamBSpawns)) + { + ALERT(at_logged, "Map validity check failure: %d/%d ready room spawns, %d/%d team A spawns, %d/%d team B spawns.\n", theNumReadyRoomSpawns, theNumDesiredReadyRoomSpawns, theNumTeamASpawns, theNumDesiredTeamASpawns, theNumTeamBSpawns, theNumDesiredTeamBSpawns); + } + else + { + ALERT(at_logged, "Map validity check success.\n"); + } +} + +void AvHGamerules::PostWorldPrecacheInitParticles() +{ + // When a player first connects, send down all particle info + ASSERT(gParticleTemplateList.GetCreatedTemplates()); + + // Make sure client clears out all particle systems first + int theNumberTemplates = gParticleTemplateList.GetNumberTemplates(); + for(int i = 0; i < theNumberTemplates; i++) + { + AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(i); + gParticleTemplateList.LinkToEntities(theTemplate); + } +} + +void AvHGamerules::PreWorldPrecacheInitParticles() +{ + // Reset templates and everything + AvHParticleSystemManager::Instance()->Reset(); + + // Load up the particle systems from modname.ps then levelname.ps + TRDescriptionList theDescriptionList; + string theLevelBaseSystemFile = string(getModDirectory()) + "/" + kBasePSName; + if(TRFactory::ReadDescriptions(theLevelBaseSystemFile, theDescriptionList)) + { + gParticleTemplateList.CreateTemplates(theDescriptionList); + } + theDescriptionList.clear(); + + // TODO: the level name isn't populated yet for some reason + const char* theCStrLevelName = STRING(gpGlobals->mapname); + if(theCStrLevelName && !FStrEq(theCStrLevelName, "")) + { + string theLevelName = theCStrLevelName; + string theLevelParticleSystemFile = string(getModDirectory()) + string("/") + theLevelName + string(".ps"); + if(TRFactory::ReadDescriptions(theLevelParticleSystemFile, theDescriptionList)) + { + gParticleTemplateList.CreateTemplates(theDescriptionList); + } + } +} + +// Emit line to log indicating victory status +void AvHGamerules::TallyVictoryStats() const +{ + int theGameLength = (int)(gpGlobals->time - this->mTimeGameStarted); + int theGameLengthMinutes = theGameLength/60; + int theGameLengthSeconds = theGameLength % 60; + + const char* thePrintableMapName = "ns_?"; + const char* theMapName = STRING(gpGlobals->mapname); + if(theMapName) + { + thePrintableMapName = theMapName; + } + + const AvHTeam* theVictoryTeam = &this->mTeamA; + const AvHTeam* theLosingTeam = &this->mTeamB; + + if(this->mVictoryTeam == this->mTeamB.GetTeamNumber()) + { + theVictoryTeam = &this->mTeamB; + theLosingTeam = &this->mTeamA; + } + + ALERT(at_logged, "Team \"%d\" scored \"%.2f\" with \"%d\" players\n", this->mTeamA.GetTeamNumber(), this->mTeamA.GetTotalResourcesGathered(), this->mTeamA.GetPlayerCount()); + ALERT(at_logged, "Team \"%d\" scored \"%.2f\" with \"%d\" players\n", this->mTeamB.GetTeamNumber(), this->mTeamB.GetTotalResourcesGathered(), this->mTeamB.GetPlayerCount()); + + if(!this->mVictoryDraw) + { + ALERT(at_logged, "(map_name \"%s\") (game_version \"%s\") (game_time \"%02d:%02d\") (victory_team \"%s\") (losing_team \"%s\") (winning_teamsize \"%d\") (losing_teamsize \"%d\")\n", thePrintableMapName, AvHSUGetGameVersionString(), theGameLengthMinutes, theGameLengthSeconds, theVictoryTeam->GetTeamTypeString(), theLosingTeam->GetTeamTypeString(), theVictoryTeam->GetPlayerCount(), theLosingTeam->GetPlayerCount()); + } + else + { + ALERT(at_logged, "(map_name \"%s\") (game_version \"%s\") (game_time \"%02d:%02d\") (victory_team \"draw\") (losing_team \"draw\")\n", thePrintableMapName, AvHSUGetGameVersionString(), theGameLengthMinutes, theGameLengthSeconds); + } +} + +void AvHGamerules::InitializeTechNodes() +{ + this->mTeamA.InitializeTechNodes(); + this->mTeamB.InitializeTechNodes(); +} + +void AvHGamerules::PlayerDeathEnd(AvHPlayer* inPlayer) +{ + ASSERT(inPlayer); + + if(inPlayer->GetPlayMode() == PLAYMODE_PLAYING) + { + inPlayer->pev->nextthink = -1; + inPlayer->SetPlayMode(PLAYMODE_AWAITINGREINFORCEMENT, true); + } +} + +void AvHGamerules::PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ) +{ + // Tell gamerules + CHalfLifeTeamplay::PlayerKilled(pVictim, pKiller, pInflictor); + + CBaseEntity* theBaseKiller = CBaseEntity::Instance(pKiller); + AvHPlayer* theKiller = dynamic_cast(theBaseKiller); + if(!theKiller) + { + CBaseEntity* theOwner = CBaseEntity::Instance(theBaseKiller->pev->owner); + theKiller = dynamic_cast(theOwner); + } + + if(theKiller) + { + this->RewardPlayerForKill(theKiller, pVictim, pInflictor); + } +} + + +// From CHalfLifeMultiplay::PlayerSpawn +void AvHGamerules::PlayerSpawn( CBasePlayer *pPlayer ) +{ + AvHPlayer* theAvHPlayer = dynamic_cast(pPlayer); + BOOL addDefault; + CBaseEntity* pWeaponEntity = NULL; + + pPlayer->pev->weapons |= (1<Touch( pPlayer ); + addDefault = FALSE; + } + + if ( addDefault ) + { + theAvHPlayer->pev->team = theAvHPlayer->GetTeam(); + //theAvHPlayer->InitializeFromTeam(); + //this->GetTeam(theAvHPlayer->GetTeam())->AddPlayer(theAvHPlayer->entindex(), theAvHPlayer->GetRole()); + } +} + +void AvHGamerules::PlayerThink( CBasePlayer *pPlayer ) +{ + AvHPlayer* thePlayer = dynamic_cast(pPlayer); + + // Update base + CHalfLifeTeamplay::PlayerThink(pPlayer); +} + +// Reset all players, remove weapons off the ground, etc. +void AvHGamerules::PostWorldPrecacheReset(bool inNewMap) +{ + EntityListType theJoinedTeamA; + EntityListType theJoinedTeamB; + EntityListType theSpectating; + + // If we're preserving teams + if(this->mPreserveTeams) + { + // Remember the players that had already joined + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + int theEntityIndex = theEntity->entindex(); + + if(theEntity->pev->team == this->mTeamA.GetTeamNumber()) + { + theJoinedTeamA.push_back(theEntityIndex); + } + else if(theEntity->pev->team == this->mTeamB.GetTeamNumber()) + { + theJoinedTeamB.push_back(theEntityIndex); + } + else if(theEntity->pev->team == TEAM_IND) + { + if(theEntity->GetPlayMode() == PLAYMODE_OBSERVER) + { + theSpectating.push_back(theEntityIndex); + } + } + + theEntity->SendMessageNextThink(kGameStarting); + + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + } + + // Stop running all scripts + AvHScriptManager::Instance()->Reset(); + + this->DeleteAndResetEntities(); + + gServerTick = 0; + gServerUpdate = false; + this->mTimeUpdatedScripts = -1; + + // Reset game on teams + this->mGameplay.Reset(); + + // Find gameplay entity if it exists + FOR_ALL_ENTITIES(kwsGameplayClassName, AvHGameplay*) + this->mGameplay = *theEntity; + END_FOR_ALL_ENTITIES(kwsMapInfoClassName) + + this->mTeamA.ResetGame(); + this->mTeamB.ResetGame(); + + this->mTeamAEntityHierarchy.Clear(); + this->mTeamBEntityHierarchy.Clear(); + this->mSpecEntityHierarchy.Clear(); + + // Set up both sides + this->mTeamA.SetTeamType(this->mGameplay.GetTeamAType()); + this->mTeamB.SetTeamType(this->mGameplay.GetTeamBType()); + + // Set team numbers differently if teams are the same, so their colors change + // Marines vs. marines will be team 1 vs, team 3, aliens vs. aliens will be team 2 vs. team 4 + if(this->mTeamA.GetTeamType() == this->mTeamB.GetTeamType()) + { + if(this->mTeamA.GetTeamType() == AVH_CLASS_TYPE_MARINE) + { + this->mTeamA.SetTeamNumber(TEAM_ONE); + this->mTeamB.SetTeamNumber(TEAM_THREE); + this->mTeamB.SetTeamName(kMarine2Team); + this->mTeamB.SetTeamPrettyName(kMarinePrettyName); + } + else + { + this->mTeamA.SetTeamNumber(TEAM_TWO); + this->mTeamA.SetTeamName(kAlien1Team); + this->mTeamA.SetTeamPrettyName(kAlienPrettyName); + this->mTeamB.SetTeamNumber(TEAM_FOUR); + this->mTeamB.SetTeamName(kAlien2Team); + this->mTeamB.SetTeamPrettyName(kAlienPrettyName); + } + } + + this->InitializeTechNodes(); + + this->PostWorldPrecacheInitParticles(); + + // Set round as not started + this->InternalResetGameRules(); + + this->mEntitiesUnderAttack.clear(); + + this->mMapExtents.ResetMapExtents(); + + this->mCalculatedMapGamma = false; + + // TODO: Clear min/max map sizes? Others? + + // Find info location entities, store our own internal representation, then delete them for efficiency + if(inNewMap || (this->mSpawnList.size() == 0)) + { + this->mInfoLocations.clear(); + + FOR_ALL_ENTITIES(kesInfoLocation, AvHInfoLocation*) + AvHBaseInfoLocation theLocation(*theEntity); + this->mInfoLocations.push_back(theLocation); + UTIL_Remove(theEntity); + END_FOR_ALL_ENTITIES(kesInfoLocation); + + // Clear spawns on a new map only + this->mSpawnList.clear(); + if(this->mSpawnEntity) + { + UTIL_Remove(this->mSpawnEntity); + } + + FOR_ALL_ENTITIES(kesReadyRoomStart, CPointEntity*) + string theClassName(STRING(theEntity->pev->classname)); + this->RegisterSpawnPoint(theClassName, theEntity->pev->origin, theEntity->pev->angles, TEAM_IND); + UTIL_Remove(theEntity); + END_FOR_ALL_ENTITIES(kesReadyRoomStart); + + FOR_ALL_ENTITIES(kesTeamStart, AvHTeamStartEntity*) + string theClassName(STRING(theEntity->pev->classname)); + this->RegisterSpawnPoint(theClassName, theEntity->pev->origin, theEntity->pev->angles, theEntity->GetTeamNumber()); + UTIL_Remove(theEntity); + END_FOR_ALL_ENTITIES(kesTeamStart); + + // Create dummy entity we can use to return from SelectSpawnPoint + this->mSpawnEntity = CBaseEntity::Create(kesReadyRoomStart, Vector(), Vector()); + ASSERT(this->mSpawnEntity); + + // Added by mmcguire. + this->mSpawnEntity->pev->effects |= EF_NODRAW; + + // Clear map voting on a level change + this->mMapVoteList.clear(); + this->mPlayersVoted.clear(); + this->mPlayersVoteTime.clear(); + } + + // Must happen after processing spawn entities + this->RecalculateMapMode(); + + // Loop through all players that are playing and respawn them + + bool theJustResetGameAtCountdownStart = false; + + // Now tell those players to all join again + EntityListType::iterator theIter, end = theJoinedTeamA.end(); + for(theIter = theJoinedTeamA.begin(); theIter != end; ++theIter) + { + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter))); + if(thePlayer) + { + GetGameRules()->JoinTeam(thePlayer, this->mTeamA.GetTeamNumber(), false, true); + } + theJustResetGameAtCountdownStart = true; + } + theJoinedTeamA.clear(); + + // Now tell those players to all join again + end = theJoinedTeamB.end(); + for(theIter = theJoinedTeamB.begin(); theIter != end; ++theIter) + { + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter))); + if(thePlayer) + { + GetGameRules()->JoinTeam(thePlayer, this->mTeamB.GetTeamNumber(), false, true); + } + theJustResetGameAtCountdownStart = true; + } + theJoinedTeamB.clear(); + + // Rejoin spectators + for(theIter = theSpectating.begin(); theIter != theSpectating.end(); theIter++) + { + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter))); + if(thePlayer) + { + thePlayer->SetPlayMode(PLAYMODE_OBSERVER); + } + theJustResetGameAtCountdownStart = true; + } + theSpectating.clear(); + + if(theJustResetGameAtCountdownStart) + { + this->mTimeCountDownStarted = this->mSavedTimeCountDownStarted; + this->mStartedCountdown = true; + } + + //gVoiceHelper.Reset(); +} + +void AvHGamerules::JoinTeam(AvHPlayer* inPlayer, AvHTeamNumber inTeamToJoin, bool inDisplayErrorMessage, bool inForce) +{ + // Report new player status + int thePrevTeam = inPlayer->pev->team; + inPlayer->pev->team = inTeamToJoin; + + inPlayer->InitializeFromTeam(); + + AvHServerPlayerData* theServerPlayerData = inPlayer->GetServerPlayerData(); + + bool thePlayerCanJoinImmediately = this->GetArePlayersAllowedToJoinImmediately() && this->SelectSpawnPoint(inPlayer) || inForce; + + if(theServerPlayerData + && theServerPlayerData->GetHasJoinedTeam() + && GetGameRules()->GetGameStarted() + && !inForce + && inTeamToJoin != TEAM_IND + && !GetGameRules()->GetCheatsEnabled()) + thePlayerCanJoinImmediately = false; + + if(thePlayerCanJoinImmediately) + { + inPlayer->SetPlayMode(PLAYMODE_PLAYING); + } + else + { + inPlayer->SetPlayMode(PLAYMODE_AWAITINGREINFORCEMENT); + inPlayer->SendMessage(kJoinSoon); + } + + if(inTeamToJoin != TEAM_IND) + { + UTIL_LogPrintf( "%s joined team \"%s\"\n", GetLogStringForPlayer( inPlayer->edict() ).c_str(), AvHSUGetTeamName(inPlayer->pev->team) ); + if(theServerPlayerData) + theServerPlayerData->SetHasJoinedTeam(true); + } +} + +// This is called before any entities are spawned, every time the map changes, including the first time +void AvHGamerules::PreWorldPrecacheReset() +{ + gParticleTemplateList.Clear(); + AvHMP3Audio::ClearSoundNameList(); + this->PreWorldPrecacheInitParticles(); +} + +void AvHGamerules::ProcessRespawnCostForPlayer(AvHPlayer* inPlayer) +{ + AvHTeam* theTeam = inPlayer->GetTeamPointer(); + if(theTeam) + { + AvHClassType theTeamType = theTeam->GetTeamType(); + if(theTeamType == AVH_CLASS_TYPE_ALIEN) + { + } + else if(theTeamType == AVH_CLASS_TYPE_MARINE) + { + this->MarkDramaticEvent(kReinforcementsPriority, inPlayer); + } + } + // No team? Player died in the ready room...fix? +} + +void AvHGamerules::ProcessTeamUpgrade(AvHMessageID inUpgrade, AvHTeamNumber inNumber, int inEntity, bool inGive) +{ + AvHTeam* theTeam = this->GetTeam(inNumber); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) + { + // Add or remove the upgrade from every entity that's on the team + FOR_ALL_BASEENTITIES(); + if(theBaseEntity->pev->team == inNumber) + { + AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); + if(thePlayer) + { + thePlayer->GiveTeamUpgrade(inUpgrade, inGive); + } + else + { + ProcessGenericUpgrade(theBaseEntity->pev->iuser4, inUpgrade, inGive); + } + } + END_FOR_ALL_BASEENTITIES(); + + // Store it in the team too, so new players and entities can be initialized properly + theTeam->ProcessTeamUpgrade(inUpgrade, inGive); + } +} + +bool AvHGamerules::ReadyToStartCountdown() +{ + bool theReadyToStartCountdown = false; + + bool theAtLeastOneOnTeamA = this->mTeamA.GetPlayerCount() > 0; + bool theAtLeastOneOnTeamB = this->mTeamB.GetPlayerCount() > 0; + + if(this->GetCheatsEnabled()) + { + if(theAtLeastOneOnTeamA || theAtLeastOneOnTeamB) + { + theReadyToStartCountdown = true; + } + } + else if(theAtLeastOneOnTeamA && theAtLeastOneOnTeamB) + { + //need to ready up for tournament mode; otherwise just need players on each team + if( !this->GetIsTournamentMode() || ( this->mTeamA.GetIsReady() && this->mTeamB.GetIsReady() ) ) + { + theReadyToStartCountdown = true; + } + } + + return theReadyToStartCountdown; +} + +void AvHGamerules::RecalculateHandicap() +{ + const float kHandicapMax = 100.0f; + + // Teams can enforce their own handicaps if they want (cap to valid values) + float handicaps[4]; + handicaps[0] = max(min(kHandicapMax, avh_team1damagepercent.value), 0.0f); + handicaps[1] = max(min(kHandicapMax, avh_team2damagepercent.value), 0.0f); + handicaps[2] = max(min(kHandicapMax, avh_team3damagepercent.value), 0.0f); + handicaps[3] = max(min(kHandicapMax, avh_team4damagepercent.value), 0.0f); + + // Set handicap scalars + this->mTeamA.SetHandicap(handicaps[this->mTeamA.GetTeamNumber()-1]/kHandicapMax); + this->mTeamB.SetHandicap(handicaps[this->mTeamB.GetTeamNumber()-1]/kHandicapMax); + + avh_team1damagepercent.value = handicaps[0]; + avh_team2damagepercent.value = handicaps[1]; + avh_team3damagepercent.value = handicaps[2]; + avh_team4damagepercent.value = handicaps[3]; +} + +// Called when dedicated server exits +void AvHGamerules::ServerExit() +{ + AvHNexus::shutdown(); +} + +void AvHGamerules::VoteMap(int inPlayerIndex, int inMapIndex) +{ + // check to remove votemap spam + map< int, float >::iterator thePlayerVoteTime = this->mPlayersVoteTime.find(inPlayerIndex); + + if (thePlayerVoteTime != this->mPlayersVoteTime.end()) + { + if (thePlayerVoteTime->second + 3.0f > gpGlobals->time) + return; + + thePlayerVoteTime->second = gpGlobals->time; + } + else + { + this->mPlayersVoteTime.insert(pair < int, float >(inPlayerIndex, gpGlobals->time)); + } + + if(avh_mapvoteratio.value != -1) + { + if(this->mMapVoteList.size() == 0) + { + this->InitializeMapVoteList(); + } + + // If this is a valid map + if((inMapIndex > 0) && (inMapIndex <= (signed)this->mMapVoteList.size())) + { + + PlayerMapVoteListType::iterator theMappedPlayer = this->mPlayersVoted.find(inPlayerIndex); + + // Increment votes for map + MapVoteListType::iterator theIter = (MapVoteListType::iterator)&this->mMapVoteList[inMapIndex-1]; + int theVotes = ++theIter->second; + + // If player has already voted, decrement previous map and update which map the player has voted + if(theMappedPlayer != this->mPlayersVoted.end()) { + ((MapVoteListType::iterator)&this->mMapVoteList[theMappedPlayer->second - 1])->second--; + theMappedPlayer->second = inMapIndex; + } + // else, remember the "new" player's vote + else + { + this->mPlayersVoted.insert(pair < int, int >(inPlayerIndex, inMapIndex)); + } + + // Tell everyone + CBaseEntity* theVotingPlayer = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex)); + ASSERT(theVotingPlayer); + char* theMessage = UTIL_VarArgs("%s executed votemap %d (%s %d/%d)\n", STRING(theVotingPlayer->pev->netname), inMapIndex, theIter->first.c_str(), theIter->second, this->GetVotesNeededForMapChange()); + UTIL_ClientPrintAll(HUD_PRINTTALK, theMessage); + UTIL_LogPrintf( "%s executed votemap %d (%s %d/%d)\n", GetLogStringForPlayer( theVotingPlayer->edict() ).c_str(), inMapIndex, theIter->first.c_str(), theIter->second, this->GetVotesNeededForMapChange()); + + // Does this map enough votes to change? + if(theVotes >= this->GetVotesNeededForMapChange()) + { + // Changelevel now + char theLevelName[256]; + strcpy(theLevelName, theIter->first.c_str()); + CHANGE_LEVEL(theLevelName, NULL); + } + + } + } +} + +// remove a player's vote from the votemap list and trigger a mapchange check +void AvHGamerules::RemovePlayerFromVotemap(int inPlayerIndex) +{ + PlayerMapVoteListType::iterator theMappedPlayer = this->mPlayersVoted.find(inPlayerIndex); + + // If player has voted, decrement the map voted for + if(theMappedPlayer != this->mPlayersVoted.end()) { + ((MapVoteListType::iterator)&this->mMapVoteList[theMappedPlayer->second - 1])->second--; + this->mPlayersVoted.erase(inPlayerIndex); + } + + // trigger mapchange check + int theVotesNeeded = this->GetVotesNeededForMapChange(); + for(MapVoteListType::iterator theMapIterator = this->mMapVoteList.begin(); + theMapIterator != this->mMapVoteList.end(); + theMapIterator++) + { + if (theMapIterator->second >= theVotesNeeded) + { + // Changelevel now + char theLevelName[256]; + strcpy(theLevelName, theMapIterator->first.c_str()); + CHANGE_LEVEL(theLevelName, NULL); + break; + } + } +} + +bool AvHGamerules::GetMapVoteStrings(StringList& outMapVoteList) +{ + bool theSuccess = false; + + if(avh_mapvoteratio.value != -1) + { + if(this->mMapVoteList.size() == 0) + { + this->InitializeMapVoteList(); + } + + int theMapIndex = 1; + outMapVoteList.clear(); + + for(MapVoteListType::iterator theIter = this->mMapVoteList.begin(); theIter != this->mMapVoteList.end(); theIter++) + { + string theOutputString = MakeStringFromInt(theMapIndex) + ") " + theIter->first + " (" + MakeStringFromInt(theIter->second) + "/" + MakeStringFromInt(this->GetVotesNeededForMapChange()) + ")\n"; + outMapVoteList.push_back(theOutputString); + theMapIndex++; + } + + theSuccess = true; + } + + return theSuccess; +} + +int AvHGamerules::GetVotesNeededForMapChange() const +{ + int theNumVotes = -1; + + float theMapVoteRatio = avh_mapvoteratio.value; + if(theMapVoteRatio > 0) + { + theMapVoteRatio = min(max(0.0f, theMapVoteRatio), 1.0f); + theNumVotes = max(theMapVoteRatio*this->GetNumberOfPlayers(true), 1.0f); + } + + return theNumVotes; +} + +void AvHGamerules::RegisterSpawnPoint(const string& inClassName, const Vector& inOrigin, const Vector& inAngles, const AvHTeamNumber& inTeamNumber) +{ + AvHSpawn theSpawnPoint(inClassName, inOrigin, inAngles, inTeamNumber); + this->mSpawnList.push_back(theSpawnPoint); +} + +void AvHGamerules::RespawnPlayer(AvHPlayer* inPlayer) +{ + bool thePlayerCanRespawn = this->FPlayerCanRespawn(inPlayer); + ASSERT(thePlayerCanRespawn); + + this->ProcessRespawnCostForPlayer(inPlayer); + + // Clear alien upgrades, they must be repurchased. Soldier upgrades stick. + AvHTeam* theTeam = inPlayer->GetTeamPointer(); + if(theTeam) + { + if(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + inPlayer->pev->iuser4 = 0; + } + + respawn(inPlayer->pev, !(inPlayer->m_afPhysicsFlags & PFLAG_OBSERVER));// don't copy a corpse if we're in deathcam. + inPlayer->pev->nextthink = -1; + + // Bring us back in the game + if(theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) + { + inPlayer->SetUser3(AVH_USER3_MARINE_PLAYER, true); + } + else if(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + inPlayer->SetUser3(AVH_USER3_ALIEN_PLAYER1, true); + } + } +} + +void AvHGamerules::ResetGame(bool inPreserveTeams) +{ + // Reset game rules + this->mFirstUpdate = true; + this->mPreserveTeams = inPreserveTeams; + gSvCheatsLastUpdateTime = -1.0f; + this->mHasPlayersToReset = false; + this->mLastPlayerResetTime = -1.0f; +} + +void AvHGamerules::RecalculateMapMode( void ) +{ + // Recalculate map mode we're in. MAP_MODE_UNDEFINED means just try to spawn somewhere. + + // Check for AVH entities + if((this->GetEntityExists(kesReadyRoomStart)) && (this->GetEntityExists(kesTeamStart))) + { + const char* theCStrLevelName = STRING(gpGlobals->mapname); + if(theCStrLevelName && (strlen(theCStrLevelName) > 3)) + { + if(!strnicmp(theCStrLevelName, "ns_", 3)) + { + if(this->GetEntityExists(kesTeamHive)) + { + if(this->GetEntityExists(kwsTeamCommand)) + { + this->mMapMode = MAP_MODE_NS; + this->PerformMapValidityCheck(); + } + } + } + else if(!strnicmp(theCStrLevelName, "co_", 3)) + { + this->mMapMode = MAP_MODE_CO; + } + } + } + // Check for CS entities + else if(this->GetEntityExists(kesTerroristStart) && this->GetEntityExists(kesCounterTerroristStart)) + { + this->mMapMode = MAP_MODE_CS; + } + else if(this->GetEntityExists(kesDeathMatchStart)) + { + this->mMapMode = MAP_MODE_DM; + } +} + +bool AvHGamerules::RoamingAllowedForPlayer(CBasePlayer* inPlayer) const +{ + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + bool theRoamingAllowed = false; + + if(thePlayer) + { + if((thePlayer->GetPlayMode() == PLAYMODE_OBSERVER)) + { + theRoamingAllowed = true; + } + + const AvHTeam* theTeam = this->GetTeam(thePlayer->GetTeam()); + if(theTeam) + { + if(theTeam->GetPlayerCount() == 0) + { + theRoamingAllowed = true; + } + } + else + { + AvHTeamNumber teamA = this->mTeamA.GetTeamNumber(); + AvHTeamNumber teamB = this->mTeamB.GetTeamNumber(); + theTeam = this->GetTeam( (thePlayer->GetTeam() == teamA ? teamB : teamA) ); + if(theTeam) + { + if(theTeam->GetPlayerCount() == 0) + { + theRoamingAllowed = true; + } + } + } + } + + return theRoamingAllowed; +} + +BOOL AvHGamerules::FShouldSwitchWeapon(CBasePlayer* inPlayer, CBasePlayerItem* inWeapon) +{ + BOOL theShouldSwitch = CHalfLifeTeamplay::FShouldSwitchWeapon(inPlayer, inWeapon); + if(theShouldSwitch) + { + AvHBasePlayerWeapon* theWeapon = dynamic_cast(inWeapon); + if(theWeapon && !theWeapon->GetIsCapableOfFiring()) + { + theShouldSwitch = false; + } + } + return theShouldSwitch; +} + +void AvHGamerules::MarkDramaticEvent(int inPriority, CBaseEntity* inPrimaryEntity, bool inDramatic, CBaseEntity* inSecondaryEntity) const +{ + ASSERT(inPrimaryEntity); + short inPrimaryEntityIndex = ENTINDEX(inPrimaryEntity->edict()); + short inSecondaryEntityIndex = !inSecondaryEntity ? 0 : ENTINDEX(inSecondaryEntity->edict()); + + this->MarkDramaticEvent(inPriority, inPrimaryEntityIndex, inDramatic, inSecondaryEntityIndex); +} + +void AvHGamerules::MarkDramaticEvent(int inPriority, short inPrimaryEntityIndex, bool inDramatic, short inSecondaryEntityIndex) const +{ + ASSERT(inPriority >= kMinDramaticPriority); + ASSERT(inPriority <= kMaxDramaticPriority); + + MESSAGE_BEGIN(MSG_SPEC, SVC_DIRECTOR); + WRITE_BYTE(DIRECTOR_MESSAGE_SIZE); + WRITE_BYTE(DRC_CMD_EVENT); + WRITE_SHORT(inPrimaryEntityIndex); + WRITE_SHORT(inSecondaryEntityIndex); + + int thePriority = inPriority; + if(inDramatic) + { + thePriority |= DRC_FLAG_DRAMATIC; + } + + WRITE_LONG(thePriority); + MESSAGE_END(); +} + +void AvHGamerules::MarkDramaticEvent(int inPriority, short inPrimaryEntityIndex, bool inDramatic, entvars_t* inSecondaryEntity) const +{ + // Changed by mmcguire. Sometimes the attacker is NULL (e.g. when a + // player creates a turret and then leaves the game), so we need to + // handle that case. + short secondaryEntityIndex = 0; + + if (inSecondaryEntity != NULL) + { + secondaryEntityIndex = OFFSET(inSecondaryEntity); + } + + this->MarkDramaticEvent(inPriority, inPrimaryEntityIndex, secondaryEntityIndex); +} + +// Resets players in chunks of 6 and 6 +void AvHGamerules::ResetPlayers() +{ + const int maxReset = 6; + int numReset = 0; + + FOR_ALL_BASEENTITIES(); + + // Reset the players + // Reset only players that have made it to the readyroom and have been on either team + AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); + if(thePlayer && (thePlayer->GetPlayMode() == PLAYMODE_READYROOM) && (thePlayer->GetHasSeenATeam())) + { + int theUser3 = thePlayer->pev->iuser3; + int theUser4 = thePlayer->pev->iuser4; + int thePlayMode = thePlayer->pev->playerclass; + int thePlayerTeam = thePlayer->pev->team; + int theSolidType = thePlayer->pev->solid; + + thePlayer->ResetEntity(); + + thePlayer->pev->iuser3 = theUser3; + thePlayer->pev->iuser4 = theUser4; + thePlayer->pev->playerclass = thePlayMode; + thePlayer->pev->team = thePlayerTeam; + thePlayer->pev->solid = theSolidType; + + if (numReset++ >= maxReset) + { + this->mHasPlayersToReset = true; + return; + } + } + + END_FOR_ALL_BASEENTITIES(); + + this->mHasPlayersToReset = false; +} + +void AvHGamerules::ResetEntities() +{ + // Now reset all the world entities and mark useable ones with AVH_USER3_USEABLE + //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::ResetEntities\n"); + + FOR_ALL_BASEENTITIES(); + + // Reset non-player entities + AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); + if(thePlayer && (thePlayer->GetPlayMode() == PLAYMODE_READYROOM)) // && (thePlayer->GetHasSeenATeam())) + { + int theUser3 = thePlayer->pev->iuser3; + int theUser4 = thePlayer->pev->iuser4; + int thePlayMode = thePlayer->pev->playerclass; + int thePlayerTeam = thePlayer->pev->team; + int theSolidType = thePlayer->pev->solid; + + thePlayer->ResetEntity(); + + thePlayer->pev->iuser3 = theUser3; + thePlayer->pev->iuser4 = theUser4; + thePlayer->pev->playerclass = thePlayMode; + thePlayer->pev->team = thePlayerTeam; + thePlayer->pev->solid = theSolidType; + } + else + { + theBaseEntity->ResetEntity(); + } + + // Don't mark commander stations as useable in this case + AvHCommandStation* theCommandStation = dynamic_cast(theBaseEntity); + if(!theCommandStation) + { + int theObjectCaps = theBaseEntity->ObjectCaps(); + if(theObjectCaps & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE)) + { + // After playing once, this is no longer zero + if(theBaseEntity->pev->iuser3 == 0) + { + theBaseEntity->pev->iuser3 = AVH_USER3_USEABLE; + + // Now also mark the target entity as useable! + if (!FStringNull(theBaseEntity->pev->target)) + { + CBaseEntity* theTarget = NULL; + + while(theTarget = UTIL_FindEntityByTargetname(theTarget, STRING(theBaseEntity->pev->target))) + { + theTarget->pev->iuser3 = AVH_USER3_USEABLE; + } + } + } + } + } + END_FOR_ALL_BASEENTITIES(); + + this->mHasPlayersToReset = true; +} + +void AvHGamerules::InternalResetGameRules() +{ + if(AvHNexus::isRecordingGame()) + { + AvHNexus::cancelGame(); + } + this->mGameStarted = false; + this->mLastJoinMessage = 0.0f; + this->mTimeCountDownStarted = 0; + this->mTimeGameStarted = -1; + this->mTimeOfLastGameTimeUpdate = -1; + this->mTimeOfLastHLTVProxyUpdate = -1; + this->mTimeOfForcedLastHLTVProxyUpdate = -1; + this->mTimeOfLastHLTVParticleTemplateSending = -1; + this->mHLTVNumParticleTemplatesSent=0; + this->mHLTVCurrentPlayer=1; + this->mTimeSentCountdown = 0; + this->mTimeLastWontStartMessageSent = 0; + this->mStartedCountdown = false; + this->mSentCountdownMessage = false; + this->mTimeWorldReset = gpGlobals->time; + this->mCombatAttackingTeamNumber = TEAM_IND; + gSvCheatsLastUpdateTime = -1.0f; + if(this->mLastMapChange == -1) + { + this->mLastMapChange = gpGlobals->time; + } + + this->mVictoryTeam = TEAM_IND; + this->mVictoryDraw = false; + this->mVictoryTime = 0; + this->mCheats.clear(); + this->mLastParticleUpdate = -1; + this->mLastNetworkUpdate = -1; + this->mLastWorldEntityUpdate = -1; + this->mLastCloakableUpdate = -1; + this->mLastVictoryUpdate = -1; +// : 0001073 +#ifdef USE_OLDAUTH //under UPP, we don't want to reestablish a connection with each new game + this->mUpdatedUplink = false; +#endif + +} + +void AvHGamerules::CopyDataToSpawnEntity(const AvHSpawn& inSpawnEntity) const +{ + VectorCopy(inSpawnEntity.GetOrigin(), this->mSpawnEntity->pev->origin); + VectorCopy(inSpawnEntity.GetAngles(), this->mSpawnEntity->pev->angles); + VectorCopy(inSpawnEntity.GetAngles(), this->mSpawnEntity->pev->v_angle); + this->mSpawnEntity->pev->team = inSpawnEntity.GetTeamNumber(); +} + +// Look for spawns in radius +CBaseEntity* AvHGamerules::GetRandomHiveSpawnPoint(CBaseEntity* inPlayer, const Vector& inOrigin, float inMaxDistance) const +{ + CBaseEntity* theSpawnPoint = NULL; + + typedef vector EntityIndexListType; + EntityIndexListType theSpawnIndexList; + + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + ASSERT(thePlayer); + + // Loop through all spawns, looking for valid spawns within range + int theIndex = 0; + SpawnListType::const_iterator theIter; + for(theIter = this->mSpawnList.begin(); theIter != this->mSpawnList.end(); theIter++, theIndex++) + { + // Found one in range? Add it's index to the list + if(theIter->GetTeamNumber() == AvHTeamNumber(thePlayer->pev->team)) + { + Vector theSpawnOrigin = theIter->GetOrigin(); + float theDistanceToSpawn = VectorDistance(theSpawnOrigin, inOrigin); + if(theDistanceToSpawn <= inMaxDistance) + { + int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(thePlayer->pev->iuser3)); + theSpawnOrigin.z += theOffset; + + if(AvHSUGetIsEnoughRoomForHull(theSpawnOrigin, AvHMUGetHull(false, inPlayer->pev->iuser3), inPlayer->edict())) + { + theSpawnIndexList.push_back(theIndex); + } + } + } + } + + // If index list has more then one entry + int theNumValidSpawns = theSpawnIndexList.size(); + if(theNumValidSpawns > 0) + { + // Pick random index + int theRandomIndex = RANDOM_LONG(0, theNumValidSpawns - 1); + ASSERT(theRandomIndex >= 0); + ASSERT(theRandomIndex < theNumValidSpawns); + ASSERT(this->mSpawnEntity); + + // Copy data into spawn entity + int theSpawnIndex = theSpawnIndexList[theRandomIndex]; + const AvHSpawn& theSpawn = this->mSpawnList[theSpawnIndex]; + this->CopyDataToSpawnEntity(theSpawn); + + // Return it + theSpawnPoint = this->mSpawnEntity; + } + + return theSpawnPoint; +} +bool AvHGamerules::CanPlayerBeacon(CBaseEntity *inPlayer) +{ + bool result=true; + SpawnListType::const_iterator theSpawnIter; + + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + ASSERT(thePlayer); + AvHTeamNumber thePlayerTeamNumber = thePlayer->GetTeam(); + for(theSpawnIter = this->mSpawnList.begin(); theSpawnIter != this->mSpawnList.end(); theSpawnIter++) + { + string theClassName = theSpawnIter->GetClassName(); + if(theClassName == kesTeamStart) + { + if(theSpawnIter->GetTeamNumber() == thePlayerTeamNumber && + VectorDistance(theSpawnIter->GetOrigin(), inPlayer->pev->origin) < BALANCE_VAR(kDistressBeaconRange)) + { + result=false; + } + } + } + return result; +} + +// : 0001073 +#ifdef USE_OLDAUTH +unsigned int gTimeLastUpdatedUplink = 0; +void AvHGamerules::UpdateUplink() +{ + #ifdef AVH_SECURE_PRERELEASE_BUILD + avh_uplink.value = 1; + #endif + + // If authentication is enabled + if(!this->mUpdatedUplink && (avh_uplink.value > 0)) + { + // Only allow it once every day -> 500 servers == num queries per hour = 500*75k = 1,500k per hour -> just over a 1 gig a month + unsigned int theCurrentSystemTime = AvHSUTimeGetTime(); + int theUpdateUplinkInterval = (60*60*1000)*24; + + #ifdef AVH_SECURE_PRERELEASE_BUILD + theUpdateUplinkInterval = (60*60*1000)/30; // every 30 minutes or so while testing + #endif + + #ifdef DEBUG + theUpdateUplinkInterval = 0; + #endif + + if((gTimeLastUpdatedUplink == 0) || (theCurrentSystemTime > (gTimeLastUpdatedUplink + theUpdateUplinkInterval))) + { + // Initialize it + ALERT(at_logged, "Contacting www.natural-selection.org...\n"); + this->InitializeAuthentication(); + //this->DisplayVersioning(); + } + else + { + //ALERT(at_logged, "You must wait longer before uplinking again.\n"); + } + } + + // If it just turned off, clear auth masks + if(this->mUpdatedUplink && !avh_uplink.value) + { + gAuthMaskList.clear(); + ALERT(at_logged, "Authentication disabled.\n"); + } + + this->mUpdatedUplink = avh_uplink.value; +} +#endif + + +edict_t* AvHGamerules::SelectSpawnPoint(CBaseEntity* inPlayer, const string& inSpawnEntityName) const +{ + bool theDone = false; + edict_t* theResult = NULL; + CBaseEntity* theSpot = NULL; + + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + ASSERT(thePlayer); + + // Handle alien spawns differently than others. + AvHTeamNumber theTeamNumber = thePlayer->GetTeam(); + const AvHTeam* theTeam = this->GetTeam(theTeamNumber); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && (inSpawnEntityName == kesTeamStart)) + { + // Get a random active hive + AvHHive* theHive = AvHSUGetRandomActiveHive(theTeamNumber); + if(theHive) + { + theSpot = this->GetRandomHiveSpawnPoint(inPlayer, theHive->pev->origin, theHive->GetMaxSpawnDistance()); + } + + // If we still didn't find one and cheats are on, try a spawn not near a hive + if(!theSpot && this->GetCheatsEnabled()) + { + theSpot = this->SelectRandomSpawn(inPlayer, inSpawnEntityName); + } + } + else + { + theSpot = this->SelectRandomSpawn(inPlayer, inSpawnEntityName); + } + + if(!FNullEnt(theSpot)) + { + int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(thePlayer->pev->iuser3)); + Vector theSpawnOrigin = theSpot->pev->origin; + theSpawnOrigin.z += theOffset; + + if(AvHSUGetIsEnoughRoomForHull(theSpawnOrigin, AvHMUGetHull(false, inPlayer->pev->iuser3), inPlayer->edict())) + { + theResult = theSpot->edict(); + theDone = true; + } + } + + if(!theDone) + { + char theErrorString[256]; + sprintf(theErrorString, "AvHGamerules::SelectSpawnPoint(): all spawns full\n"); + ALERT(at_logged, theErrorString); + theResult = NULL; + } + + return theResult; +} + +void AvHGamerules::UpdateHLTVProxy() +{ + // Check if HLTV proxy is connected to server + bool theHLTVProxyConnected = false; + + // Every so often, send out team for all players (needed for late joiners) + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( i ); + if ( plr && (plr->pev->flags & FL_PROXY)) + { + theHLTVProxyConnected = true; + break; + } + } + + // Update proxy if connected + if(theHLTVProxyConnected) + { + + + const float kParticleTemplateRate = 0.1f; + if(gParticleTemplateList.GetCreatedTemplates()) + { + // Make sure client clears out all particle systems first + int theNumberTemplates = gParticleTemplateList.GetNumberTemplates(); + if(theNumberTemplates > this->mHLTVNumParticleTemplatesSent) + { + if((this->mTimeOfLastHLTVParticleTemplateSending == -1) || (gpGlobals->time > this->mTimeOfLastHLTVParticleTemplateSending + kParticleTemplateRate)) + { + AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(this->mHLTVNumParticleTemplatesSent); + ASSERT(theTemplate); + NetMsg_SetParticleTemplate( NULL, this->mHLTVNumParticleTemplatesSent, *theTemplate ); + this->mHLTVNumParticleTemplatesSent++; + this->mTimeOfLastHLTVParticleTemplateSending = gpGlobals->time; + } + } + } + + // Send out team for all players (needed for late joiners) + for (int i = this->mHLTVCurrentPlayer; i <= gpGlobals->maxClients; i++ ) + { + this->mHLTVCurrentPlayer++; + CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( i ); + if ( plr && GetGameRules()->IsValidTeam( plr->TeamID() ) ) + { + NetMsgSpec_TeamInfo( plr->entindex(), plr->TeamID() ); + ScoreInfo info; + memset(&info, 0, sizeof(info)); + AvHSUFillScoreInfo(info, dynamic_cast(plr)); + NetMsgSpec_ScoreInfo( info ); + break; + } + } + + + const float kHLTVProxyUpdateTime = 6.0f; + const float kForcedHLTVProxyUpdateTime = 30.0f; + + if((this->mTimeOfLastHLTVProxyUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastHLTVProxyUpdate + kHLTVProxyUpdateTime))) + { + if((this->mTimeOfForcedLastHLTVProxyUpdate == -1) || (gpGlobals->time > (this->mTimeOfForcedLastHLTVProxyUpdate + kForcedHLTVProxyUpdateTime))) { + + if(gParticleTemplateList.GetCreatedTemplates()) + { + int theNumberTemplates = gParticleTemplateList.GetNumberTemplates(); + if(theNumberTemplates <= this->mHLTVNumParticleTemplatesSent) { + // restart the hltv particle templates + this->mHLTVNumParticleTemplatesSent=0; + this->mTimeOfLastHLTVParticleTemplateSending = -1; + // Make sure client clears out all particle systems first + //NetMsg_DelParts(NULL); + } + } + // restart the scoreboard hltv updates + if ( this->mHLTVCurrentPlayer > gpGlobals->maxClients ) this->mHLTVCurrentPlayer=1; + + // restart the enthier hltv updates + mHLTVEntityHierarchy.Clear(); + NetMsg_DelEntityHierarchy(NULL); + + // Send down map extents so players can start computing it + // Cache this so it isn't computed every round, only the when a player connects or a new map starts? + const char* theCStrLevelName = STRING(gpGlobals->mapname); + const AvHMapExtents& theMapExtents = GetGameRules()->GetMapExtents(); + float mins[3] = { theMapExtents.GetMinMapX(), theMapExtents.GetMinMapY(), theMapExtents.GetMinViewHeight() }; + float maxs[3] = { theMapExtents.GetMaxMapX(), theMapExtents.GetMaxMapY(), theMapExtents.GetMaxViewHeight() }; + NetMsg_SetupMap_Extents( NULL, string( theCStrLevelName ), mins, maxs, theMapExtents.GetDrawMapBG() ); + + this->mTimeOfForcedLastHLTVProxyUpdate=gpGlobals->time; + } + else { + AvHEntityHierarchy& theEntityList = GetGameRules()->GetEntityHierarchy(TEAM_SPECT); + if ( theEntityList.SendToNetworkStream(mHLTVEntityHierarchy, NULL, true) ) + { + mHLTVEntityHierarchy = theEntityList; + } + + // Resend the gammaramp + NetMsgSpec_SetGammaRamp( GetGameRules()->GetMapGamma() ); + + HiveInfoListType theTeamHiveInfo = this->mTeamB.GetHiveInfoList(); + const HiveInfoListType tmp; + NetMsg_AlienInfo_Hives( NULL, theTeamHiveInfo, tmp ); + + this->mTimeOfLastHLTVProxyUpdate = gpGlobals->time; + } + } + } +} + +void AvHGamerules::UpdatePlaytesting() +{ + if(this->GetGameStarted() && !this->GetIsCombatMode()) + { + const int theActiveNodesMessageUpdateTime = BALANCE_VAR(kActiveNodesMessageUpdateTime); + if((theActiveNodesMessageUpdateTime > 0) && !this->GetIsTournamentMode()) + { + if((this->mTimeOfLastPlaytestUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastPlaytestUpdate + theActiveNodesMessageUpdateTime))) + { + int theTeamATowers = 0; + int theTeamBTowers = 0; + + FOR_ALL_BASEENTITIES(); + AvHResourceTower* theResourceTower = dynamic_cast(theBaseEntity); + if(theResourceTower && theResourceTower->GetIsActive()) + { + if(theResourceTower->pev->team == this->mTeamA.GetTeamNumber()) + { + theTeamATowers++; + } + else if(theResourceTower->pev->team == this->mTeamB.GetTeamNumber()) + { + theTeamBTowers++; + } + } + END_FOR_ALL_BASEENTITIES(); + + // Count how many active towers each team has, and tell the world + char* theMessage = UTIL_VarArgs( "Active resource nodes: TeamA: %d TeamB: %d\n", theTeamATowers, theTeamBTowers); + UTIL_ClientPrintAll(HUD_PRINTTALK, theMessage); + UTIL_LogPrintf(theMessage); + + this->mTimeOfLastPlaytestUpdate = gpGlobals->time; + } + } + } +} + +edict_t* AvHGamerules::SelectSpawnPoint(CBaseEntity* inPlayer) const +{ + const char* theSpawnName = kesReadyRoomStart; + edict_t* theResult = NULL; + + // Get name of spawn entity from player + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + if(thePlayer) + { + // Set name of spawn point accordingly + theSpawnName = this->GetSpawnEntityName(thePlayer); + } + + theResult = this->SelectSpawnPoint(inPlayer, theSpawnName); + + return theResult; +} + +const char* AvHGamerules::SetDefaultPlayerTeam(CBasePlayer *pPlayer) +{ + // By default, do nothing. We start in the ready room, we don't join a team + return NULL; +} + +CBaseEntity* AvHGamerules::SelectRandomSpawn(CBaseEntity* inPlayer, const string& inSpawnName) const +{ + CBaseEntity* theSpawn = NULL; + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + ASSERT(thePlayer); + AvHTeamNumber thePlayerTeamNumber = thePlayer->GetTeam(); + + // Count how many spawns of this type + int theNumSpawns = 0; + + SpawnListType::const_iterator theSpawnIter; + + for(theSpawnIter = this->mSpawnList.begin(); theSpawnIter != this->mSpawnList.end(); theSpawnIter++) + { + string theClassName = theSpawnIter->GetClassName(); + if(theClassName == inSpawnName) + { + if(theSpawnIter->GetTeamNumber() == thePlayerTeamNumber) + { + theNumSpawns++; + } + } + } + + bool theSuccess = false; + int theSpawnListSize = this->mSpawnList.size(); + if(theSpawnListSize > 0) + { + int theNumTries = 0; + + do + { + int theOffset = RANDOM_LONG(0, theSpawnListSize-1); + + //theSpawnIter = &this->mSpawnList[theCurrentOffset]; + const AvHSpawn& theSpawn = this->mSpawnList[theOffset]; + if(theSpawn.GetClassName() == inSpawnName) + { + if(theSpawn.GetTeamNumber() == thePlayerTeamNumber) + { + // Copy data into current spawn point + ASSERT(this->mSpawnEntity); + + this->CopyDataToSpawnEntity(theSpawn); + + if(IsSpawnPointValid(inPlayer, this->mSpawnEntity)) + { + theSuccess = true; + } + } + } + + // Put a limit to the number of tries, to avoid an infinite loop when mapper didn't create the right type of spawns + } while((theNumTries++ < 100) && !theSuccess); + } + + if(theSuccess) + { + theSpawn = this->mSpawnEntity; + } + else + { + theSpawn = NULL; + } + + return theSpawn; +} + +void AvHGamerules::SetGameStarted(bool inGameStarted) +{ + if(!this->mGameStarted && inGameStarted) + { + FireTargets(ktGameStartedStatus, NULL, NULL, USE_TOGGLE, 0.0f); + AvHNexus::startGame(); + } + this->mGameStarted = inGameStarted; + this->mTimeGameStarted = gpGlobals->time; + + // Choose a random defending team in Combat + if(this->GetIsCombatMode()) + { + int theTeamIndex = 1;// + (rand()%2); + AvHTeamNumber theAttackingTeamNumber = AvHTeamNumber(theTeamIndex); + this->mCombatAttackingTeamNumber = theAttackingTeamNumber; + } + + this->mTeamA.SetGameStarted(inGameStarted); + this->mTeamB.SetGameStarted(inGameStarted); +} + +void AvHGamerules::SendMOTDToClient( edict_t *client ) +{ + // read from the MOTD.txt file + int length, char_count = 0; + char* pFileList; + char* aFileList = pFileList = (char*)LOAD_FILE_FOR_ME( kMOTDName, &length ); + + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(client)); + ASSERT(thePlayer); + + // send the server name + NetMsg_ServerName( thePlayer->pev, string( CVAR_GET_STRING("hostname") ) ); + + UTIL_ShowMessage(pFileList, thePlayer); + + FREE_FILE( aFileList ); +} + +int AvHGamerules::GetNumberOfPlayers(bool inPlayingGame) const +{ + int theNumberOfPlayers = 0; + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + + // GetIsRelevant()? + if(inPlayingGame && (theEntity->GetPlayMode() > 0)) + { + if(UTIL_IsValidEntity(theEntity->edict())) + { + theNumberOfPlayers++; + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + + return theNumberOfPlayers; +} + +char gLastKnownMapName[256] = ""; + +void AvHGamerules::Think(void) +{ + PROFILE_START() + + float theTime = gpGlobals->time; + float theTimePassed = gpGlobals->frametime; + + if(this->mFirstUpdate) + { + const char* theCStrLevelName = STRING(gpGlobals->mapname); + bool theNewMap = !gLastKnownMapName || !theCStrLevelName || strcmp(theCStrLevelName, gLastKnownMapName); + + PROFILE_START(); + this->PostWorldPrecacheReset(theNewMap); + PROFILE_END(kUpdatePrecacheResetProfile); + + strcpy(gLastKnownMapName, theCStrLevelName); + + // Tell all HUDs to reset + NetMsg_GameStatus_State( kGameStatusReset, this->mMapMode ); + + this->mFirstUpdate = false; + } + +// ResetPlayer throttling, commented out for now +// const float playerResetDelay = 0.3f; +// if(this->mHasPlayersToReset && (this->mLastPlayerResetTime + playerResetDelay < theTime) ) +// { +// this->ResetPlayers(); +// this->mLastPlayerResetTime = theTime; +// } + + // Handle queued network messages + #ifdef USE_NETWORK_METERING + const float kNetworkUpdateInterval = .1f; + if((this->mLastNetworkUpdate == -1) || (theTime > (this->mLastNetworkUpdate + kNetworkUpdateInterval))) + { + PROFILE_START(); + int theBytesPerSecond = 100000;//(int)(avh_networkmeterrate.value); + NetworkMeter::Instance()->SetBufferAmount(theBytesPerSecond); + NetworkMeter::Instance()->ProcessQueuedMessages(); + PROFILE_END(kUpdateNetworkMeterProfile); + } + #endif + + #ifdef PROFILE_BUILD + kProfileRunConfig = (int)(avh_performance.value); + #endif + + PROFILE_START(); + AvHNexus::processResponses(); + this->RecalculateHandicap(); +// : 0001073 +#ifdef USE_OLDAUTH + this->UpdateUplink(); +#endif + this->UpdatePlaytesting(); + this->UpdateHLTVProxy(); + PROFILE_END(kUpdateMisc); + + const float kWorldEntityUpdateInterval = 1.0f; + if((this->mLastWorldEntityUpdate == -1) || (theTime > (this->mLastWorldEntityUpdate + kWorldEntityUpdateInterval))) + { + if(GET_RUN_CODE(1)) + { + // Update world entities + PROFILE_START() + this->UpdateWorldEntities(); + this->mLastWorldEntityUpdate = theTime; + PROFILE_END(kUpdateWorldEntitiesProfile) + } + + // Don't need to update cheats every tick, as they can be expensive + this->UpdateCheats(); + } + + this->mMiniMap.Process(); + + PROFILE_START(); + g_VoiceGameMgr.Update(gpGlobals->frametime); + PROFILE_END(kUpdateVoiceManagerProfile); + + const float kParticleUpdateInterval = 2.0f; + if((this->mLastParticleUpdate == -1) || (theTime > (this->mLastParticleUpdate + kParticleUpdateInterval))) + { + if(GET_RUN_CODE(2)) + { + PROFILE_START(); + AvHParticleSystemManager::Instance()->Update(theTimePassed); + this->mLastParticleUpdate = theTime; + PROFILE_END(kUpdateParticleSystemManager) + } + + #ifdef DEBUG + int theUseCaching = BALANCE_VAR(kDebugServerCaching); + if(theUseCaching) + { + PROFILE_START() + + char theMessage[128]; + int theReturnTotal = kNumReturn0 + kNumReturn1; + float theNumReturnedPercentage = 0.0f; + float theNumCachedPercentage = 0.0f; + float theComputedPercentage = 0.0f; + + if(theReturnTotal > 0) + { + theNumReturnedPercentage = kNumReturn1/((float)theReturnTotal); + theNumCachedPercentage = kNumCached/((float)theReturnTotal); + theComputedPercentage = kNumComputed/((float)theReturnTotal); + } + + kPVSCoherencyTime = BALANCE_VAR(kDebugPVSCoherencyTime); + + sprintf(theMessage, "Percentage entities propagated: %f (%d/%d) (compute percentage: %f) (pvs time: %f)\n", theNumReturnedPercentage, kNumReturn1, theReturnTotal, theComputedPercentage, kPVSCoherencyTime); + UTIL_LogPrintf(theMessage); + + PROFILE_END(kUpdateDebugShowEntityLog) + } + #endif + + kServerFrameRate = 0; + kNumReturn0 = kNumReturn1 = kNumCached = kNumComputed = 0; + } + + this->UpdateServerCommands(); + this->UpdateGameTime(); + + if(GET_RUN_CODE(4)) + { + if(!this->GetGameStarted()) + { + if(!this->GetIsTournamentMode()) + { + this->UpdateTimeLimit(); + } + + this->UpdateCountdown(theTime); + + PROFILE_START(); + // Try to join any players that are waiting for a spot (bypass cost of reinforcements before game starts) + // Players are put into reinforcement mode if there are no available spawns + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) + { + AvHTeamNumber theTeamNumber = theEntity->GetTeam(); + if(theTeamNumber != TEAM_IND) + { + this->AttemptToJoinTeam(theEntity, theTeamNumber, false); + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + PROFILE_END(kUpdateReinforcementsProfile); + } + else + { + //this->ProcessTeamUpgrades(); + this->UpdateEntitiesUnderAttack(); + + // Has a side won? + if(this->mVictoryTeam != TEAM_IND) + { + // Let players bask in their victory, don't update the world normally + PROFILE_START() + this->UpdateVictory(); + PROFILE_END(kUpdateVictoryProfile) + } + else + { + PROFILE_START() + this->mTeamA.Update(); + PROFILE_END(kTeamOneUpdate) + + PROFILE_START() + this->mTeamB.Update(); + PROFILE_END(kTeamTwoUpdate) + } + + PROFILE_START() + // Don't update every frame for performance reasons + const float kVictoryUpdateInterval = 1.0f; + if((this->mLastVictoryUpdate == -1) || (theTime > (this->mLastVictoryUpdate + kVictoryUpdateInterval))) + { + this->UpdateVictoryStatus(); + this->mLastVictoryUpdate = theTime; + } + PROFILE_END(kUpdateVictoryStatusProfile) + } + } + + gServerTick++; + if((gServerTick % 5) == 0) + { + gServerUpdate = true; + } + else + { + gServerUpdate = false; + } + PROFILE_END(kUpdateGamerulesProfile) + +} + +void AvHGamerules::RegisterServerVariable(const cvar_t* inCvar) +{ + mServerVariableList.push_back(inCvar); +} + +int AvHGamerules::GetNumServerVariables() const +{ + return mServerVariableList.size(); +} + +const cvar_t* AvHGamerules::GetServerVariable(int i) const +{ + return mServerVariableList[i]; +} + +bool AvHGamerules::GetIsEntityUnderAttack(int inEntityIndex) const +{ + bool theEntityIsUnderAttack = false; + // If entity is in list, it's being attacked + if ( this->mEntitiesUnderAttack.find(inEntityIndex) != this->mEntitiesUnderAttack.end() ) { + theEntityIsUnderAttack=true; + } + return theEntityIsUnderAttack; +} + +void AvHGamerules::TriggerAlert(AvHTeamNumber inTeamNumber, AvHAlertType inAlertType, int inEntIndex, AvHMessageID inMessageID) +{ + AvHTeam* theTeam = this->GetTeam(inTeamNumber); + if(theTeam) + { + // Don't play audio alerts too often. This also allows neat tactics where players can time strikes to prevent the other team from instant notification of an alert, ala RTS + float theTimeOfLastAlert = -1; + AvHAlertType theLastAlertType = ALERT_NONE; + + const AvHAlert* theLastAlert = theTeam->GetLastAudioAlert(); + if(theLastAlert) + { + theTimeOfLastAlert = theLastAlert->GetTime(); + theLastAlertType = theLastAlert->GetAlertType(); + } + + // Play alerts when enough time has passed + bool theIsRepeatAlert = (inAlertType == theLastAlertType); + const float kBaseAlertInterval = theTeam->GetAudioAlertInterval(); + float theAlertInterval = theIsRepeatAlert ? 2.0f*kBaseAlertInterval : kBaseAlertInterval; + + bool theAlertIntervalTimePassed = (gpGlobals->time - theTimeOfLastAlert > theAlertInterval); + bool thePlayAlertSound = ((theTimeOfLastAlert == -1) || theAlertIntervalTimePassed); + + // Always play urgent alerts, but only when they're new (so you don't get 'hive under attack' twice in quick succession) + if(AvHSUGetIsUrgentAlert(inAlertType) && (!AvHSUGetIsOftRepeatedAlert(inAlertType) && !theIsRepeatAlert)) + { + thePlayAlertSound = true; + } + + // Add it, remembering if we triggered sound for it + AvHAlert theNewAlert(inAlertType, gpGlobals->time, inEntIndex); + theNewAlert.SetPlayedAudio(thePlayAlertSound); + theTeam->AddAlert(theNewAlert, inMessageID); + + if(thePlayAlertSound) + { + AvHHUDSound theSound = HUD_SOUND_INVALID; + CBaseEntity* theAlertEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inEntIndex)); + + // If team type is marines, play sound for commander + AvHClassType theTeamType = theTeam->GetTeamType(); + if(theTeamType == AVH_CLASS_TYPE_MARINE) + { + // Get commander for team, if any + int theCommanderIndex = theTeam->GetCommander(); + + if(this->GetIsTesting()) + { + theCommanderIndex = 1; + } + + if(theCommanderIndex != -1) + { + CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theCommanderIndex)); + AvHPlayer* theCommander = dynamic_cast(theBaseEntity); + if(theCommander /*&& thePlayAudioAlert*/) + { + if(inAlertType == ALERT_RESEARCH_COMPLETE) + { + theSound = HUD_SOUND_MARINE_RESEARCHCOMPLETE; + } + else if(inAlertType == ALERT_UPGRADE_COMPLETE) + { + theSound = HUD_SOUND_MARINE_UPGRADE_COMPLETE; + } + else if(inAlertType == ALERT_LOW_RESOURCES) + { + theSound = HUD_SOUND_MARINE_RESOURCES_LOW; + } + // Special sound for the CC, always play it no matter where the commander is + else if(inAlertType == ALERT_UNDER_ATTACK) + { + if(dynamic_cast(theAlertEntity)) + { + theSound = HUD_SOUND_MARINE_CCUNDERATTACK; + } + else + { + theSound = HUD_SOUND_MARINE_BASE_UNDER_ATTACK; + } + } + else if(inAlertType == ALERT_ORDER_COMPLETE) + { + theSound = HUD_SOUND_ORDER_COMPLETE; + } + // Check for positional alerts (only send them if they are out of commander's sight) + else + { + if(theAlertEntity) + { + vec3_t theDistanceToAlert; + VectorSubtract(theCommander->pev->origin, theAlertEntity->pev->origin, theDistanceToAlert); + float theXYDistanceToAlert = theDistanceToAlert.Length2D(); + + if(theXYDistanceToAlert > this->GetMapExtents().GetTopDownCullDistance()) + { + if(inAlertType == ALERT_PLAYER_ENGAGE) + { + theSound = HUD_SOUND_MARINE_SOLDIER_UNDER_ATTACK; + } + else if(inAlertType == ALERT_UNDER_ATTACK) + { + theSound = HUD_SOUND_MARINE_BASE_UNDER_ATTACK; + } + else if(inAlertType == ALERT_SOLDIER_NEEDS_AMMO) + { + theSound = HUD_SOUND_MARINE_NEEDS_AMMO; + } + else if(inAlertType == ALERT_SOLDIER_NEEDS_HEALTH) + { + theSound = HUD_SOUND_MARINE_NEEDS_HEALTH; + } + else if(inAlertType == ALERT_ORDER_NEEDED) + { + theSound = HUD_SOUND_MARINE_NEEDS_ORDER; + } + else if(inAlertType == ALERT_PLAYER_DIED) + { + theSound = HUD_SOUND_MARINE_SOLDIER_LOST; + } + else if(inAlertType == ALERT_SENTRY_FIRING) + { + theSound = HUD_SOUND_MARINE_SENTRYFIRING; + } + else if(inAlertType == ALERT_SENTRY_DAMAGED) + { + theSound = HUD_SOUND_MARINE_SENTRYDAMAGED; + } + } + } + } + + if(theSound != HUD_SOUND_INVALID) + { + if (theAlertEntity != NULL) + { + // Added by mmcguire. + theCommander->PlayHUDSound(theSound, theAlertEntity->pev->origin[0], theAlertEntity->pev->origin[1]); + } + else + { + theCommander->PlayHUDSound(theSound); + } + } + } + } + else + { + // Play "command station under attack" sound for all players on team + if(inAlertType == ALERT_UNDER_ATTACK) + { + if(dynamic_cast(theAlertEntity)) + { + // For all players on this team, play the sound + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if((theEntity->pev->team == inTeamNumber) || this->GetIsTesting() || (theEntity->GetIsSpectatingTeam(inTeamNumber))) + { + theEntity->PlayHUDSound(HUD_SOUND_MARINE_CCUNDERATTACK); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + } + } + } + } + // Else it's aliens, play hive under attack sound + else if(theTeamType == AVH_CLASS_TYPE_ALIEN) + { + // When adding alien alerts, don't always play audio notification, it gets annoying + if(thePlayAlertSound) + { + // Tell everyone where attack is + if(inAlertType == ALERT_PLAYER_ENGAGE) + { + theSound = HUD_SOUND_ALIEN_LIFEFORM_ATTACK; + } + else if(inAlertType == ALERT_UNDER_ATTACK) + { + // If it's a resource tower + if(theAlertEntity) + { + switch(theAlertEntity->pev->iuser3) + { + case AVH_USER3_ALIENRESTOWER: + theSound = HUD_SOUND_ALIEN_RESOURCES_ATTACK; + break; + case AVH_USER3_HIVE: + theSound = HUD_SOUND_ALIEN_HIVE_ATTACK; + break; + default: + theSound = HUD_SOUND_ALIEN_STRUCTURE_ATTACK; + break; + } + } + } + else if(inAlertType == ALERT_HIVE_COMPLETE) + { + theSound = HUD_SOUND_ALIEN_HIVE_COMPLETE; + } + else if(inAlertType == ALERT_HIVE_DYING) + { + theSound = HUD_SOUND_ALIEN_HIVE_DYING; + } + else if(inAlertType == ALERT_LOW_RESOURCES) + { + theSound = HUD_SOUND_ALIEN_RESOURCES_LOW; + } + else if(inAlertType == ALERT_NEW_TRAIT) + { + theSound = HUD_SOUND_ALIEN_NEW_TRAIT; + } + else if(inAlertType == ALERT_HIVE_DEFEND) + { + theSound = HUD_SOUND_ALIEN_ENEMY_APPROACHES; + } + + if(theSound != HUD_SOUND_INVALID) + { + // For all players on this team, play the sound + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if((theEntity->pev->team == inTeamNumber) || this->GetIsTesting() || (theEntity->GetIsSpectatingTeam(inTeamNumber))) + { + theEntity->PlayHUDSound(theSound); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + } + } + } + else + { + ASSERT(false); + } + } + + // Add entity to our list of entities that are under attack + if(((inAlertType == ALERT_UNDER_ATTACK) || (inAlertType == ALERT_PLAYER_ENGAGE) || (inAlertType == ALERT_HIVE_DYING)|| (inAlertType == ALERT_HIVE_DEFEND) ) && (inEntIndex > 0)) + { + // This will update current time longer if continually attacked + const float kUnderAttackDuration = 5.0f; + this->mEntitiesUnderAttack[inEntIndex] = gpGlobals->time + kUnderAttackDuration; + } + } +} + +bool AvHGamerules::GetIsCheatEnabled(const string& inCheatName) const +{ + bool theCheatIsEnabled = false; + + bool theAllowCheats = this->GetCheatsEnabled(); + + #ifdef DEBUG + theAllowCheats = true; + #endif + + if(theAllowCheats) + { + StringList::const_iterator theIter = std::find(this->mCheats.begin(), this->mCheats.end(), inCheatName); + if(theIter != this->mCheats.end()) + { + theCheatIsEnabled = true; + } + } + + return theCheatIsEnabled; +} + +void AvHGamerules::SetCheatEnabled(const string& inCheatName, bool inEnabledState) +{ + if(this->GetCheatsEnabled()) + { + StringList::iterator theIter = std::find(this->mCheats.begin(), this->mCheats.end(), inCheatName); + if(theIter == this->mCheats.end()) + { + if(inEnabledState) + { + this->mCheats.push_back(inCheatName); + } + } + else + { + if(!inEnabledState) + { + this->mCheats.erase(theIter); + } + } + } +} + +void AvHGamerules::UpdateCheats() +{ + if(this->GetCheatsEnabled()) + { + // If bigdig is enabled + if(this->GetIsCheatEnabled(kcBigDig)) + { + // Run through all buildables, set them to complete + //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::UpdateCheats\n"); + + FOR_ALL_BASEENTITIES(); + AvHBuildable* theBuildable = dynamic_cast(theBaseEntity); + if(theBuildable && !theBuildable->GetHasBeenBuilt() && !GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_RECYCLING) && (theBaseEntity->pev->team != TEAM_IND)) + { + theBuildable->SetConstructionComplete(true); + } +// AvHDeployedTurret* theTurret = dynamic_cast(theBaseEntity); +// if(theTurret && !theTurret->GetIsBuilt()) +// { +// theTurret->SetConstructionComplete(); +// } + END_FOR_ALL_BASEENTITIES(); + } + } +} + +void AvHGamerules::UpdateCountdown(float inTime) +{ + const float kTimeWontStartInterval = 8.0f; + int kSecondsToCountdown = 5; + + #ifdef DEBUG + kSecondsToCountdown = 1; + #endif + + // Time to start counting down? + if(this->ReadyToStartCountdown()) + { + if(!this->mStartedCountdown) + { + //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + // theEntity->SendMessageNextThink(kGameStarting); + //END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + // If there is at least one person on one team, start counting + this->mTimeCountDownStarted = inTime; + this->mStartedCountdown = true; + + // Reset server and respawn everyone + this->ResetGame(true); + + this->mSavedTimeCountDownStarted = this->mTimeCountDownStarted; + } + } + else + { + if(!this->GetIsTrainingMode()) + { + if((this->mTimeLastWontStartMessageSent == 0) || (inTime > (this->mTimeLastWontStartMessageSent + kTimeWontStartInterval))) + { + const char* theMessage = kGameWontStart; + if(this->GetIsTournamentMode()) + { + theMessage = kGameWontStartUntilReady; + } + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + theEntity->SendMessageOnce(theMessage, TOOLTIP); + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + this->mTimeLastWontStartMessageSent = inTime; + } + } + } + + if(this->mStartedCountdown) + { + if(inTime - this->mTimeCountDownStarted > (avh_countdowntime.value*60 - kSecondsToCountdown) && !this->mSentCountdownMessage) + { + // Send update to everyone + NetMsg_UpdateCountdown( kSecondsToCountdown ); + + this->mTimeSentCountdown = gpGlobals->time; + this->mSentCountdownMessage = true; + } + + if(this->mSentCountdownMessage) + { + if(gpGlobals->time - this->mTimeSentCountdown >= kSecondsToCountdown) + { + this->SetGameStarted(true); + } + } + } +} + +void AvHGamerules::RemoveEntityUnderAttack(int entIndex) { + EntityUnderAttackListType::iterator theIter=this->mEntitiesUnderAttack.find(entIndex); + if ( theIter != mEntitiesUnderAttack.end() ) + this->mEntitiesUnderAttack.erase(theIter); +} + +void AvHGamerules::UpdateEntitiesUnderAttack() +{ + // Expire entities under attack + for(EntityUnderAttackListType::iterator theIter = this->mEntitiesUnderAttack.begin(); theIter != this->mEntitiesUnderAttack.end(); /* no increment*/) + { + if(gpGlobals->time >= theIter->second) + { + EntityUnderAttackListType::iterator theTempIter = theIter; + theTempIter++; + this->mEntitiesUnderAttack.erase(theIter); + theIter = theTempIter; + } + else + { + theIter++; + } + } +} + +void AvHGamerules::SendGameTimeUpdate(bool inReliable) +{ + // Send down game time to clients periodically + NetMsg_GameStatus_Time( kGameStatusGameTime, this->mMapMode, this->GetGameTime(), this->GetTimeLimit(), this->mCombatAttackingTeamNumber, inReliable ); + + this->mTimeOfLastGameTimeUpdate = gpGlobals->time; +} + +void AvHGamerules::UpdateGameTime() +{ + // Send periodic game time updates to everyone (needed for HLTV spectators) + if(this->GetGameStarted() && (this->GetVictoryTeam() == TEAM_IND)) + { + // Only reason why this isn't longer is to make sure late-joiners see time on scoreboard after reasonable period, without causing needless bandwidth + const float kGameTimeUpdateInterval = 8; + if((this->mTimeOfLastGameTimeUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastGameTimeUpdate + kGameTimeUpdateInterval))) + { + this->SendGameTimeUpdate(); + } + } +} + + +void AvHGamerules::UpdateScripts() +{ + const float kUpdateScriptsInterval = 1.0f; + + // If server scripts are enabled + if(CVAR_GET_FLOAT(kvServerScripts)) + { +// const char* theCStrLevelName = STRING(gpGlobals->mapname); +// if(theCStrLevelName && !FStrEq(theCStrLevelName, "")) +// { +// string theLevelName = theCStrLevelName; +// string theLevelScript = string(kModDirectory) + string("/") + kScriptsDirectory + string("/") + theLevelName + string(".bin"); +// +// if((this->mTimeUpdatedScripts == -1) || (gpGlobals->time > this->mTimeUpdatedScripts + kUpdateScriptsInterval)) +// { +// // Execute the script +// AvHLUADoFile(theLevelScript.c_str()); +// } +// +// } + AvHScriptManager::Instance()->Update(gpGlobals->time); + } +} + +void AvHGamerules::UpdateServerCommands() +{ + //float theAirAccelerate = CVAR_GET_FLOAT("sv_airaccelerate"); + //float theAirMove = CVAR_GET_FLOAT("sv_airmove"); + + // TODO: Disguises these strings somehow to prevent hacking? + SERVER_COMMAND("sv_airaccelerate 10\n"); + SERVER_COMMAND("sv_airmove 1\n"); +} + +void AvHGamerules::UpdateTimeLimit() +{ + // Only update time limit after world has been reset. Give some time to make sure all clients get the game status message. + #ifdef DEBUG + const float kIntermissionTime = 2; + #else + const float kIntermissionTime = 5; + #endif + + if(gpGlobals->time > (this->mTimeWorldReset + kIntermissionTime)) + { + float theTimeLimit = timelimit.value * 60; + + if((theTimeLimit > 0) && (gpGlobals->time >= (theTimeLimit + this->mLastMapChange))) + { + this->ChangeLevel(); + } + } +} + +void AvHGamerules::UpdateVictory() +{ + if((gpGlobals->time - this->mVictoryTime) > (kVictoryIntermission + kMaxPlayers/kResetPlayersPerSecond)) + { + this->ResetGame(); + } +} + +void AvHGamerules::UpdateVictoryStatus(void) +{ + bool theCheckVictoryWithCheats = !this->GetCheatsEnabled() || this->GetIsCheatEnabled(kcEndGame1) || this->GetIsCheatEnabled(kcEndGame2); + + if((this->mVictoryTeam == TEAM_IND) && this->mGameStarted && theCheckVictoryWithCheats && !this->GetIsTrainingMode()) + { + char* theVictoryMessage = NULL; + bool theTeamALost = this->mTeamA.GetHasTeamLost(); + bool theTeamBLost = this->mTeamB.GetHasTeamLost(); + bool theDrawGame = false; + + int theTeamAPlayers = this->mTeamA.GetPlayerCount(); + int theTeamBPlayers = this->mTeamB.GetPlayerCount(); + + bool theTimeLimitHit = false; + int theTimeLimitSeconds = this->GetTimeLimit(); + + if(this->GetIsCombatMode()) + { + if(this->GetIsIronMan()) + { + theTimeLimitSeconds = ns_cvar_float(&avh_ironmantime)*60; + } + } + + if((theTimeLimitSeconds > 0) && ((gpGlobals->time - GetGameRules()->GetTimeGameStarted()) > theTimeLimitSeconds)) + { + theTimeLimitHit = true; + } + + if(this->GetIsCheatEnabled(kcEndGame1)) + { + theTeamBLost = true; + } + else if(this->GetIsCheatEnabled(kcEndGame2)) + { + theTeamALost = true; + } + else if(this->GetIsCombatMode()) + { + // Alien victory if time limit is hit + if(theTimeLimitHit) + { + if(this->mCombatAttackingTeamNumber == this->mTeamA.GetTeamNumber()) + { + theTeamALost = true; + } + else if(this->mCombatAttackingTeamNumber == this->mTeamB.GetTeamNumber()) + { + theTeamBLost = true; + } + } + } + else if(this->GetIsTournamentMode() && !this->GetIsCombatMode()) + { + // If timelimit has elapsed in tourny mode, the victor is the team with the most resources + if(theTimeLimitHit) + { + // Don't count fractional resources. If it's that close, it was a tie. + int theTeamAResources = this->mTeamA.GetTotalResourcesGathered(); + int theTeamBResources = this->mTeamB.GetTotalResourcesGathered(); + if(theTeamAResources > theTeamBResources) + { + theTeamBLost = true; + } + else if(theTeamBResources > theTeamAResources) + { + theTeamALost = true; + } + else + { + // It's a draw + theDrawGame = true; + } + } + } + else if(this->GetIsIronMan()) + { + if(theTimeLimitHit) + { + // Aliens win. If both teams are the same, it's a draw. + if(this->mTeamA.GetTeamType() == this->mTeamB.GetTeamType()) + { + theDrawGame = true; + } + else + { + if(this->mTeamA.GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + theTeamBLost = true; + } + else if(this->mTeamB.GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + theTeamALost = true; + } + } + } + else if(!this->mTeamA.GetHasAtLeastOneActivePlayer()) + { + theTeamALost = true; + } + else if(!this->mTeamB.GetHasAtLeastOneActivePlayer()) + { + theTeamBLost = true; + } + } + else if(!this->GetIsTournamentMode()) + { + // Check for automatic concession, if teams are suddenly unbalanced (generally because a team realizes it's futile and leaves) + const int kAutoConcedeNumPlayers = (int)(avh_autoconcede.value); + if(kAutoConcedeNumPlayers > 0) + { + if((theTeamAPlayers - theTeamBPlayers) >= kAutoConcedeNumPlayers) + { + theTeamBLost = true; + } + else if((theTeamBPlayers - theTeamAPlayers) >= kAutoConcedeNumPlayers) + { + theTeamALost = true; + } + } + } + + if((theTeamALost && theTeamBLost) || theDrawGame) + { + // Draw! + this->mVictoryTime = gpGlobals->time; + this->mVictoryTeam = TEAM_SPECT; + this->mVictoryDraw = true; + theVictoryMessage = kGameDraw; + theDrawGame = true; + } + + if(theTeamALost) + { + // If there is a victory, set mVictoryTime and mVictoryTeam + this->mVictoryTime = gpGlobals->time; + this->mVictoryTeam = this->mTeamB.GetTeamNumber(); + theVictoryMessage = kTeamTwoWon; + } + else if(theTeamBLost) + { + // If there is a victory, set mVictoryTime and mVictoryTeam + this->mVictoryTime = gpGlobals->time; + this->mVictoryTeam = this->mTeamA.GetTeamNumber(); + theVictoryMessage = kTeamOneWon; + } + + //if(!theAtLeastOneTeamOneMemberLeft || !theAtLeastOneTeamTwoMemberLeft) + if(this->mVictoryTeam != TEAM_IND) + { + this->TallyVictoryStats(); + AvHNexus::finishGame(); + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + AvHTeam* theTeam = theEntity->GetTeamPointer(); + AvHHUDSound theHUDSound = HUD_SOUND_YOU_LOSE; + if((theTeam && (theTeam->GetTeamNumber() == this->mVictoryTeam)) || (theEntity->GetPlayMode() == PLAYMODE_OBSERVER) || (theEntity->pev->team == TEAM_IND) || theDrawGame) + { + theHUDSound = HUD_SOUND_YOU_WIN; + } + else if(!this->GetIsCombatMode()) + { + // Fade losers out to black + Vector theFadeColor; + theFadeColor.x = 0; + theFadeColor.y = 0; + theFadeColor.z = 0; + UTIL_ScreenFade(theEntity, theFadeColor, 1.0f, 0.0f, 255, FFADE_OUT | FFADE_STAYOUT); + } + theEntity->PlayHUDSound(theHUDSound); + theEntity->SendMessage(theVictoryMessage, CENTER); + + // Final game time update to all clients have same winning time + this->SendGameTimeUpdate(true); + + + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + // Send final score to everyone if needed + this->mTeamA.SendResourcesGatheredScore(false); + this->mTeamB.SendResourcesGatheredScore(false); + + // Tell everyone that the game ended + NetMsg_GameStatus_State( kGameStatusEnded, this->mMapMode ); + + NetMsg_TeamScore(this->mTeamA.GetTeamName(), 0, 1 ); + NetMsg_TeamScore(this->mTeamB.GetTeamName(), 0, 1 ); + } + } +} + +int AvHGamerules::GetBaseHealthForMessageID(AvHMessageID inMessageID) const +{ + int health = 100; + + switch(inMessageID) + { + case BUILD_INFANTRYPORTAL: health = BALANCE_VAR(kInfantryPortalHealth); break; + case BUILD_RESOURCES: health = BALANCE_VAR(kResourceTowerHealth); break; + case BUILD_TURRET_FACTORY: health = BALANCE_VAR(kTurretFactoryHealth); break; + case TURRET_FACTORY_UPGRADE: health = BALANCE_VAR(kTurretFactoryHealth); break; + case BUILD_ARMSLAB: health = BALANCE_VAR(kArmsLabHealth); break; + case BUILD_PROTOTYPE_LAB: health = BALANCE_VAR(kArmsLabHealth); break; + case BUILD_ARMORY: health = BALANCE_VAR(kArmoryHealth); break; + case ARMORY_UPGRADE: health = BALANCE_VAR(kAdvArmoryHealth); break; + case BUILD_OBSERVATORY: health = BALANCE_VAR(kObservatoryHealth); break; + case BUILD_PHASEGATE: health = BALANCE_VAR(kPhaseGateHealth); break; + case BUILD_TURRET: health = BALANCE_VAR(kSentryHealth); break; + case BUILD_SIEGE: health = BALANCE_VAR(kSiegeHealth); break; + case BUILD_COMMANDSTATION: health = BALANCE_VAR(kCommandStationHealth); break; + case ALIEN_BUILD_RESOURCES: health = BALANCE_VAR(kAlienResourceTowerHealth); break; + case ALIEN_BUILD_OFFENSE_CHAMBER: health = BALANCE_VAR(kOffenseChamberHealth); break; + case ALIEN_BUILD_DEFENSE_CHAMBER: health = BALANCE_VAR(kDefenseChamberHealth); break; + case ALIEN_BUILD_SENSORY_CHAMBER: health = BALANCE_VAR(kSensoryChamberHealth); break; + case ALIEN_BUILD_MOVEMENT_CHAMBER: health = BALANCE_VAR(kMovementChamberHealth); break; + case ALIEN_BUILD_HIVE: health = BALANCE_VAR(kHiveHealth); break; + } + return health; +} + + +int AvHGamerules::GetBuildTimeForMessageID(AvHMessageID inMessageID) const +{ + float time = 0.0f; + const float CO_Scalar = this->GetIsCombatMode() ? BALANCE_VAR(kCombatModeTimeScalar) : 1.0f; + const float CO_GScalar = this->GetIsCombatMode() ? BALANCE_VAR(kCombatModeGestationTimeScalar) : 1.0f; + + switch(inMessageID) + { + // Marine Research + case RESEARCH_ELECTRICAL: time = BALANCE_VAR(kElectricalUpgradeResearchTime); break; + case RESEARCH_ARMOR_ONE: time = BALANCE_VAR(kArmorOneResearchTime); break; + case RESEARCH_ARMOR_TWO: time = BALANCE_VAR(kArmorTwoResearchTime); break; + case RESEARCH_ARMOR_THREE: time = BALANCE_VAR(kArmorThreeResearchTime); break; + case RESEARCH_WEAPONS_ONE: time = BALANCE_VAR(kWeaponsOneResearchTime); break; + case RESEARCH_WEAPONS_TWO: time = BALANCE_VAR(kWeaponsTwoResearchTime); break; + case RESEARCH_WEAPONS_THREE: time = BALANCE_VAR(kWeaponsThreeResearchTime); break; + case RESEARCH_CATALYSTS: time = BALANCE_VAR(kCatalystResearchTime); break; + case RESEARCH_GRENADES: time = BALANCE_VAR(kGrenadesResearchTime); break; + case RESEARCH_JETPACKS: time = BALANCE_VAR(kJetpacksResearchTime); break; + case RESEARCH_HEAVYARMOR: time = BALANCE_VAR(kHeavyArmorResearchTime); break; + case RESEARCH_DISTRESSBEACON: time = BALANCE_VAR(kDistressBeaconTime); break; + case RESEARCH_HEALTH: time = BALANCE_VAR(kHealthResearchTime); break; + case RESEARCH_MOTIONTRACK: time = BALANCE_VAR(kMotionTrackingResearchTime); break; + case RESEARCH_PHASETECH: time = BALANCE_VAR(kPhaseTechResearchTime); break; + + // Marine Structures + case BUILD_INFANTRYPORTAL: time = BALANCE_VAR(kInfantryPortalBuildTime); break; + case BUILD_RESOURCES: time = BALANCE_VAR(kResourceTowerBuildTime); break; + case BUILD_TURRET_FACTORY: time = BALANCE_VAR(kTurretFactoryBuildTime); break; + case BUILD_ARMSLAB: time = BALANCE_VAR(kArmsLabBuildTime); break; + case BUILD_PROTOTYPE_LAB: time = BALANCE_VAR(kPrototypeLabBuildTime); break; + case BUILD_ARMORY: time = BALANCE_VAR(kArmoryBuildTime); break; + case ARMORY_UPGRADE: time = BALANCE_VAR(kArmoryUpgradeTime); break; + case BUILD_OBSERVATORY: time = BALANCE_VAR(kObservatoryBuildTime); break; + case BUILD_PHASEGATE: time = BALANCE_VAR(kPhaseGateBuildTime); break; + case BUILD_TURRET: time = BALANCE_VAR(kSentryBuildTime); break; + case BUILD_SIEGE: time = BALANCE_VAR(kSiegeBuildTime); break; + case BUILD_COMMANDSTATION: time = BALANCE_VAR(kCommandStationBuildTime); break; + case TURRET_FACTORY_UPGRADE: time = BALANCE_VAR(kTurretFactoryUpgradeTime); break; + + // Scan Duration + case BUILD_SCAN: time = BALANCE_VAR(kScanDuration); break; + + // Alien Structures + case ALIEN_BUILD_RESOURCES: time = BALANCE_VAR(kAlienResourceTowerBuildTime); break; + case ALIEN_BUILD_OFFENSE_CHAMBER: time = BALANCE_VAR(kOffenseChamberBuildTime); break; + case ALIEN_BUILD_DEFENSE_CHAMBER: time = BALANCE_VAR(kDefenseChamberBuildTime); break; + case ALIEN_BUILD_SENSORY_CHAMBER: time = BALANCE_VAR(kSensoryChamberBuildTime); break; + case ALIEN_BUILD_MOVEMENT_CHAMBER: time = BALANCE_VAR(kMovementChamberBuildTime); break; + case ALIEN_BUILD_HIVE: time = BALANCE_VAR(kHiveBuildTime); break; + + // Alien Evolutions + case ALIEN_EVOLUTION_ONE: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_EVOLUTION_TWO: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_EVOLUTION_THREE: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_EVOLUTION_SEVEN: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_EVOLUTION_EIGHT: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_EVOLUTION_NINE: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_EVOLUTION_TEN: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_EVOLUTION_ELEVEN: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_EVOLUTION_TWELVE: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_HIVE_TWO_UNLOCK: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + case ALIEN_HIVE_THREE_UNLOCK: time = BALANCE_VAR(kEvolutionGestateTime)*CO_Scalar; break; + + // Alien Lifeforms + case ALIEN_LIFEFORM_ONE: time = BALANCE_VAR(kSkulkGestateTime)*CO_GScalar; break; + case ALIEN_LIFEFORM_TWO: time = BALANCE_VAR(kGorgeGestateTime)*CO_GScalar; break; + case ALIEN_LIFEFORM_THREE: time = BALANCE_VAR(kLerkGestateTime)*CO_GScalar; break; + case ALIEN_LIFEFORM_FOUR: time = BALANCE_VAR(kFadeGestateTime)*CO_GScalar; break; + case ALIEN_LIFEFORM_FIVE: time = BALANCE_VAR(kOnosGestateTime)*CO_GScalar; break; + } + + if( time > 0 ) + { + time = max( time, 1.0f ); //for cases where combat scalars would result in fractional seconds + } + + if(this->GetCheatsEnabled() && !this->GetIsCheatEnabled(kcSlowResearch)) + { + time = min( time, 2.0f ); + } + + return floor( time ); +} + +int AvHGamerules::GetCostForMessageID(AvHMessageID inMessageID) const +{ + // This is point cost or energy cost in NS, or number of levels in Combat + int cost = 0; + + if(this->GetIsCombatMode()) + { + switch(inMessageID) + { + case ALIEN_LIFEFORM_TWO: + case ALIEN_EVOLUTION_ONE: + case ALIEN_EVOLUTION_TWO: + case ALIEN_EVOLUTION_THREE: + case ALIEN_EVOLUTION_SEVEN: + case ALIEN_EVOLUTION_EIGHT: + case ALIEN_EVOLUTION_NINE: + case ALIEN_EVOLUTION_TEN: + case ALIEN_EVOLUTION_TWELVE: + case BUILD_RESUPPLY: + case RESEARCH_ARMOR_ONE: + case RESEARCH_ARMOR_TWO: + case RESEARCH_ARMOR_THREE: + case RESEARCH_WEAPONS_ONE: + case RESEARCH_WEAPONS_TWO: + case RESEARCH_WEAPONS_THREE: + case BUILD_CAT: + case RESEARCH_MOTIONTRACK: + case RESEARCH_GRENADES: + case BUILD_SCAN: + case BUILD_MINES: + case BUILD_WELDER: + case BUILD_SHOTGUN: + case BUILD_HMG: + case BUILD_GRENADE_GUN: + case ALIEN_HIVE_TWO_UNLOCK: + cost = 1; + break; + + case ALIEN_LIFEFORM_THREE: + case BUILD_HEAVY: + case BUILD_JETPACK: + case ALIEN_HIVE_THREE_UNLOCK: + case ALIEN_EVOLUTION_ELEVEN: + cost = 2; + break; + + case ALIEN_LIFEFORM_FOUR: + cost = 3; + break; + + case ALIEN_LIFEFORM_FIVE: + cost = 4; + break; + } + } + else + { + switch(inMessageID) + { + // Marine Research + case RESEARCH_ELECTRICAL: cost = BALANCE_VAR(kElectricalUpgradeResearchCost); break; + case RESEARCH_ARMOR_ONE: cost = BALANCE_VAR(kArmorOneResearchCost); break; + case RESEARCH_ARMOR_TWO: cost = BALANCE_VAR(kArmorTwoResearchCost); break; + case RESEARCH_ARMOR_THREE: cost = BALANCE_VAR(kArmorThreeResearchCost); break; + case RESEARCH_WEAPONS_ONE: cost = BALANCE_VAR(kWeaponsOneResearchCost); break; + case RESEARCH_WEAPONS_TWO: cost = BALANCE_VAR(kWeaponsTwoResearchCost); break; + case RESEARCH_WEAPONS_THREE: cost = BALANCE_VAR(kWeaponsThreeResearchCost); break; + case RESEARCH_CATALYSTS: cost = BALANCE_VAR(kCatalystResearchCost); break; + case RESEARCH_GRENADES: cost = BALANCE_VAR(kGrenadesResearchCost); break; + case TURRET_FACTORY_UPGRADE: cost = BALANCE_VAR(kTurretFactoryUpgradeCost); break; + case RESEARCH_JETPACKS: cost = BALANCE_VAR(kJetpacksResearchCost); break; + case RESEARCH_HEAVYARMOR: cost = BALANCE_VAR(kHeavyArmorResearchCost); break; + case RESEARCH_HEALTH: cost = BALANCE_VAR(kHealthResearchCost); break; + case RESEARCH_MOTIONTRACK: cost = BALANCE_VAR(kMotionTrackingResearchCost); break; + case RESEARCH_PHASETECH: cost = BALANCE_VAR(kPhaseTechResearchCost); break; + case RESEARCH_DISTRESSBEACON: cost = BALANCE_VAR(kDistressBeaconCost); break; + + // Marine Structures + case BUILD_HEAVY: cost = BALANCE_VAR(kHeavyArmorCost); break; + case BUILD_JETPACK: cost = BALANCE_VAR(kJetpackCost); break; + case BUILD_INFANTRYPORTAL: cost = BALANCE_VAR(kInfantryPortalCost); break; + case BUILD_RESOURCES: cost = BALANCE_VAR(kResourceTowerCost); break; + case BUILD_TURRET_FACTORY: cost = BALANCE_VAR(kTurretFactoryCost); break; + case BUILD_ARMSLAB: cost = BALANCE_VAR(kArmsLabCost); break; + case BUILD_PROTOTYPE_LAB: cost = BALANCE_VAR(kPrototypeLabCost); break; + case BUILD_ARMORY: cost = BALANCE_VAR(kArmoryCost); break; + case ARMORY_UPGRADE: cost = BALANCE_VAR(kArmoryUpgradeCost); break; + case BUILD_OBSERVATORY: cost = BALANCE_VAR(kObservatoryCost); break; + case BUILD_PHASEGATE: cost = BALANCE_VAR(kPhaseGateCost); break; + case BUILD_TURRET: cost = BALANCE_VAR(kSentryCost); break; + case BUILD_SIEGE: cost = BALANCE_VAR(kSiegeCost); break; + case BUILD_COMMANDSTATION: cost = BALANCE_VAR(kCommandStationCost); break; + + // Marine Equipment + case BUILD_HEALTH: cost = BALANCE_VAR(kHealthCost); break; + case BUILD_AMMO: cost = BALANCE_VAR(kAmmoCost); break; + case BUILD_CAT: cost = BALANCE_VAR(kCatalystCost); break; + case BUILD_MINES: cost = BALANCE_VAR(kMineCost); break; + case BUILD_WELDER: cost = BALANCE_VAR(kWelderCost); break; + case BUILD_SHOTGUN: cost = BALANCE_VAR(kShotgunCost); break; + case BUILD_HMG: cost = BALANCE_VAR(kHMGCost); break; + case BUILD_GRENADE_GUN: cost = BALANCE_VAR(kGrenadeLauncherCost); break; + + // Alien Structures + case ALIEN_BUILD_RESOURCES: cost = BALANCE_VAR(kAlienResourceTowerCost); break; + case ALIEN_BUILD_OFFENSE_CHAMBER: cost = BALANCE_VAR(kOffenseChamberCost); break; + case ALIEN_BUILD_DEFENSE_CHAMBER: cost = BALANCE_VAR(kDefenseChamberCost); break; + case ALIEN_BUILD_SENSORY_CHAMBER: cost = BALANCE_VAR(kSensoryChamberCost); break; + case ALIEN_BUILD_MOVEMENT_CHAMBER: cost = BALANCE_VAR(kMovementChamberCost); break; + case ALIEN_BUILD_HIVE: cost = BALANCE_VAR(kHiveCost); break; + + // Alien Evolutions + case ALIEN_EVOLUTION_ONE: cost = BALANCE_VAR(kEvolutionCost); break; + case ALIEN_EVOLUTION_TWO: cost = BALANCE_VAR(kEvolutionCost); break; + case ALIEN_EVOLUTION_THREE: cost = BALANCE_VAR(kEvolutionCost); break; + case ALIEN_EVOLUTION_SEVEN: cost = BALANCE_VAR(kEvolutionCost); break; + case ALIEN_EVOLUTION_EIGHT: cost = BALANCE_VAR(kEvolutionCost); break; + case ALIEN_EVOLUTION_NINE: cost = BALANCE_VAR(kEvolutionCost); break; + case ALIEN_EVOLUTION_TEN: cost = BALANCE_VAR(kEvolutionCost); break; + case ALIEN_EVOLUTION_ELEVEN: cost = BALANCE_VAR(kEvolutionCost); break; + case ALIEN_EVOLUTION_TWELVE: cost = BALANCE_VAR(kEvolutionCost); break; + + // Alien Lifeforms + case ALIEN_LIFEFORM_ONE: cost = BALANCE_VAR(kSkulkCost); break; + case ALIEN_LIFEFORM_TWO: cost = BALANCE_VAR(kGorgeCost); break; + case ALIEN_LIFEFORM_THREE: cost = BALANCE_VAR(kLerkCost); break; + case ALIEN_LIFEFORM_FOUR: cost = BALANCE_VAR(kFadeCost); break; + case ALIEN_LIFEFORM_FIVE: cost = BALANCE_VAR(kOnosCost); break; + + // Energy Costs + case BUILD_SCAN: cost = BALANCE_VAR(kScanEnergyCost); break; + } + } + + return cost; +} + +void AvHGamerules::BalanceChanged() +{ + // Tell all players to update their weapons + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + theEntity->SendWeaponUpdate(); + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) +} + + diff --git a/main/source/mod/AvHGamerules.h b/main/source/mod/AvHGamerules.h index ea086e0..509a43d 100644 --- a/main/source/mod/AvHGamerules.h +++ b/main/source/mod/AvHGamerules.h @@ -1,419 +1,436 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: NS high-level game rules -// -// $Workfile: AvHGamerules.h $ -// $Date: 2002/11/15 04:46:18 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHGamerules.h,v $ -// Revision 1.52 2002/11/15 04:46:18 Flayra -// - Changes to for profiling and for improving AddToFullPack performance -// -// Revision 1.51 2002/11/12 02:24:57 Flayra -// - HLTV updates -// -// Revision 1.50 2002/11/06 01:40:01 Flayra -// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) -// -// Revision 1.49 2002/11/03 04:49:45 Flayra -// - Moved constants into .dll out of .cfg -// - Particle systems update much less often, big optimization -// - Team balance fixes -// -// Revision 1.48 2002/10/28 20:34:53 Flayra -// - Reworked game reset slightly, to meter out network usage during game reset -// -// Revision 1.47 2002/10/20 21:10:41 Flayra -// - Optimizations -// -// Revision 1.46 2002/10/16 00:56:22 Flayra -// - Prototyped curl support for NS stats -// - Added player auth stuff (server string) -// - Added concept of secondary weapons -// - Removed disconnect sound -// - Fixed MOTD -// - Added "order needed" alert -// -// Revision 1.45 2002/10/03 18:44:03 Flayra -// - Play disconnected sound quieter because of mass exodus effect -// - Added functionality to allow players to join a team and run around freely before game countdown starts. Game resets when countdown starts. -// - Added new alien alerts -// -// Revision 1.44 2002/09/23 22:16:01 Flayra -// - Added game victory status logging -// - Fixed commander alerts -// - Added new alerts -// - Particle system changes, only send them down when connecting or map changes -// -// Revision 1.43 2002/09/09 19:51:13 Flayra -// - Removed gamerules dictating alien respawn time, now it's in server variable -// -// Revision 1.42 2002/08/16 02:35:09 Flayra -// - Blips now update once per second, instead of once per player per second -// -// Revision 1.41 2002/08/09 01:01:42 Flayra -// - Removed error condition for map validity, allow marines to have only one primary weapon, adjust weapon weights -// -// Revision 1.40 2002/08/02 22:00:23 Flayra -// - New alert system that's not so annoying and is more helpful. Tweaks for new help system. -// -// Revision 1.39 2002/07/24 18:45:41 Flayra -// - Linux and scripting changes -// -// Revision 1.38 2002/07/23 17:03:21 Flayra -// - Resource model changes, refactoring spawning to fix level 5 redemption bug without code duplication -// -// Revision 1.37 2002/07/08 16:59:14 Flayra -// - Don't pick up empty weapons, added handicapping, check map validity when loaded, reset players just like all other entities, fixed spawn bug code where it was counting non-team spawns -// -// Revision 1.36 2002/07/01 21:32:44 Flayra -// - Visibility update now updates world for primal scream and umbra, don't update reliable network messages every tick (big optimization) -// -// Revision 1.35 2002/06/25 17:59:03 Flayra -// - Added timelimit (switches map after a game finishes), added info_locations, tried adding team balance (not sure it works yet), give resources for kills -// -// Revision 1.34 2002/06/10 19:54:30 Flayra -// - New minimap support, more attempts to fix picking up of alien weapons -// -// Revision 1.33 2002/05/28 17:40:32 Flayra -// - Minimap refactoring, hive sight "under attack", reinforcement refactoring, don't delete entities marked as permanent (it was deleting hives!), allow players to join the opposite team until this can be fixed for real (server retry gets around this code so this is just an inconvenience), things build fast with cheats -// -// Revision 1.32 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifndef AVH_GAMERULES_H -#define AVH_GAMERULES_H - -#include "dlls/gamerules.h" -//#include "mod/AvHCOCRuleset.h" -#include "mod/AvHTeam.h" -#include "mod/AvHMarineWeapons.h" -#include "types.h" -#include "mod/AvHMessage.h" -#include "mod/AvHEntityHierarchy.h" -#include "mod/AvHEntities.h" -#include "mod/AvHMiniMap.h" -#include "dlls/teamplay_gamerules.h" -#include "mod/AvHDramaticPriority.h" -#include "mod/AvHMapExtents.h" -#include "util/Tokenizer.h" -#include "mod/AvHSpawn.h" - -class AvHPlayer; - -class TeamPurchase -{ -public: - TeamPurchase(edict_t* inPlayer, AvHMessageID inMessageID) : mPlayer(inPlayer), mUpgrade(inMessageID) - {} - - edict_t* mPlayer; - AvHMessageID mUpgrade; -}; - -typedef vector< pair > MapVoteListType; -typedef map< int, int > PlayerMapVoteListType; -typedef map< int, float > PlayerVoteTimeType; -// puzl: 0001073 -#ifdef USE_OLDAUTH -typedef vector< pair > AuthIDListType; -typedef map AuthMaskListType; -#endif - -class AvHGamerules : public CHalfLifeTeamplay //public CHalfLifeMultiplay/*, public AvHCOCRuleset*/ -{ -public: - AvHGamerules(); - virtual ~AvHGamerules(); - -// puzl: 0001073 -#ifdef USE_OLDAUTH - virtual BOOL GetIsClientAuthorizedToPlay(edict_t* inEntity, bool inDisplayMessage, bool inForcePending = false) const; - virtual bool PerformHardAuthorization(AvHPlayer* inPlayer) const; - virtual int GetAuthenticationMask(const string& inAuthID) const; - const AuthIDListType& GetServerOpList() const; - void UpdateUplink(); -#endif - - // this is the game name that gets seen in the server browser - virtual int AmmoShouldRespawn( CBasePlayerAmmo *pAmmo ); - virtual void BuildableBuilt(AvHBuildable* inBuildable); - virtual void BuildableKilled(AvHBuildable* inBuildable); - virtual void BuildMiniMap(AvHPlayer* inPlayer); - virtual BOOL CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); - virtual void ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer ); - virtual const char* GetGameDescription(void); - - virtual int WeaponShouldRespawn( CBasePlayerItem *pWeapon ); - virtual void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); - - virtual BOOL FShouldSwitchWeapon(CBasePlayer* inPlayer, CBasePlayerItem* inWeapon); - - virtual bool GetCountdownStarted(void) const; - virtual bool GetGameStarted(void) const; - virtual int GetGameTime() const; - virtual void SetGameStarted(bool inGameStarted); - AvHEntityHierarchy& GetEntityHierarchy(AvHTeamNumber inTeam); - bool GetIsPlayerSelectableByPlayer(AvHPlayer* inTargetPlayer, AvHPlayer* inByPlayer); - virtual int IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled); - void ProcessTeamUpgrade(AvHMessageID inUpgrade, AvHTeamNumber inNumber, int inEntity, bool inGive = true); - - // Playtest functionality - void BalanceChanged(); - - // This isn't called yet, add in hooks? - void ClientKill( edict_t *pEntity ); - virtual BOOL CanHaveAmmo( CBasePlayer *pPlayer, const char *pszAmmoName, int iMaxCarry );// can this player take more of this ammo? - virtual bool CanPlayerBeKilled(CBasePlayer* inPlayer); - - virtual void ChangePlayerTeam(CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib); - virtual BOOL ClientCommand( CBasePlayer *pPlayer, const char *pcmd ); - virtual BOOL ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ); - virtual void ClientDisconnected( edict_t *pClient ); - - virtual int DeadPlayerAmmo( CBasePlayer *pPlayer ); - virtual int DeadPlayerWeapons( CBasePlayer *pPlayer ); - - virtual BOOL FAllowMonsters( void ); - virtual BOOL FPlayerCanRespawn( CBasePlayer *pPlayer ); - virtual BOOL FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ); - - // TODO: Add splash damage parameter and outgoing float percentage damage? This way splash damage could do some damage in non-tourny mode? - virtual bool CanEntityDoDamageTo(const CBaseEntity* inAttacker, const CBaseEntity* inReceiver, float* outScalar = NULL); - - //virtual edict_t* GetPlayerSpawnSpot( CBasePlayer *pPlayer ); - virtual void PlayerThink( CBasePlayer *pPlayer ); - - void ComputeWorldChecksum(Checksum& outChecksum) const; - float GetMapGamma(); - int GetNumCommandersOnTeam(AvHTeamNumber inTeam); - int GetNumActiveHives(AvHTeamNumber inTeam) const; - int GetNumEntities() const; - const AvHGameplay& GetGameplay() const; - const AvHMapExtents& GetMapExtents(); - - virtual BOOL IsMultiplayer( void ); - virtual BOOL IsDeathmatch( void ); - virtual BOOL IsCoOp( void ); - - virtual void InitHUD( CBasePlayer *pPlayer ); - virtual void PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); - virtual void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ); - virtual void PlayerSpawn( CBasePlayer *pPlayer ); - virtual void ProcessRespawnCostForPlayer(AvHPlayer* inPlayer); - - virtual void RewardPlayerForKill(AvHPlayer* inPlayer, CBaseEntity* inTarget, entvars_t* inInflictor = NULL); - bool RoamingAllowedForPlayer(CBasePlayer* inPlayer) const; - virtual void Think(void); - - void RegisterServerVariable(const char* inName); - int GetNumServerVariables() const; - const std::string& GetServerVariable(int i) const; - - bool GetCheatsEnabled(void) const; - bool GetIsCheatEnabled(const string& inCheatName) const; - void SetCheatEnabled(const string& inCheatName, bool inEnabledState = true); - - float GetFirstScanThinkTime() const; - bool GetDrawInvisibleEntities() const; - bool GetEntityExists(const char* inName) const; - bool GetIsTesting(void) const; - bool GetIsValidFutureTeam(AvHPlayer inPlayer, int inTeamNumber) const; - bool GetCanJoinTeamInFuture(AvHPlayer* inPlayer, AvHTeamNumber theTeamNumber, string& outString) const; - const AvHBaseInfoLocationListType& GetInfoLocations() const; - int GetMaxWeight(void) const; - const char* GetSpawnEntityName(AvHPlayer* inPlayer) const; - Vector GetSpawnAreaCenter(AvHTeamNumber inTeamNumber) const; - float GetTimeGameStarted() const; - int GetTimeLimit() const; - int GetWeightForItemAndAmmo(AvHWeaponID inWeapon, int inNumRounds) const; - bool AttemptToJoinTeam(AvHPlayer* inPlayer, AvHTeamNumber theTeamNumber, bool inDisplayErrorMessage = true); - const AvHTeam* GetTeam(AvHTeamNumber inTeamNumber) const; - const AvHTeam* GetTeamA() const; - const AvHTeam* GetTeamB() const; - AvHTeam* GetTeam(AvHTeamNumber inTeamNumber); - AvHTeam* GetTeamA(); - AvHTeam* GetTeamB(); - AvHMapMode GetMapMode(void) const; - int GetServerTick() const; - AvHTeamNumber GetVictoryTeam() const; - float GetVictoryTime() const; - //void QueueTeamUpgrade(edict_t* inPlayer, AvHMessageID inUpgrade); - void DeleteAndResetEntities(); - void PlayerDeathEnd(AvHPlayer* inPlayer); - void PostWorldPrecacheReset(bool inNewMap); - void PreWorldPrecacheReset(); - void RegisterSpawnPoint(const string& inClassName, const Vector& inOrigin, const Vector& inAngles, const AvHTeamNumber& inTeamNumber); - void RespawnPlayer(AvHPlayer* inPlayer); - - void TriggerAlert(AvHTeamNumber inTeamNumber, AvHAlertType inAlertType, int inEntIndex, AvHMessageID inMessageID = MESSAGE_NULL); - bool GetIsEntityUnderAttack(int inEntityIndex) const; - - virtual bool GetArePlayersAllowedToJoinImmediately(void) const; - virtual bool GetIsTournamentMode(void) const; - virtual bool GetIsHamboneMode(void) const; - virtual bool GetIsIronMan(void) const; - virtual bool GetIsCombatMode(void) const; - virtual AvHTeamNumber GetCombatAttackingTeamNumber() const; - virtual bool GetIsNSMode(void) const; - virtual bool GetIsScriptedMode(void) const; - virtual bool GetIsTrainingMode(void) const; - - int GetBaseHealthForMessageID(AvHMessageID inMessageID) const; - int GetBuildTimeForMessageID(AvHMessageID inMessageID) const; - int GetCostForMessageID(AvHMessageID inMessageID) const; - - CBaseEntity* GetRandomHiveSpawnPoint(CBaseEntity* inPlayer, const Vector& inOrigin, float inMaxDistance) const; - virtual edict_t* SelectSpawnPoint(CBaseEntity* inPlayer) const; - bool CanPlayerBeacon(CBaseEntity *inPlayer); - edict_t* SelectSpawnPoint(CBaseEntity* inEntity, const string& inSpawnEntityName) const; - const char* SetDefaultPlayerTeam(CBasePlayer *pPlayer); - - void MarkDramaticEvent(int inPriority, CBaseEntity* inPrimaryEntity, bool inDramatic = false, CBaseEntity* inSecondaryEntity = NULL) const; - void MarkDramaticEvent(int inPriority, short inPrimaryEntityIndex, bool inDramatic = false, short inSecondaryEntityIndex = 0) const; - void MarkDramaticEvent(int inPriority, short inPrimaryEntityIndex, bool inDramatic, entvars_t* inSecondaryEntity) const; - - virtual void RecalculateHandicap(void); - void ServerExit(); - - void VoteMap(int inPlayerIndex, int inMapIndex); - bool GetMapVoteStrings(StringList& outMapVoteList); - void RemovePlayerFromVotemap(int inPlayerIndex); - -protected: - void AutoAssignPlayer(AvHPlayer* inPlayer); - void PerformMapValidityCheck(); - virtual void RecalculateMapMode( void ); - bool GetDeathMatchMode(void) const; - //void PutPlayerIntoSpectateMode(AvHPlayer* inPlayer) const; - virtual void SendMOTDToClient( edict_t *client ); - - -// puzl: 0001073 -#ifdef USE_OLDAUTH - - void AddAuthStatus(AvHPlayerAuthentication inAuthMask, const string& inWONID, const string& inSteamID); - void DisplayVersioning(); - void InitializeAuthentication(); -#endif - -private: - void AwardExperience(AvHPlayer* inPlayer, int inTargetLevel, bool inAwardFriendliesInRange = true); - void CalculateMapExtents(); - void CalculateMapGamma(); - void CopyDataToSpawnEntity(const AvHSpawn& inSpawnEntity) const; - void JoinTeam(AvHPlayer* inPlayer, AvHTeamNumber theTeamNumber, bool inDisplayErrorMessage, bool inForce); - void PreWorldPrecacheInitParticles(); - void PostWorldPrecacheInitParticles(); - void InitializeMapVoteList(); - int GetVotesNeededForMapChange() const; - void InitializeTechNodes(); - void InternalResetGameRules(); - int GetNumberOfPlayers() const; - void TallyVictoryStats() const; - void PostVictoryStatsToWeb(const string& inFormParams) const; - bool ReadyToStartCountdown(); - void ResetGame(bool inPreserveTeams = false); - void SendGameTimeUpdate(bool inReliable = false); - void ProcessTeamUpgrades(); - void ResetEntities(); - CBaseEntity* SelectRandomSpawn(CBaseEntity* inPlayer, const string& inSpawnName) const; - void UpdateCheats(); - void UpdateHLTVProxy(); - void UpdatePlaytesting(); - void UpdateCountdown(float inTime); - void UpdateEntitiesUnderAttack(); - void UpdateGameTime(); - void UpdateLOS(); - void UpdateScripts(); - void UpdateServerCommands(); - void UpdateTimeLimit(); - void UpdateWorldEntities(); - void UpdateVictory(void); - void UpdateVictoryStatus(void); - - bool mFirstUpdate; - bool mPreserveTeams; - bool mGameStarted; - AvHTeamNumber mVictoryTeam; - float mTimeCountDownStarted; - float mTimeGameStarted; - float mTimeOfLastHLTVProxyUpdate; - float mTimeOfLastGameTimeUpdate; - float mTimeSentCountdown; - float mTimeLastWontStartMessageSent; - float mTimeWorldReset; - bool mStartedCountdown; - bool mSentCountdownMessage; - bool mVictoryDraw; - AvHTeam mTeamA; - AvHTeam mTeamB; - float mVictoryTime; - AvHMapMode mMapMode; -// puzl: 0001073 -#ifdef USE_OLDAUTH - bool mUpdatedUplink; - AuthIDListType mServerOpList; -#endif - - float mLastParticleUpdate; - float mLastNetworkUpdate; - float mLastWorldEntityUpdate; - float mLastCloakableUpdate; - float mLastVictoryUpdate; - float mLastMapChange; - - float mTimeOfLastPlaytestUpdate; - float mTimeOfLastHandicapUpdate; - - float mTimeUpdatedScripts; - - typedef vector TeamPurchaseListType; - TeamPurchaseListType mPendingTeamUpgrades; - - // Potentially marines vs. marines - AvHEntityHierarchy mTeamAEntityHierarchy; - AvHEntityHierarchy mTeamBEntityHierarchy; - - AvHGameplay mGameplay; - - bool mCalculatedMapGamma; - float mMapGamma; - - typedef map EntityUnderAttackListType; - EntityUnderAttackListType mEntitiesUnderAttack; - - AvHMiniMap mMiniMap; - - AvHMapExtents mMapExtents; - - StringList mCheats; - - AvHBaseInfoLocationListType mInfoLocations; - - float mSavedTimeCountDownStarted; - - SpawnListType mSpawnList; - mutable CBaseEntity* mSpawnEntity; - - // Map voting - MapVoteListType mMapVoteList; - PlayerMapVoteListType mPlayersVoted; - PlayerVoteTimeType mPlayersVoteTime; - - std::vector mServerVariableList; - - AvHTeamNumber mCombatAttackingTeamNumber; -}; - -AvHGamerules* GetGameRules(); -void SetGameRules(AvHGamerules* inGameRules); - -#endif +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: NS high-level game rules +// +// $Workfile: AvHGamerules.h $ +// $Date: 2002/11/15 04:46:18 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHGamerules.h,v $ +// Revision 1.52 2002/11/15 04:46:18 Flayra +// - Changes to for profiling and for improving AddToFullPack performance +// +// Revision 1.51 2002/11/12 02:24:57 Flayra +// - HLTV updates +// +// Revision 1.50 2002/11/06 01:40:01 Flayra +// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) +// +// Revision 1.49 2002/11/03 04:49:45 Flayra +// - Moved constants into .dll out of .cfg +// - Particle systems update much less often, big optimization +// - Team balance fixes +// +// Revision 1.48 2002/10/28 20:34:53 Flayra +// - Reworked game reset slightly, to meter out network usage during game reset +// +// Revision 1.47 2002/10/20 21:10:41 Flayra +// - Optimizations +// +// Revision 1.46 2002/10/16 00:56:22 Flayra +// - Prototyped curl support for NS stats +// - Added player auth stuff (server string) +// - Added concept of secondary weapons +// - Removed disconnect sound +// - Fixed MOTD +// - Added "order needed" alert +// +// Revision 1.45 2002/10/03 18:44:03 Flayra +// - Play disconnected sound quieter because of mass exodus effect +// - Added functionality to allow players to join a team and run around freely before game countdown starts. Game resets when countdown starts. +// - Added new alien alerts +// +// Revision 1.44 2002/09/23 22:16:01 Flayra +// - Added game victory status logging +// - Fixed commander alerts +// - Added new alerts +// - Particle system changes, only send them down when connecting or map changes +// +// Revision 1.43 2002/09/09 19:51:13 Flayra +// - Removed gamerules dictating alien respawn time, now it's in server variable +// +// Revision 1.42 2002/08/16 02:35:09 Flayra +// - Blips now update once per second, instead of once per player per second +// +// Revision 1.41 2002/08/09 01:01:42 Flayra +// - Removed error condition for map validity, allow marines to have only one primary weapon, adjust weapon weights +// +// Revision 1.40 2002/08/02 22:00:23 Flayra +// - New alert system that's not so annoying and is more helpful. Tweaks for new help system. +// +// Revision 1.39 2002/07/24 18:45:41 Flayra +// - Linux and scripting changes +// +// Revision 1.38 2002/07/23 17:03:21 Flayra +// - Resource model changes, refactoring spawning to fix level 5 redemption bug without code duplication +// +// Revision 1.37 2002/07/08 16:59:14 Flayra +// - Don't pick up empty weapons, added handicapping, check map validity when loaded, reset players just like all other entities, fixed spawn bug code where it was counting non-team spawns +// +// Revision 1.36 2002/07/01 21:32:44 Flayra +// - Visibility update now updates world for primal scream and umbra, don't update reliable network messages every tick (big optimization) +// +// Revision 1.35 2002/06/25 17:59:03 Flayra +// - Added timelimit (switches map after a game finishes), added info_locations, tried adding team balance (not sure it works yet), give resources for kills +// +// Revision 1.34 2002/06/10 19:54:30 Flayra +// - New minimap support, more attempts to fix picking up of alien weapons +// +// Revision 1.33 2002/05/28 17:40:32 Flayra +// - Minimap refactoring, hive sight "under attack", reinforcement refactoring, don't delete entities marked as permanent (it was deleting hives!), allow players to join the opposite team until this can be fixed for real (server retry gets around this code so this is just an inconvenience), things build fast with cheats +// +// Revision 1.32 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifndef AVH_GAMERULES_H +#define AVH_GAMERULES_H + +#include "dlls/gamerules.h" +//#include "mod/AvHCOCRuleset.h" +#include "mod/AvHTeam.h" +#include "mod/AvHMarineWeapons.h" +#include "types.h" +#include "mod/AvHMessage.h" +#include "mod/AvHEntityHierarchy.h" +#include "mod/AvHEntities.h" +#include "mod/AvHMiniMap.h" +#include "dlls/teamplay_gamerules.h" +#include "mod/AvHDramaticPriority.h" +#include "mod/AvHMapExtents.h" +#include "util/Tokenizer.h" +#include "mod/AvHSpawn.h" + +class AvHPlayer; + +class TeamPurchase +{ +public: + TeamPurchase(edict_t* inPlayer, AvHMessageID inMessageID) : mPlayer(inPlayer), mUpgrade(inMessageID) + {} + + edict_t* mPlayer; + AvHMessageID mUpgrade; +}; + +typedef vector< pair > MapVoteListType; +typedef map< int, int > PlayerMapVoteListType; +typedef map< int, float > PlayerVoteTimeType; +// : 0001073 +#ifdef USE_OLDAUTH +typedef vector< pair > AuthIDListType; +typedef map AuthMaskListType; +#endif + +class AvHGamerules : public CHalfLifeTeamplay //public CHalfLifeMultiplay/*, public AvHCOCRuleset*/ +{ +public: + AvHGamerules(); + virtual ~AvHGamerules(); + +// : 0001073 +#ifdef USE_OLDAUTH + virtual BOOL GetIsClientAuthorizedToPlay(edict_t* inEntity, bool inDisplayMessage, bool inForcePending = false) const; + virtual bool PerformHardAuthorization(AvHPlayer* inPlayer) const; + virtual int GetAuthenticationMask(const string& inAuthID) const; + const AuthIDListType& GetServerOpList() const; + void UpdateUplink(); +#endif + + // this is the game name that gets seen in the server browser + virtual int AmmoShouldRespawn( CBasePlayerAmmo *pAmmo ); + virtual void BuildableBuilt(AvHBuildable* inBuildable); + virtual void BuildableKilled(AvHBuildable* inBuildable); + virtual void BuildMiniMap(AvHPlayer* inPlayer); + virtual BOOL CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + virtual void ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer ); + virtual const char* GetGameDescription(void); + + virtual int WeaponShouldRespawn( CBasePlayerItem *pWeapon ); + virtual void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); + + virtual BOOL FShouldSwitchWeapon(CBasePlayer* inPlayer, CBasePlayerItem* inWeapon); + + virtual bool GetCountdownStarted(void) const; + virtual bool GetGameStarted(void) const; + virtual int GetGameTime() const; + virtual void SetGameStarted(bool inGameStarted); + AvHEntityHierarchy& GetEntityHierarchy(AvHTeamNumber inTeam); + bool GetIsPlayerSelectableByPlayer(AvHPlayer* inTargetPlayer, AvHPlayer* inByPlayer); + virtual int IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled); + void ProcessTeamUpgrade(AvHMessageID inUpgrade, AvHTeamNumber inNumber, int inEntity, bool inGive = true); + + // Playtest functionality + void BalanceChanged(); + + // This isn't called yet, add in hooks? + void ClientKill( edict_t *pEntity ); + virtual BOOL CanHaveAmmo( CBasePlayer *pPlayer, const char *pszAmmoName, int iMaxCarry );// can this player take more of this ammo? + virtual bool CanPlayerBeKilled(CBasePlayer* inPlayer); + + virtual void ChangePlayerTeam(CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib); + virtual BOOL ClientCommand( CBasePlayer *pPlayer, const char *pcmd ); + virtual BOOL ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ); + virtual void ClientDisconnected( edict_t *pClient ); + + virtual int DeadPlayerAmmo( CBasePlayer *pPlayer ); + virtual int DeadPlayerWeapons( CBasePlayer *pPlayer ); + + virtual BOOL FAllowMonsters( void ); + virtual BOOL FPlayerCanRespawn( CBasePlayer *pPlayer ); + virtual BOOL FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ); + + // TODO: Add splash damage parameter and outgoing float percentage damage? This way splash damage could do some damage in non-tourny mode? + virtual bool CanEntityDoDamageTo(const CBaseEntity* inAttacker, const CBaseEntity* inReceiver, float* outScalar = NULL); + + //virtual edict_t* GetPlayerSpawnSpot( CBasePlayer *pPlayer ); + virtual void PlayerThink( CBasePlayer *pPlayer ); + + void ComputeWorldChecksum(Checksum& outChecksum) const; + float GetMapGamma(); + int GetNumCommandersOnTeam(AvHTeamNumber inTeam); + int GetNumActiveHives(AvHTeamNumber inTeam) const; + int GetNumEntities() const; + const AvHGameplay& GetGameplay() const; + const AvHMapExtents& GetMapExtents(); + + virtual BOOL IsMultiplayer( void ); + virtual BOOL IsDeathmatch( void ); + virtual BOOL IsCoOp( void ); + + virtual void InitHUD( CBasePlayer *pPlayer ); + virtual void PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + virtual void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ); + virtual void PlayerSpawn( CBasePlayer *pPlayer ); + virtual void ProcessRespawnCostForPlayer(AvHPlayer* inPlayer); + + virtual void RewardPlayerForKill(AvHPlayer* inPlayer, CBaseEntity* inTarget, entvars_t* inInflictor = NULL); + bool RoamingAllowedForPlayer(CBasePlayer* inPlayer) const; + virtual void Think(void); + + void RegisterServerVariable(const cvar_t* inCvar); + int GetNumServerVariables() const; + const cvar_t* GetServerVariable(int i) const; + + bool GetCheatsEnabled(void) const; + bool GetIsCheatEnabled(const string& inCheatName) const; + void SetCheatEnabled(const string& inCheatName, bool inEnabledState = true); + + float GetFirstScanThinkTime() const; + bool GetDrawInvisibleEntities() const; + bool GetEntityExists(const char* inName) const; + bool GetIsTesting(void) const; + bool GetIsValidFutureTeam(AvHPlayer inPlayer, int inTeamNumber) const; + bool GetCanJoinTeamInFuture(AvHPlayer* inPlayer, AvHTeamNumber theTeamNumber, string& outString) const; + const AvHBaseInfoLocationListType& GetInfoLocations() const; + int GetMaxWeight(void) const; + const char* GetSpawnEntityName(AvHPlayer* inPlayer) const; + Vector GetSpawnAreaCenter(AvHTeamNumber inTeamNumber) const; + float GetTimeGameStarted() const; + int GetTimeLimit() const; + int GetWeightForItemAndAmmo(AvHWeaponID inWeapon, int inNumRounds) const; + bool AttemptToJoinTeam(AvHPlayer* inPlayer, AvHTeamNumber theTeamNumber, bool inDisplayErrorMessage = true); + const AvHTeam* GetTeam(AvHTeamNumber inTeamNumber) const; + const AvHTeam* GetTeamA() const; + const AvHTeam* GetTeamB() const; + AvHTeam* GetTeam(AvHTeamNumber inTeamNumber); + AvHTeam* GetTeamA(); + AvHTeam* GetTeamB(); + AvHMapMode GetMapMode(void) const; + int GetServerTick() const; + AvHTeamNumber GetVictoryTeam() const; + float GetVictoryTime() const; + //void QueueTeamUpgrade(edict_t* inPlayer, AvHMessageID inUpgrade); + void DeleteAndResetEntities(); + void PlayerDeathEnd(AvHPlayer* inPlayer); + void PostWorldPrecacheReset(bool inNewMap); + void PreWorldPrecacheReset(); + void RegisterSpawnPoint(const string& inClassName, const Vector& inOrigin, const Vector& inAngles, const AvHTeamNumber& inTeamNumber); + void RespawnPlayer(AvHPlayer* inPlayer); + + void TriggerAlert(AvHTeamNumber inTeamNumber, AvHAlertType inAlertType, int inEntIndex, AvHMessageID inMessageID = MESSAGE_NULL); + bool GetIsEntityUnderAttack(int inEntityIndex) const; + + virtual bool GetArePlayersAllowedToJoinImmediately(void) const; + virtual bool GetIsTournamentMode(void) const; + virtual bool GetIsHamboneMode(void) const; + virtual bool GetIsIronMan(void) const; + virtual bool GetIsCombatMode(void) const; + virtual AvHTeamNumber GetCombatAttackingTeamNumber() const; + virtual bool GetIsNSMode(void) const; + virtual bool GetIsTrainingMode(void) const; + + int GetBaseHealthForMessageID(AvHMessageID inMessageID) const; + int GetBuildTimeForMessageID(AvHMessageID inMessageID) const; + int GetCostForMessageID(AvHMessageID inMessageID) const; + + CBaseEntity* GetRandomHiveSpawnPoint(CBaseEntity* inPlayer, const Vector& inOrigin, float inMaxDistance) const; + virtual edict_t* SelectSpawnPoint(CBaseEntity* inPlayer) const; + bool CanPlayerBeacon(CBaseEntity *inPlayer); + edict_t* SelectSpawnPoint(CBaseEntity* inEntity, const string& inSpawnEntityName) const; + const char* SetDefaultPlayerTeam(CBasePlayer *pPlayer); + + void MarkDramaticEvent(int inPriority, CBaseEntity* inPrimaryEntity, bool inDramatic = false, CBaseEntity* inSecondaryEntity = NULL) const; + void MarkDramaticEvent(int inPriority, short inPrimaryEntityIndex, bool inDramatic = false, short inSecondaryEntityIndex = 0) const; + void MarkDramaticEvent(int inPriority, short inPrimaryEntityIndex, bool inDramatic, entvars_t* inSecondaryEntity) const; + + virtual void RecalculateHandicap(void); + void ServerExit(); + + void VoteMap(int inPlayerIndex, int inMapIndex); + bool GetMapVoteStrings(StringList& outMapVoteList); + void RemovePlayerFromVotemap(int inPlayerIndex); + + bool GetIsGameInReset() {return this->mGameInReset; }; + + int GetStructureLimit(); + void RemoveEntityUnderAttack(int entIndex); +protected: + void AutoAssignPlayer(AvHPlayer* inPlayer); + void PerformMapValidityCheck(); + virtual void RecalculateMapMode( void ); + bool GetDeathMatchMode(void) const; + //void PutPlayerIntoSpectateMode(AvHPlayer* inPlayer) const; + virtual void SendMOTDToClient( edict_t *client ); + + +// : 0001073 +#ifdef USE_OLDAUTH + + void AddAuthStatus(AvHPlayerAuthentication inAuthMask, const string& inWONID, const string& inSteamID); + void DisplayVersioning(); + void InitializeAuthentication(); +#endif + +private: + void AwardExperience(AvHPlayer* inPlayer, int inTargetLevel, bool inAwardFriendliesInRange = true); + void CalculateMapExtents(); + void CalculateMapGamma(); + void CopyDataToSpawnEntity(const AvHSpawn& inSpawnEntity) const; + void JoinTeam(AvHPlayer* inPlayer, AvHTeamNumber theTeamNumber, bool inDisplayErrorMessage, bool inForce); + void PreWorldPrecacheInitParticles(); + void PostWorldPrecacheInitParticles(); + void InitializeMapVoteList(); + int GetVotesNeededForMapChange() const; + void InitializeTechNodes(); + void InternalResetGameRules(); + int GetNumberOfPlayers(bool inPlayingGame=false) const; + void TallyVictoryStats() const; + void PostVictoryStatsToWeb(const string& inFormParams) const; + bool ReadyToStartCountdown(); + void ResetGame(bool inPreserveTeams = false); + void ResetPlayers(); + void SendGameTimeUpdate(bool inReliable = false); + void ProcessTeamUpgrades(); + void ResetEntities(); + CBaseEntity* SelectRandomSpawn(CBaseEntity* inPlayer, const string& inSpawnName) const; + void UpdateCheats(); + void UpdateHLTVProxy(); + void UpdatePlaytesting(); + void UpdateCountdown(float inTime); + void UpdateEntitiesUnderAttack(); + void UpdateGameTime(); + void UpdateLOS(); + void UpdateScripts(); + void UpdateServerCommands(); + void UpdateTimeLimit(); + void UpdateWorldEntities(); + void UpdateVictory(void); + void UpdateVictoryStatus(void); + + bool mFirstUpdate; + bool mPreserveTeams; + bool mGameStarted; + float mLastJoinMessage; + AvHTeamNumber mVictoryTeam; + float mTimeCountDownStarted; + float mTimeGameStarted; + float mTimeOfLastHLTVProxyUpdate; + float mTimeOfForcedLastHLTVProxyUpdate; + float mTimeOfLastHLTVParticleTemplateSending; + int mHLTVNumParticleTemplatesSent; + int mHLTVCurrentPlayer; + + AvHEntityHierarchy mHLTVEntityHierarchy; + float mTimeOfLastGameTimeUpdate; + float mTimeSentCountdown; + float mTimeLastWontStartMessageSent; + float mTimeWorldReset; + bool mStartedCountdown; + bool mSentCountdownMessage; + bool mVictoryDraw; + AvHTeam mTeamA; + AvHTeam mTeamB; + float mVictoryTime; + AvHMapMode mMapMode; +// : 0001073 +#ifdef USE_OLDAUTH + bool mUpdatedUplink; + AuthIDListType mServerOpList; +#endif + + float mLastParticleUpdate; + float mLastNetworkUpdate; + float mLastWorldEntityUpdate; + float mLastCloakableUpdate; + float mLastVictoryUpdate; + float mLastMapChange; + + float mTimeOfLastPlaytestUpdate; + float mTimeOfLastHandicapUpdate; + + float mTimeUpdatedScripts; + + typedef vector TeamPurchaseListType; + TeamPurchaseListType mPendingTeamUpgrades; + + // Potentially marines vs. marines + AvHEntityHierarchy mTeamAEntityHierarchy; + AvHEntityHierarchy mTeamBEntityHierarchy; + AvHEntityHierarchy mSpecEntityHierarchy; + + AvHGameplay mGameplay; + + bool mCalculatedMapGamma; + float mMapGamma; + + typedef map EntityUnderAttackListType; + EntityUnderAttackListType mEntitiesUnderAttack; + + bool mGameInReset; + + AvHMiniMap mMiniMap; + + AvHMapExtents mMapExtents; + + StringList mCheats; + + AvHBaseInfoLocationListType mInfoLocations; + + float mSavedTimeCountDownStarted; + + SpawnListType mSpawnList; + mutable CBaseEntity* mSpawnEntity; + + // Map voting + MapVoteListType mMapVoteList; + PlayerMapVoteListType mPlayersVoted; + PlayerVoteTimeType mPlayersVoteTime; + + std::vector mServerVariableList; + + AvHTeamNumber mCombatAttackingTeamNumber; + + bool mHasPlayersToReset; + float mLastPlayerResetTime; +}; + +AvHGamerules* GetGameRules(); +void SetGameRules(AvHGamerules* inGameRules); + +#endif diff --git a/main/source/mod/AvHGrenade.cpp b/main/source/mod/AvHGrenade.cpp index 43cbe5c..b524938 100644 --- a/main/source/mod/AvHGrenade.cpp +++ b/main/source/mod/AvHGrenade.cpp @@ -103,9 +103,7 @@ int AvHGrenade::GetDeployAnimation() const char* AvHGrenade::GetDeploySound() const { - //return kGRDeploySound; - return NULL; - + return kGRDeploySound; } float AvHGrenade::GetDeployTime() const @@ -301,7 +299,7 @@ void AvHGrenade::Precache(void) AvHMarineWeapon::Precache(); PRECACHE_UNMODIFIED_SOUND(kGRFireSound1); - //PRECACHE_UNMODIFIED_SOUND(kGRDeploySound); + PRECACHE_UNMODIFIED_SOUND(kGRDeploySound); PRECACHE_UNMODIFIED_SOUND(kGRExplodeSound); PRECACHE_UNMODIFIED_SOUND(kGRHitSound); @@ -380,7 +378,7 @@ void AvHGrenade::CreateProjectile() } // How to handle this? Only generate entity on server, but we should do SOMETHING on the client, no? - CGrenade* theGrenade = AvHSUShootServerGrenade(this->m_pPlayer->pev, theStartPosition, theVelocity, BALANCE_VAR(kGrenDetonateTime), true); + CGrenade* theGrenade = AvHSUShootServerGrenade(this->m_pPlayer->pev, theStartPosition, theVelocity, BALANCE_VAR(kHandGrenDetonateTime), true); ASSERT(theGrenade); theGrenade->pev->dmg = this->mDamage; diff --git a/main/source/mod/AvHHealingSpray.cpp b/main/source/mod/AvHHealingSpray.cpp index e1ed54e..d9929d0 100644 --- a/main/source/mod/AvHHealingSpray.cpp +++ b/main/source/mod/AvHHealingSpray.cpp @@ -165,11 +165,12 @@ void AvHHealingSpray::FireProjectiles(void) while((theCurrentEntity = UTIL_FindEntityInSphere(theCurrentEntity, theOriginatingPosition, kHealingSprayRange)) != NULL) { + bool isSelf=(theCurrentEntity == this->m_pPlayer); // Can't affect self - if(theCurrentEntity != this->m_pPlayer) - { +// if(theCurrentEntity != this->m_pPlayer) +// { // If entity is in view cone, and within range - if(this->m_pPlayer->FInViewCone(&theCurrentEntity->pev->origin)) + if(isSelf || this->m_pPlayer->FInViewCone(&theCurrentEntity->pev->origin) ) { // UTIL_FindEntityInSphere doesn't seem to take height into account. Make sure the entity is within range. float theMaxEntitySize = max(Length(theCurrentEntity->pev->mins), Length(theCurrentEntity->pev->maxs)); @@ -208,6 +209,7 @@ void AvHHealingSpray::FireProjectiles(void) // Players heal by base amount, plus percentage of health float thePercentage = BALANCE_VAR(kHealingSprayPlayerPercent)/100.0f; theDamage += thePercentage*theCurrentEntity->pev->max_health; + if ( isSelf ) theDamage *= 0.5f; thePlayer->Heal(theDamage, true); } else if(theBuildable) @@ -240,7 +242,7 @@ void AvHHealingSpray::FireProjectiles(void) } } } - } +// } } #endif diff --git a/main/source/mod/AvHHive.cpp b/main/source/mod/AvHHive.cpp index de2e1fd..acbe4dd 100644 --- a/main/source/mod/AvHHive.cpp +++ b/main/source/mod/AvHHive.cpp @@ -1,899 +1,1012 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHHive.cpp $ -// $Date: 2002/11/22 21:28:16 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHHive.cpp,v $ -// Revision 1.20 2002/11/22 21:28:16 Flayra -// - mp_consistency changes -// -// Revision 1.19 2002/11/06 01:38:37 Flayra -// - Added ability for buildings to be enabled and disabled, for turrets to be shut down -// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) -// -// Revision 1.18 2002/11/03 04:50:26 Flayra -// - Hard-coded gameplay constants instead of putting in skill.cfg -// -// Revision 1.17 2002/10/24 21:26:37 Flayra -// - Fixed hive wound animation when dying -// - Hives now choose a random spawn point instead of the first -// -// Revision 1.16 2002/10/16 20:53:21 Flayra -// - Hives have more health while growing -// -// Revision 1.15 2002/10/16 00:57:19 Flayra -// - Fixed hive not going solid sometimes (when player was in the way I think when construction complete) -// - Fixed exploit where hives can be manually sped up (oops!) -// -// Revision 1.14 2002/10/03 18:50:27 Flayra -// - Trigger "hive complete" alert -// - Trigger "hive is dying" alert -// -// Revision 1.13 2002/09/23 22:16:44 Flayra -// - Removed resource donation at hives -// -// Revision 1.12 2002/09/09 19:52:57 Flayra -// - Animations play properly -// - Hive can be hit once it starts gestating (hive becomes solid when gestating, not when complete) -// - Respawn fixes -// -// Revision 1.11 2002/08/16 02:36:01 Flayra -// - New damage system -// - Fixed bug where hive was absorbing too much damage in armor -// -// Revision 1.10 2002/08/02 21:59:36 Flayra -// - New alert system -// -// Revision 1.9 2002/07/26 23:04:19 Flayra -// - Generate numerical feedback for damage events -// -// Revision 1.8 2002/07/23 17:06:09 Flayra -// - Added ability for aliens to donate their resources at the hive, bind technology to a hive (so builders can choose the route), fixed bug where death animation played repeatedly -// -// Revision 1.7 2002/07/08 17:03:04 Flayra -// - Refactored reinforcements -// -// Revision 1.6 2002/07/01 21:33:48 Flayra -// - Hives can no longer be "used" to speed construction, wound sounds play on CHAN_BODY -// -// Revision 1.5 2002/06/25 18:00:14 Flayra -// - Play sequence for non-active hives -// -// Revision 1.4 2002/06/03 16:47:49 Flayra -// - Hives are base buildables now (bug with allowing use to speed building), added other hive anims for hurt, death, bad-touch, fixed bug where hives didn't get full health when they were initially built (after being killed once) -// -// Revision 1.3 2002/05/28 17:46:05 Flayra -// - Mark hives as persistent so they aren't deleted on level cleanup, new hive sight support, reinforcement refactoring and fixes -// -// Revision 1.2 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHHive.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "mod/AvHHulls.h" -#include "mod/AvHSoundListManager.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHParticleConstants.h" -#include "mod/AvHSpecials.h" -#include "mod/AvHPlayerUpgrade.h" - -extern AvHSoundListManager gSoundListManager; -BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ); - -LINK_ENTITY_TO_CLASS( keTeamHive, AvHHive ); - -extern int gRegenerationEventID; -const int kScaredAnimationIndex = 9; - -AvHHive::AvHHive() : AvHBaseBuildable(TECH_HIVE, ALIEN_BUILD_HIVE, kesTeamHive, AVH_USER3_HIVE) -{ - // This value should be the default in the .fgd - this->mMaxSpawnDistance = 2000; - this->mMaxHitPoints = 0; - this->mActive = false; - this->mSolid = false; - this->mSpawning = false; - this->mTimeLastWoundSound = -1; - this->mTechnology = MESSAGE_NULL; - this->mEnergy = 0.0f; -} - -bool AvHHive::CanBecomeActive() const -{ - return !this->mActive;// && (!this->mHasBeenKilled || !GetGameRules()->GetIsTournamentMode() || GetGameRules()->GetCheatsEnabled()); -} - -void AvHHive::ConstructUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue) -{ - int a = 0; -} - -void AvHHive::DonateUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue) -{ - // Player is trying to donate his resources to the pool - if(this->GetIsActive()) - { - AvHPlayer* inActivatingPlayer = dynamic_cast(inActivator); - if(inActivatingPlayer && (inActivator->pev->team == this->pev->team)) - { - // Take some resources, give some resources - const float kResourcesToDonatePerUse = .4f; - float theResourcesToGive = min(inActivatingPlayer->GetResources(), kResourcesToDonatePerUse); - - if(theResourcesToGive > 0.0f) - { - AvHTeam* theTeam = inActivatingPlayer->GetTeamPointer(); - if(theTeam) - { - inActivatingPlayer->SetResources(inActivatingPlayer->GetResources() - theResourcesToGive); - theTeam->SetTeamResources(theTeam->GetTeamResources() + theResourcesToGive); - - if(g_engfuncs.pfnRandomLong(0, 20) == 0) - { - PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - - // Just say "resources donated" - inActivatingPlayer->PlaybackNumericalEvent(kNumericalInfoResourcesDonatedEvent, 0); - } - } - } - } - } -} - -AvHTeamNumber AvHHive::GetTeamNumber() const -{ - return (AvHTeamNumber)this->pev->team; -} - -bool AvHHive::GetIsActive() const -{ - return this->mActive; -} - -bool AvHHive::GetIsOrganic() const -{ - return true; -} - -bool AvHHive::GetIsSpawning() const -{ - return this->mSpawning; -} - -int AvHHive::GetMaxSpawnDistance() const -{ - return this->mMaxSpawnDistance; -} - -int AvHHive::GetMoveType() const -{ - return MOVETYPE_NONE; -} - -float AvHHive::GetTimeLastContributed() -{ - return this->mTimeLastContributed; -} - -void AvHHive::SetTimeLastContributed(float inTime) -{ - this->mTimeLastContributed = inTime; -} - -int AvHHive::GetIdle1Animation() const -{ - int theAnimation = -1; - - if(this->GetIsBuilt()) - { - theAnimation = 2; - } - - return theAnimation; -} - -int AvHHive::GetIdle2Animation() const -{ - int theAnimation = -1; - - if(this->GetIsBuilt()) - { - theAnimation = 3; - } - - return theAnimation; -} - -int AvHHive::GetTakeDamageAnimation() const -{ - int theAnimation = -1; - - // Choose animation based on global time, so animation doesn't interrupt itself - float theTime = gpGlobals->time; - int theOffset = (int)(ceil(theTime) - theTime + .5f); - - if(this->GetIsActive()) - { - // Play wound animation. - theAnimation = 5 + theOffset; - } - else - { - // Use still-building flinch anims - theAnimation = 7 + theOffset; - } - - return theAnimation; -} - -int AvHHive::GetPointValue(void) const -{ - return BALANCE_VAR(kScoringHiveValue); -} - -int AvHHive::GetSpawnAnimation() const -{ - return 0; -} - -AvHMessageID AvHHive::GetTechnology() const -{ - return this->mTechnology; -} - -void AvHHive::SetTechnology(AvHMessageID inMessageID) -{ - this->mTechnology = inMessageID; -} - -void AvHHive::HiveAliveThink(void) -{ - // For some reason, velocity is non-zero when created (meant they were showing up on motion-tracking) - this->pev->velocity = Vector(0, 0, 0); - - if(GetGameRules()->GetGameStarted()) - { - if(!this->mActive) - { - bool theIsBuilding, theIsResearching; - float thePercentage; - AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); - - float theBuildTime = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID()); - float theBuildPercentage = kHiveAliveThinkInterval/theBuildTime; - - float theNewPercentage = min(thePercentage + theBuildPercentage, 1.0f); - this->SetNormalizedBuildPercentage(theNewPercentage); - } - else - { - this->ProcessHealing(); - - // Play idle anims - AvHBaseBuildable::AnimateThink(); - } - - this->UpdateReinforcements(); - - //this->UpdateUmbra(); - } - - // Set next think - this->pev->nextthink = gpGlobals->time + kHiveAliveThinkInterval; -} - -void AvHHive::UpdateUmbra() -{ - bool theIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(this->entindex()); - if(theIsUnderAttack) - { - if(this->mTimeOfNextUmbra == -1) - { - this->mTimeOfNextUmbra = gpGlobals->time + RANDOM_LONG(5, 15); - } - } - - if((this->mTimeOfNextUmbra != -1) && (gpGlobals->time > this->mTimeOfNextUmbra)) - { - // If we're under attack, sometimes create umbra at hive - vec3_t theUmbraOrigin = this->pev->origin; - - // else create umbra at random spawn -// if(!theIsUnderAttack) -// { -// CBaseEntity* theSpawnPoint = GetGameRules()->GetRandomHiveSpawnPoint(this, this->pev->origin, this->GetMaxSpawnDistance()); -// if(theSpawnPoint) -// { -// VectorCopy(theSpawnPoint->pev->origin, theUmbraOrigin); -// } -// } - - // Create umbra around it, play "scared" anim - //this->CreateUmbra(theUmbraOrigin); - - this->PlayAnimationAtIndex(kScaredAnimationIndex, true); - } -} - -void AvHHive::KeyValue(KeyValueData* pkvd) -{ - this->SetPersistent(); - - if(FStrEq(pkvd->szKeyName, "maxspawndistance")) - { - this->mMaxSpawnDistance = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - AvHBaseBuildable::KeyValue(pkvd); - } -} - -void AvHHive::TriggerDeathAudioVisuals() -{ - AvHSUPlayParticleEvent(kpsHiveDeath, this->edict(), this->pev->origin); - - AvHSUExplodeEntity(this, matFlesh); - - EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveDeathSound, 1.0, ATTN_IDLE); - - // Play death animation (increment time just to make sure there's no idle anim played after killed and before death) - const float kDeathAnimationLength = 1.2f; - - this->PlayAnimationAtIndex(10, true); - - // Then explode - //SetThink(&AvHHive::DeathThink); - //this->pev->nextthink = gpGlobals->time + kDeathAnimationLength; - - this->FireDeathTarget(); -} - -void AvHHive::Precache(void) -{ - AvHBaseBuildable::Precache(); - - PRECACHE_UNMODIFIED_SOUND(kHiveSpawnSound); - PRECACHE_UNMODIFIED_SOUND(kHiveAmbientSound); - PRECACHE_UNMODIFIED_SOUND(kHiveDeathSound); - - PRECACHE_UNMODIFIED_MODEL(kHiveModel); - - CBreakable::PrecacheAll(); -} - -void AvHHive::ProcessHealing() -{ - // Regenerate nearby friendly aliens - CBaseEntity* theEntity = NULL; - const int theHiveHealRadius = BALANCE_VAR(kHiveHealRadius); - - while((theEntity = UTIL_FindEntityInSphere(theEntity, this->pev->origin, theHiveHealRadius)) != NULL) - { - AvHPlayer* thePlayer = dynamic_cast(theEntity); - if(thePlayer) - { - if(thePlayer->GetIsRelevant() && (thePlayer->GetTeam() == this->GetTeamNumber()) && !thePlayer->GetIsBeingDigested()) - { - // Hive heals percentage of player health - float theRegenPercentage = BALANCE_VAR(kHiveRegenerationPercentage); - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3, thePlayer->GetExperienceLevel()); - float theRegenAmount = (theRegenPercentage*theMaxHealth); - thePlayer->Heal(theRegenAmount, true); - } - } - } - - // Regenerate self - bool theDidHeal = false; - - // If we aren't at full health, heal health - if(this->pev->health < this->mMaxHitPoints) - { - float theHiveRegenAmount = BALANCE_VAR(kHiveRegenerationAmount); - float theCombatModeScalar = /*GetGameRules()->GetIsCombatMode() ? (1.0f/BALANCE_VAR(kCombatModeTimeScalar)) :*/ 1.0f; - - this->pev->health = min((float)this->mMaxHitPoints, this->pev->health + theHiveRegenAmount*theCombatModeScalar); - theDidHeal = true; - } - - // Play regen event - if(theDidHeal) - { - // Play regeneration event - PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - } -} - -void AvHHive::ResetEntity(void) -{ - AvHReinforceable::ResetEntity(); - AvHBaseBuildable::ResetEntity(); - - SetUse(&AvHHive::ConstructUse); - - this->ResetCloaking(); - - this->SetInactive(); - - this->pev->health = this->mMaxHitPoints; - - this->pev->takedamage = DAMAGE_NO; - - // Reset parasites, etc. - this->pev->iuser4 = 0; - this->SetPersistent(); - - this->mTimeOfNextUmbra = -1; - - // Reset fuser1 progress - this->pev->fuser1 = 0; -} - -void AvHHive::ResetReinforcingPlayer(bool inSuccess) -{ - AvHReinforceable::ResetReinforcingPlayer(inSuccess); - - this->mEnergy = 0.0f; -} - -bool AvHHive::SetSolid(bool inForce) -{ - // Check to make sure there aren't any players in the destination area - CBaseEntity* pList[128]; - - // Crank up the area just to be safe - Vector theMinArea = this->pev->origin + kHiveMinSize; - Vector theMaxArea = this->pev->origin + kHiveMaxSize; - - // TODO: If players are blocking this area for too long, spawn hive and kill them - int theNumBlockingEntities = UTIL_EntitiesInBox(pList, 128, theMinArea, theMaxArea, FL_CLIENT); - if((theNumBlockingEntities == 0) || inForce) - { - this->pev->solid = SOLID_BBOX; - this->pev->movetype = MOVETYPE_NONE; - - UTIL_SetSize(this->pev, kHiveMinSize, kHiveMaxSize); - - // pev->frame = 0; - // pev->body = 3; - // pev->sequence = 0; - // // ResetSequenceInfo( ); - // pev->framerate = 0; - // - // UTIL_SetOrigin(pev, pev->origin); - // UTIL_SetSize(pev, Vector(-20, -20, 0), Vector(20, 20, 28) ); - - SetTouch(&AvHHive::HiveTouch); - - this->mSolid = true; - } - - return this->mSolid; -} - -void AvHHive::SetHasBeenBuilt() -{ - AvHBuildable::SetHasBeenBuilt(); - - GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_HIVE_COMPLETE, this->entindex()); - - // Make hive support any unassigned upgrade technologies (happens after a hive supporting a technology is destroyed and then rebuilt) - AvHTeamNumber theTeam = (AvHTeamNumber)this->pev->team; - AvHTeam* theTeamPointer = GetGameRules()->GetTeam(theTeam); - if(theTeamPointer) - { - AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); - - if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_DEFENSE) > 0) - { - AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_DEFENSE_CHAMBER); - } - - if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_MOVEMENT) > 0) - { - AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_MOVEMENT_CHAMBER); - } - - if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_SENSORY) > 0) - { - AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_SENSORY_CHAMBER); - } - } -} - -bool AvHHive::StartSpawningForTeam(AvHTeamNumber inTeam, bool inForce) -{ - bool theSuccess = false; - - if(this->SetSolid(inForce)) - { - this->pev->team = inTeam; - this->pev->takedamage = DAMAGE_YES; - - this->pev->rendermode = kRenderNormal; - this->pev->renderamt = 0; - - SetBits(pev->flags, FL_MONSTER); - SetUpgradeMask(&this->pev->iuser4, MASK_BUILDABLE); - - this->mSpawning = true; - this->pev->health = kBaseHealthPercentage*this->mBaseHealth; - - // Looping, growing animation - this->pev->sequence = 0; - this->pev->frame = 0; - ResetSequenceInfo(); - - this->pev->nextthink = gpGlobals->time + kHiveAliveThinkInterval; - SetThink(&AvHHive::HiveAliveThink); - - theSuccess = true; - } - - return theSuccess; -} - - -void AvHHive::Spawn() -{ - this->Precache(); - - AvHBaseBuildable::Spawn(); - - this->pev->classname = MAKE_STRING(kesTeamHive); - //this->pev->movetype = MOVETYPE_FLY; - - this->pev->movetype = MOVETYPE_FLY; - this->pev->solid = SOLID_NOT; - this->pev->flags = 0; - this->pev->iuser3 = AVH_USER3_HIVE; - - this->mMaxHitPoints = GetGameRules()->GetBaseHealthForMessageID(ALIEN_BUILD_HIVE); - - SET_MODEL( ENT(this->pev), kHiveModel); - //this->pev->scale = 2; - -// this->pev->sequence = 0; -// this->pev->frame = 0; -// ResetSequenceInfo(); - - this->ResetEntity(); - - SetUse(&AvHHive::ConstructUse); -} - -void AvHHive::SetActive() -{ - AvHBaseBuildable::SetActive(); - - if(!this->mActive) - { - this->mActive = true; - this->mSpawning = false; - - this->mTimeLastContributed = gpGlobals->time; - - // Start animating - this->pev->sequence = 1; - this->pev->frame = 0; - ResetSequenceInfo(); - //AvHSUSetCollisionBoxFromSequence(this->pev); - - // Play spawn sound here - EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveSpawnSound, 1.0, ATTN_IDLE); - - // Note: this isn't being created for the first hive because this sound plays before the map is totally up - UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kHiveAmbientSound, 1.0f, 2.0, 0, 100); - - this->FireSpawnTarget(); - - //SetUse(&AvHHive::DonateUse); - } -} - -void AvHHive::SetInactive() -{ - AvHBaseBuildable::SetInactive(); - - // Set this so hives can be drawn translucently at hive locations for aliens - this->pev->effects &= ~EF_NODRAW; - - this->ResetReinforcingPlayer(false); - - this->mActive = false; - this->mSpawning = false; - this->mSolid = false; - this->mTimeLastContributed = -1; - this->mTechnology = MESSAGE_NULL; - - this->pev->health = 0; - this->pev->takedamage = DAMAGE_NO; - this->pev->dmgtime = gpGlobals->time; - this->pev->solid = SOLID_NOT; - this->pev->team = TEAM_IND; - SetUpgradeMask(&this->pev->iuser4, MASK_BUILDABLE, false); - - this->pev->rendermode = kRenderTransAlpha; - this->pev->renderamt = 0; - - // Stop animation - this->pev->sequence = 0; - this->pev->frame = 0; - this->pev->framerate = 0; - - // No longer built at all - this->pev->fuser1 = 0.0f; - SetThink(NULL); - - // Stop looping - UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kHiveAmbientSound, 1.0f, .5, SND_STOP, 100); - - ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? - - SetTouch(NULL); -} - -int AvHHive::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - const float kWoundSoundInterval = 1.0f; - - int theReturnCode = 0; - - if(this->mActive || this->mSpawning) - { - //CBaseEntity* theAttackingEntity = CBaseEntity::Instance(pevAttacker); - //if(GetGameRules()->CanEntityDoDamageTo(theAttackingEntity, this)) - //{ - theReturnCode = AvHBaseBuildable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); - - if(theReturnCode > 0) - { - const float kDyingThreshold = .4f; - if(this->pev->health < kDyingThreshold*this->mMaxHitPoints) - { - GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_HIVE_DYING, this->entindex()); - } - else - { - GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_UNDER_ATTACK, this->entindex()); - } - - if((this->mTimeLastWoundSound == -1) || ((this->mTimeLastWoundSound + kWoundSoundInterval) < gpGlobals->time)) - { - // Pick a random wound sound to play - //int theIndex = RANDOM_LONG(0, kNumWoundSounds - 1); - //char* theSoundToPlay = kWoundSoundList[theIndex]; - //EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveDeathSound, 1.0, ATTN_IDLE); - - //EMIT_SOUND(ENT(this->pev), CHAN_AUTO, "misc/hive_wound1.wav", 1.0, ATTN_IDLE); - - // Emit hive damaged sound - gSoundListManager.PlaySoundInList(kHiveWoundSoundList, this, CHAN_BODY); - - this->mTimeLastWoundSound = gpGlobals->time; - } - } - //} - } - - return theReturnCode; -} - - - -bool AvHHive::GetCanReinforce() const -{ - return (this->GetIsBuilt() && this->IsAlive() && !GetGameRules()->GetIsCombatMode()); -} - -bool AvHHive::GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const -{ - bool theSuccess = false; - - CBaseEntity* theSpawnPoint = GetGameRules()->GetRandomHiveSpawnPoint(inPlayer, this->pev->origin, this->GetMaxSpawnDistance()); - if(theSpawnPoint) - { - outLocation = theSpawnPoint->pev->origin; - theSuccess = true; - } - - return theSuccess; -} - -bool AvHHive::GetTriggerAlertOnDamage() const -{ - return false; -} - -AvHTeamNumber AvHHive::GetReinforceTeamNumber() const -{ - return AvHBaseBuildable::GetTeamNumber(); -} - - -//void AvHHive::UpdateReinforcements() -//{ -// // If hive is active -// if(this->GetIsActive()) -// { -// // Test to make sure our reinforcing player is still valid -// AvHPlayer* theReinforcingPlayer = this->GetReinforcingPlayer(); -// if(theReinforcingPlayer) -// { -// AvHPlayMode thePlayMode = theReinforcingPlayer->GetPlayMode(); -// if((theReinforcingPlayer->pev->team != this->pev->team) || (thePlayMode == PLAYMODE_UNDEFINED) || (thePlayMode == PLAYMODE_READYROOM) || (thePlayMode == PLAYMODE_OBSERVER)) -// { -// this->mReinforcingPlayer = -1; -// } -// } -// -// // If hive isn't spawning a player in currently -// if(this->mReinforcingPlayer == -1) -// { -// // Find player on this team that's been waiting the longest -// AvHPlayer* thePlayerToSpawn = NULL; -// -// FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*); -// if(theEntity->GetTeam() == this->GetTeamNumber()) -// { -// if(theEntity->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) -// { -// if(!thePlayerToSpawn || (theEntity->GetTimeLastPlayModeSet() < thePlayerToSpawn->GetTimeLastPlayModeSet())) -// { -// thePlayerToSpawn = theEntity; -// break; -// } -// } -// } -// END_FOR_ALL_ENTITIES(kAvHPlayerClassName); -// -// // Spawn the one waiting the longest -// if(thePlayerToSpawn) -// { -// // Set the player to be reinforcing -// this->mReinforcingPlayer = thePlayerToSpawn->entindex(); -// -// thePlayerToSpawn->SetPlayMode(PLAYMODE_REINFORCING); -// -// // Play hive animation, play effect for player? -// this->pev->sequence = 4; -// this->pev->frame = 0; -// ResetSequenceInfo(); -// } -// } -// // else hive is spawning a player -// else -// { -// // Is player still valid, or has he left the server/team? -// AvHPlayer* thePlayer = this->GetReinforcingPlayer(); -// if(thePlayer && (thePlayer->GetTeam() == this->GetTeamNumber())) -// { -// if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING) -// { -// // Has enough time passed to bring the player in? -// const float kHiveRespawnTime = GetGameRules()->GetAlienRespawnTime(); -// -// if(gpGlobals->time > (thePlayer->GetTimeLastPlayModeSet() + kHiveRespawnTime)) -// { -// this->ResetReinforcingPlayer(true); -// -// // Take away points from the player if possible -// float theNewPointTotal = max(thePlayer->GetResources() - GetGameRules()->GetGameplay().GetAlienRespawnCost(), 0.0f); -// thePlayer->SetResources(theNewPointTotal); -// } -// } -// else -// { -// this->mReinforcingPlayer = -1; -// } -// } -// } -// } -//} - -void AvHHive::CreateUmbra(vec3_t& inOrigin) -{ - AvHSUCreateUmbraCloud(inOrigin, AvHTeamNumber(this->pev->team), this); - - // Don't create another for a bit - this->mTimeOfNextUmbra = -1; -} - -void AvHHive::CueRespawnEffect(AvHPlayer* inPlayer) -{ - // Play hive animation, play effect for player? - this->pev->sequence = 4; - this->pev->frame = 0; - ResetSequenceInfo(); - - // Create umbra around spawning players, but not until after late-join period (to avoid a ton of umbras all at once) - if(!GetGameRules()->GetArePlayersAllowedToJoinImmediately()) - { - //this->CreateUmbra(inPlayer->pev->origin); - } -} - -float AvHHive::GetReinforceTime() const -{ - const float kMaxRespawnTime = BALANCE_VAR(kAlienRespawnTime); - - float theRespawnTime = (kMaxRespawnTime - kMaxRespawnTime*this->mEnergy); - - // puzl 0000854 - // Decrease respawn wait time for aliens (NS: Classic) - // With one hive, for every player above six on the alien team, - // reduce the per-player respawn wait time by two-thirds of a second. - // With two hives, make the reduction one-third of a second. - // With three (or more, in the case of weird custom maps) hives, do not apply it. - - AvHTeam* theTeam = GetGameRules()->GetTeam(GetTeamNumber()); - ASSERT(theTeam); - - int thePlayerModifier = theTeam->GetPlayerCount() - BALANCE_VAR(kAlienRespawnPlayerModifier); - int theHiveCount = GetGameRules()->GetNumActiveHives(GetTeamNumber()); - - if ( thePlayerModifier > 0 && theHiveCount < 3 ) - { - float theTimeModifier = BALANCE_VAR(kAlienRespawnTimeModifier); - - // For one hive double the modifier - if ( theHiveCount == 1 ) - { - theTimeModifier *= 2.0f; - } - - theRespawnTime -= theTimeModifier * (float)thePlayerModifier; - } - - theRespawnTime = min(max(theRespawnTime, 0.0f), kMaxRespawnTime); - - return theRespawnTime; -} - -bool AvHHive::Energize(float inEnergyAmount) -{ - bool theSuccess = false; - - // Only energize when a player is in the cue - if(this->GetIsBuilt() && this->GetReinforcingPlayer()) - { - if(this->mEnergy < 1.0f) - { - this->mEnergy += inEnergyAmount; - this->mEnergy = min(max(0.0f, this->mEnergy), 1.0f); - theSuccess = true; - } - } - - return theSuccess; -} - -void AvHHive::HiveTouch(CBaseEntity* inOther) -{ - AvHPlayer* thePlayer = dynamic_cast(inOther); - if(thePlayer && (thePlayer->pev->team != this->pev->team)) - { - if(this->GetIsActive()) - { - // Play scared animation, it recoils from human touch - this->PlayAnimationAtIndex(kScaredAnimationIndex, true); - } - } - - AvHBaseBuildable::BuildableTouch(inOther); -} +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHHive.cpp $ +// $Date: 2002/11/22 21:28:16 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHHive.cpp,v $ +// Revision 1.20 2002/11/22 21:28:16 Flayra +// - mp_consistency changes +// +// Revision 1.19 2002/11/06 01:38:37 Flayra +// - Added ability for buildings to be enabled and disabled, for turrets to be shut down +// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) +// +// Revision 1.18 2002/11/03 04:50:26 Flayra +// - Hard-coded gameplay constants instead of putting in skill.cfg +// +// Revision 1.17 2002/10/24 21:26:37 Flayra +// - Fixed hive wound animation when dying +// - Hives now choose a random spawn point instead of the first +// +// Revision 1.16 2002/10/16 20:53:21 Flayra +// - Hives have more health while growing +// +// Revision 1.15 2002/10/16 00:57:19 Flayra +// - Fixed hive not going solid sometimes (when player was in the way I think when construction complete) +// - Fixed exploit where hives can be manually sped up (oops!) +// +// Revision 1.14 2002/10/03 18:50:27 Flayra +// - Trigger "hive complete" alert +// - Trigger "hive is dying" alert +// +// Revision 1.13 2002/09/23 22:16:44 Flayra +// - Removed resource donation at hives +// +// Revision 1.12 2002/09/09 19:52:57 Flayra +// - Animations play properly +// - Hive can be hit once it starts gestating (hive becomes solid when gestating, not when complete) +// - Respawn fixes +// +// Revision 1.11 2002/08/16 02:36:01 Flayra +// - New damage system +// - Fixed bug where hive was absorbing too much damage in armor +// +// Revision 1.10 2002/08/02 21:59:36 Flayra +// - New alert system +// +// Revision 1.9 2002/07/26 23:04:19 Flayra +// - Generate numerical feedback for damage events +// +// Revision 1.8 2002/07/23 17:06:09 Flayra +// - Added ability for aliens to donate their resources at the hive, bind technology to a hive (so builders can choose the route), fixed bug where death animation played repeatedly +// +// Revision 1.7 2002/07/08 17:03:04 Flayra +// - Refactored reinforcements +// +// Revision 1.6 2002/07/01 21:33:48 Flayra +// - Hives can no longer be "used" to speed construction, wound sounds play on CHAN_BODY +// +// Revision 1.5 2002/06/25 18:00:14 Flayra +// - Play sequence for non-active hives +// +// Revision 1.4 2002/06/03 16:47:49 Flayra +// - Hives are base buildables now (bug with allowing use to speed building), added other hive anims for hurt, death, bad-touch, fixed bug where hives didn't get full health when they were initially built (after being killed once) +// +// Revision 1.3 2002/05/28 17:46:05 Flayra +// - Mark hives as persistent so they aren't deleted on level cleanup, new hive sight support, reinforcement refactoring and fixes +// +// Revision 1.2 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== + +#include "mod/AvHHive.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHAlienAbilityConstants.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "mod/AvHHulls.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHSoundListManager.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHParticleConstants.h" +#include "mod/AvHSpecials.h" +#include "mod/AvHPlayerUpgrade.h" +#include "util/MathUtil.h" +#include + +extern AvHSoundListManager gSoundListManager; +BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ); + +LINK_ENTITY_TO_CLASS( keTeamHive, AvHHive ); + +extern int gRegenerationEventID; +const int kScaredAnimationIndex = 9; + +AvHHive::AvHHive() : AvHBaseBuildable(TECH_HIVE, ALIEN_BUILD_HIVE, kesTeamHive, AVH_USER3_HIVE) +{ + // This value should be the default in the .fgd + this->mMaxSpawnDistance = 2000; + this->mMaxHitPoints = 0; + this->mActive = false; + this->mSolid = false; + this->mSpawning = false; + this->mTimeLastWoundSound = -1; + this->mTechnology = MESSAGE_NULL; + this->mEnergy = 0.0f; + this->mLastTimeScannedHives=-1.0f; + this->mTimeEmergencyUseEnabled=-1.0f; + this->mTeleportHiveIndex=-1; + +} + +bool AvHHive::CanBecomeActive() const +{ + return !this->mActive;// && (!this->mHasBeenKilled || !GetGameRules()->GetIsTournamentMode() || GetGameRules()->GetCheatsEnabled()); +} + +void AvHHive::ConstructUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue) +{ + int a = 0; +} + +void AvHHive::DonateUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue) +{ + // Player is trying to donate his resources to the pool + if(this->GetIsActive()) + { + AvHPlayer* inActivatingPlayer = dynamic_cast(inActivator); + if(inActivatingPlayer && (inActivator->pev->team == this->pev->team)) + { + // Take some resources, give some resources + const float kResourcesToDonatePerUse = .4f; + float theResourcesToGive = min(inActivatingPlayer->GetResources(), kResourcesToDonatePerUse); + + if(theResourcesToGive > 0.0f) + { + AvHTeam* theTeam = inActivatingPlayer->GetTeamPointer(); + if(theTeam) + { + inActivatingPlayer->SetResources(inActivatingPlayer->GetResources() - theResourcesToGive); + theTeam->SetTeamResources(theTeam->GetTeamResources() + theResourcesToGive); + + if(g_engfuncs.pfnRandomLong(0, 20) == 0) + { + PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + + // Just say "resources donated" + inActivatingPlayer->PlaybackNumericalEvent(kNumericalInfoResourcesDonatedEvent, 0); + } + } + } + } + } +} + +AvHTeamNumber AvHHive::GetTeamNumber() const +{ + return (AvHTeamNumber)this->pev->team; +} + +bool AvHHive::GetIsActive() const +{ + return this->mActive; +} + +bool AvHHive::GetIsOrganic() const +{ + return true; +} + +bool AvHHive::GetIsSpawning() const +{ + return this->mSpawning; +} + +int AvHHive::GetMaxSpawnDistance() const +{ + return this->mMaxSpawnDistance; +} + +int AvHHive::GetMoveType() const +{ + return MOVETYPE_NONE; +} + +float AvHHive::GetTimeLastContributed() +{ + return this->mTimeLastContributed; +} + +void AvHHive::SetTimeLastContributed(float inTime) +{ + this->mTimeLastContributed = inTime; +} + +int AvHHive::GetIdle1Animation() const +{ + int theAnimation = -1; + + if(this->GetIsBuilt()) + { + theAnimation = 2; + } + + return theAnimation; +} + +int AvHHive::GetIdle2Animation() const +{ + int theAnimation = -1; + + if(this->GetIsBuilt()) + { + theAnimation = 3; + } + + return theAnimation; +} + +int AvHHive::GetTakeDamageAnimation() const +{ + int theAnimation = -1; + + // Choose animation based on global time, so animation doesn't interrupt itself + float theTime = gpGlobals->time; + int theOffset = (int)(ceil(theTime) - theTime + .5f); + + if(this->GetIsActive()) + { + // Play wound animation. + theAnimation = 5 + theOffset; + } + else + { + // Use still-building flinch anims + theAnimation = 7 + theOffset; + } + + return theAnimation; +} + +int AvHHive::GetPointValue(void) const +{ + return BALANCE_VAR(kScoringHiveValue); +} + +int AvHHive::GetSpawnAnimation() const +{ + return 0; +} + +AvHMessageID AvHHive::GetTechnology() const +{ + return this->mTechnology; +} + +void AvHHive::SetTechnology(AvHMessageID inMessageID) +{ + this->mTechnology = inMessageID; +} + +void AvHHive::HiveAliveThink(void) +{ + // For some reason, velocity is non-zero when created (meant they were showing up on motion-tracking) + this->pev->velocity = Vector(0, 0, 0); + + if(GetGameRules()->GetGameStarted()) + { + if(!this->mActive) + { + bool theIsBuilding, theIsResearching; + float thePercentage; + AvHSHUGetBuildResearchState(this->pev->iuser3, this->pev->iuser4, this->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); + + float theBuildTime = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID()); + float theBuildPercentage = kHiveAliveThinkInterval/theBuildTime; + + float theNewPercentage = min(thePercentage + theBuildPercentage, 1.0f); + this->SetNormalizedBuildPercentage(theNewPercentage); + } + else + { + this->ProcessHealing(); + + // Play idle anims + AvHBaseBuildable::AnimateThink(); + } + + this->UpdateReinforcements(); + + //this->UpdateUmbra(); + } + + // Set next think + this->pev->nextthink = gpGlobals->time + kHiveAliveThinkInterval; +} + +void AvHHive::UpdateUmbra() +{ + bool theIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(this->entindex()); + if(theIsUnderAttack) + { + if(this->mTimeOfNextUmbra == -1) + { + this->mTimeOfNextUmbra = gpGlobals->time + RANDOM_LONG(5, 15); + } + } + + if((this->mTimeOfNextUmbra != -1) && (gpGlobals->time > this->mTimeOfNextUmbra)) + { + // If we're under attack, sometimes create umbra at hive + vec3_t theUmbraOrigin = this->pev->origin; + + // else create umbra at random spawn +// if(!theIsUnderAttack) +// { +// CBaseEntity* theSpawnPoint = GetGameRules()->GetRandomHiveSpawnPoint(this, this->pev->origin, this->GetMaxSpawnDistance()); +// if(theSpawnPoint) +// { +// VectorCopy(theSpawnPoint->pev->origin, theUmbraOrigin); +// } +// } + + // Create umbra around it, play "scared" anim + //this->CreateUmbra(theUmbraOrigin); + + this->PlayAnimationAtIndex(kScaredAnimationIndex, true); + } +} + +void AvHHive::KeyValue(KeyValueData* pkvd) +{ + this->SetPersistent(); + + if(FStrEq(pkvd->szKeyName, "maxspawndistance")) + { + this->mMaxSpawnDistance = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + AvHBaseBuildable::KeyValue(pkvd); + } +} + +void AvHHive::TriggerDeathAudioVisuals(bool isRecycled) +{ + AvHSUPlayParticleEvent(kpsHiveDeath, this->edict(), this->pev->origin); + + AvHSUExplodeEntity(this, matFlesh); + + EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveDeathSound, 1.0, ATTN_IDLE); + + // Play death animation (increment time just to make sure there's no idle anim played after killed and before death) + const float kDeathAnimationLength = 1.2f; + + this->PlayAnimationAtIndex(10, true); + + // Then explode + //SetThink(&AvHHive::DeathThink); + //this->pev->nextthink = gpGlobals->time + kDeathAnimationLength; + + this->FireDeathTarget(); +} + +void AvHHive::Precache(void) +{ + AvHBaseBuildable::Precache(); + + PRECACHE_UNMODIFIED_SOUND(kHiveSpawnSound); + PRECACHE_UNMODIFIED_SOUND(kHiveAmbientSound); + PRECACHE_UNMODIFIED_SOUND(kHiveDeathSound); + + PRECACHE_UNMODIFIED_MODEL(kHiveModel); + + CBreakable::PrecacheAll(); +} + +void AvHHive::ProcessHealing() +{ + // Regenerate nearby friendly aliens + CBaseEntity* theEntity = NULL; + const int theHiveHealRadius = BALANCE_VAR(kHiveHealRadius); + + while((theEntity = UTIL_FindEntityInSphere(theEntity, this->pev->origin, theHiveHealRadius)) != NULL) + { + AvHPlayer* thePlayer = dynamic_cast(theEntity); + if(thePlayer) + { + if(thePlayer->GetIsRelevant() && (thePlayer->GetTeam() == this->GetTeamNumber()) && !thePlayer->GetIsBeingDigested()) + { + // Hive heals percentage of player health + float theRegenPercentage = BALANCE_VAR(kHiveRegenerationPercentage); + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3, thePlayer->GetExperienceLevel()); + float theRegenAmount = (theRegenPercentage*theMaxHealth); + thePlayer->Heal(theRegenAmount, true); + } + } + } + + // Regenerate self + bool theDidHeal = false; + + // If we aren't at full health, heal health + if(this->pev->health < this->mMaxHitPoints) + { + float theHiveRegenAmount = BALANCE_VAR(kHiveRegenerationAmount); + float theCombatModeScalar = /*GetGameRules()->GetIsCombatMode() ? (1.0f/BALANCE_VAR(kCombatModeTimeScalar)) :*/ 1.0f; + + this->pev->health = min((float)this->mMaxHitPoints, this->pev->health + theHiveRegenAmount*theCombatModeScalar); + theDidHeal = true; + } + + // Play regen event + if(theDidHeal) + { + // Play regeneration event + PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + } +} + +void AvHHive::ResetEntity(void) +{ + AvHReinforceable::ResetEntity(); + AvHBaseBuildable::ResetEntity(); + + this->ResetCloaking(); + + this->SetInactive(); + + this->pev->health = this->mMaxHitPoints; + + this->pev->takedamage = DAMAGE_NO; + + // Reset parasites, etc. + this->pev->iuser4 = 0; + this->SetPersistent(); + + this->mTimeOfNextUmbra = -1; + + // Reset fuser1 progress + this->pev->fuser1 = 0; +} + +void AvHHive::ResetReinforcingPlayer(bool inSuccess) +{ + AvHReinforceable::ResetReinforcingPlayer(inSuccess); + + this->mEnergy = 0.0f; +} + +bool AvHHive::SetSolid(bool inForce) +{ + // Check to make sure there aren't any players in the destination area + CBaseEntity* pList[128]; + + // Crank up the area just to be safe + Vector theMinArea = this->pev->origin + kHiveMinSize; + Vector theMaxArea = this->pev->origin + kHiveMaxSize; + + // TODO: If players are blocking this area for too long, spawn hive and kill them + int theNumBlockingEntities = UTIL_EntitiesInBox(pList, 128, theMinArea, theMaxArea, FL_CLIENT); + if((theNumBlockingEntities == 0) || inForce) + { + this->pev->solid = SOLID_BBOX; + this->pev->movetype = MOVETYPE_NONE; + + UTIL_SetSize(this->pev, kHiveMinSize, kHiveMaxSize); + + // pev->frame = 0; + // pev->body = 3; + // pev->sequence = 0; + // // ResetSequenceInfo( ); + // pev->framerate = 0; + // + // UTIL_SetOrigin(pev, pev->origin); + // UTIL_SetSize(pev, Vector(-20, -20, 0), Vector(20, 20, 28) ); + + SetTouch(&AvHHive::HiveTouch); + + this->mSolid = true; + } + + return this->mSolid; +} + +void AvHHive::SetHasBeenBuilt() +{ + AvHBuildable::SetHasBeenBuilt(); + + GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_HIVE_COMPLETE, this->entindex()); + + // Make hive support any unassigned upgrade technologies (happens after a hive supporting a technology is destroyed and then rebuilt) + AvHTeamNumber theTeam = (AvHTeamNumber)this->pev->team; + AvHTeam* theTeamPointer = GetGameRules()->GetTeam(theTeam); + if(theTeamPointer) + { + AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); + + if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_DEFENSE) > 0) + { + AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_DEFENSE_CHAMBER); + } + + if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_MOVEMENT) > 0) + { + AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_MOVEMENT_CHAMBER); + } + + if(AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_SENSORY) > 0) + { + AvHSUUpdateHiveTechology(theTeam, ALIEN_BUILD_SENSORY_CHAMBER); + } + } +} + +bool AvHHive::StartSpawningForTeam(AvHTeamNumber inTeam, bool inForce) +{ + bool theSuccess = false; + + if(this->SetSolid(inForce)) + { + this->pev->team = inTeam; + this->pev->takedamage = DAMAGE_YES; + + this->pev->rendermode = kRenderNormal; + this->pev->renderamt = 0; + + SetBits(pev->flags, FL_MONSTER); + SetUpgradeMask(&this->pev->iuser4, MASK_BUILDABLE); + + this->mSpawning = true; + this->pev->health = kBaseHealthPercentage*this->mBaseHealth; + + // Looping, growing animation + this->pev->sequence = 0; + this->pev->frame = 0; + ResetSequenceInfo(); + + this->pev->nextthink = gpGlobals->time + kHiveAliveThinkInterval; + SetThink(&AvHHive::HiveAliveThink); + SetUse(&AvHHive::TeleportUse); + + theSuccess = true; + } + + return theSuccess; +} + + +void AvHHive::Spawn() +{ + this->Precache(); + + AvHBaseBuildable::Spawn(); + + this->pev->classname = MAKE_STRING(kesTeamHive); + //this->pev->movetype = MOVETYPE_FLY; + + this->pev->movetype = MOVETYPE_FLY; + this->pev->solid = SOLID_NOT; + this->pev->flags = 0; + this->pev->iuser3 = AVH_USER3_HIVE; + + this->mMaxHitPoints = GetGameRules()->GetBaseHealthForMessageID(ALIEN_BUILD_HIVE); + + SET_MODEL( ENT(this->pev), kHiveModel); + //this->pev->scale = 2; + +// this->pev->sequence = 0; +// this->pev->frame = 0; +// ResetSequenceInfo(); + + this->ResetEntity(); + +} + +void AvHHive::SetActive() +{ + AvHBaseBuildable::SetActive(); + + if(!this->mActive) + { + this->mActive = true; + this->mSpawning = false; + + this->mTimeLastContributed = gpGlobals->time; + + // Start animating + this->pev->sequence = 1; + this->pev->frame = 0; + ResetSequenceInfo(); + //AvHSUSetCollisionBoxFromSequence(this->pev); + + // Play spawn sound here + EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveSpawnSound, 1.0, ATTN_IDLE); + + // Note: this isn't being created for the first hive because this sound plays before the map is totally up + UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kHiveAmbientSound, 1.0f, 2.0, 0, 100); + + this->FireSpawnTarget(); + + //SetUse(&AvHHive::DonateUse); + } +} + +void AvHHive::SetInactive() +{ + AvHBaseBuildable::SetInactive(); + + // Set this so hives can be drawn translucently at hive locations for aliens + this->pev->effects &= ~EF_NODRAW; + + this->ResetReinforcingPlayer(false); + + this->mActive = false; + this->mSpawning = false; + this->mSolid = false; + this->mTimeLastContributed = -1; + this->mTimeEmergencyUseEnabled=-1.0f; + this->mTechnology = MESSAGE_NULL; + + this->pev->health = 0; + this->pev->takedamage = DAMAGE_NO; + this->pev->dmgtime = gpGlobals->time; + this->pev->solid = SOLID_NOT; + this->pev->team = TEAM_IND; + SetUpgradeMask(&this->pev->iuser4, MASK_BUILDABLE, false); + + this->pev->rendermode = kRenderTransAlpha; + this->pev->renderamt = 0; + + // Stop animation + this->pev->sequence = 0; + this->pev->frame = 0; + this->pev->framerate = 0; + + // No longer built at all + this->pev->fuser1 = 0.0f; + SetThink(NULL); + SetUse(NULL); + // Stop looping + UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kHiveAmbientSound, 1.0f, .5, SND_STOP, 100); + + ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? + + SetTouch(NULL); +} + +int AvHHive::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + const float kWoundSoundInterval = 1.0f; + + int theReturnCode = 0; + + if(this->mActive || this->mSpawning) + { + //CBaseEntity* theAttackingEntity = CBaseEntity::Instance(pevAttacker); + //if(GetGameRules()->CanEntityDoDamageTo(theAttackingEntity, this)) + //{ + theReturnCode = AvHBaseBuildable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + + if(theReturnCode > 0) + { + const float kDyingThreshold = .4f; + if(this->pev->health < kDyingThreshold*this->mMaxHitPoints) + { + GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_HIVE_DYING, this->entindex()); + } + else + { + if ( pevAttacker->team != this->pev->team ) + GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_UNDER_ATTACK, this->entindex()); + } + + if((this->mTimeLastWoundSound == -1) || ((this->mTimeLastWoundSound + kWoundSoundInterval) < gpGlobals->time)) + { + // Pick a random wound sound to play + //int theIndex = RANDOM_LONG(0, kNumWoundSounds - 1); + //char* theSoundToPlay = kWoundSoundList[theIndex]; + //EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kHiveDeathSound, 1.0, ATTN_IDLE); + + //EMIT_SOUND(ENT(this->pev), CHAN_AUTO, "misc/hive_wound1.wav", 1.0, ATTN_IDLE); + + // Emit hive damaged sound + gSoundListManager.PlaySoundInList(kHiveWoundSoundList, this, CHAN_BODY); + + this->mTimeLastWoundSound = gpGlobals->time; + } + } + //} + } + + return theReturnCode; +} + + + +bool AvHHive::GetCanReinforce() const +{ + return (this->GetIsBuilt() && this->IsAlive() && !GetGameRules()->GetIsCombatMode()); +} + +bool AvHHive::GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const +{ + bool theSuccess = false; + + CBaseEntity* theSpawnPoint = GetGameRules()->GetRandomHiveSpawnPoint(inPlayer, this->pev->origin, this->GetMaxSpawnDistance()); + if(theSpawnPoint) + { + outLocation = theSpawnPoint->pev->origin; + theSuccess = true; + } + + return theSuccess; +} + +bool AvHHive::GetTriggerAlertOnDamage() const +{ + return false; +} + +AvHTeamNumber AvHHive::GetReinforceTeamNumber() const +{ + return AvHBaseBuildable::GetTeamNumber(); +} + + +//void AvHHive::UpdateReinforcements() +//{ +// // If hive is active +// if(this->GetIsActive()) +// { +// // Test to make sure our reinforcing player is still valid +// AvHPlayer* theReinforcingPlayer = this->GetReinforcingPlayer(); +// if(theReinforcingPlayer) +// { +// AvHPlayMode thePlayMode = theReinforcingPlayer->GetPlayMode(); +// if((theReinforcingPlayer->pev->team != this->pev->team) || (thePlayMode == PLAYMODE_UNDEFINED) || (thePlayMode == PLAYMODE_READYROOM) || (thePlayMode == PLAYMODE_OBSERVER)) +// { +// this->mReinforcingPlayer = -1; +// } +// } +// +// // If hive isn't spawning a player in currently +// if(this->mReinforcingPlayer == -1) +// { +// // Find player on this team that's been waiting the longest +// AvHPlayer* thePlayerToSpawn = NULL; +// +// FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*); +// if(theEntity->GetTeam() == this->GetTeamNumber()) +// { +// if(theEntity->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) +// { +// if(!thePlayerToSpawn || (theEntity->GetTimeLastPlayModeSet() < thePlayerToSpawn->GetTimeLastPlayModeSet())) +// { +// thePlayerToSpawn = theEntity; +// break; +// } +// } +// } +// END_FOR_ALL_ENTITIES(kAvHPlayerClassName); +// +// // Spawn the one waiting the longest +// if(thePlayerToSpawn) +// { +// // Set the player to be reinforcing +// this->mReinforcingPlayer = thePlayerToSpawn->entindex(); +// +// thePlayerToSpawn->SetPlayMode(PLAYMODE_REINFORCING); +// +// // Play hive animation, play effect for player? +// this->pev->sequence = 4; +// this->pev->frame = 0; +// ResetSequenceInfo(); +// } +// } +// // else hive is spawning a player +// else +// { +// // Is player still valid, or has he left the server/team? +// AvHPlayer* thePlayer = this->GetReinforcingPlayer(); +// if(thePlayer && (thePlayer->GetTeam() == this->GetTeamNumber())) +// { +// if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING) +// { +// // Has enough time passed to bring the player in? +// const float kHiveRespawnTime = GetGameRules()->GetAlienRespawnTime(); +// +// if(gpGlobals->time > (thePlayer->GetTimeLastPlayModeSet() + kHiveRespawnTime)) +// { +// this->ResetReinforcingPlayer(true); +// +// // Take away points from the player if possible +// float theNewPointTotal = max(thePlayer->GetResources() - GetGameRules()->GetGameplay().GetAlienRespawnCost(), 0.0f); +// thePlayer->SetResources(theNewPointTotal); +// } +// } +// else +// { +// this->mReinforcingPlayer = -1; +// } +// } +// } +// } +//} + +void AvHHive::CreateUmbra(vec3_t& inOrigin) +{ + AvHSUCreateUmbraCloud(inOrigin, AvHTeamNumber(this->pev->team), this); + + // Don't create another for a bit + this->mTimeOfNextUmbra = -1; +} + +void AvHHive::CueRespawnEffect(AvHPlayer* inPlayer) +{ + // Play hive animation, play effect for player? + this->pev->sequence = 4; + this->pev->frame = 0; + ResetSequenceInfo(); + + // Create umbra around spawning players, but not until after late-join period (to avoid a ton of umbras all at once) + if(!GetGameRules()->GetArePlayersAllowedToJoinImmediately()) + { + //this->CreateUmbra(inPlayer->pev->origin); + } +} + +float AvHHive::GetReinforceTime() const +{ + const float kMaxRespawnTime = BALANCE_VAR(kAlienRespawnTime); + + float theRespawnTime = (kMaxRespawnTime - kMaxRespawnTime*this->mEnergy); + + // 0000854 + // Decrease respawn wait time for aliens (NS: Classic) + // With one hive, for every player above six on the alien team, + // reduce the per-player respawn wait time by two-thirds of a second. + // With two hives, make the reduction one-third of a second. + // With three (or more, in the case of weird custom maps) hives, do not apply it. + + AvHTeam* theTeam = GetGameRules()->GetTeam(GetTeamNumber()); + ASSERT(theTeam); + + int thePlayerModifier = theTeam->GetPlayerCount() - BALANCE_VAR(kAlienRespawnPlayerModifier); + int theHiveCount = GetGameRules()->GetNumActiveHives(GetTeamNumber()); + + if ( thePlayerModifier > 0 && theHiveCount < 3 ) + { + float theTimeModifier = BALANCE_VAR(kAlienRespawnTimeModifier); + + // For one hive double the modifier + if ( theHiveCount == 1 ) + { + theTimeModifier *= 2.0f; + } + + theRespawnTime -= theTimeModifier * (float)thePlayerModifier; + } + + theRespawnTime = min(max(theRespawnTime, 0.0f), kMaxRespawnTime); + + return theRespawnTime; +} + +bool AvHHive::Energize(float inEnergyAmount) +{ + bool theSuccess = false; + + // Only energize when a player is in the cue + if(this->GetIsBuilt() && this->GetReinforcingPlayer()) + { + if(this->mEnergy < 1.0f) + { + this->mEnergy += inEnergyAmount; + this->mEnergy = min(max(0.0f, this->mEnergy), 1.0f); + theSuccess = true; + } + } + + return theSuccess; +} + +void AvHHive::HiveTouch(CBaseEntity* inOther) +{ + AvHPlayer* thePlayer = dynamic_cast(inOther); + if(thePlayer && (thePlayer->pev->team != this->pev->team)) + { + if(this->GetIsActive()) + { + // Play scared animation, it recoils from human touch + this->PlayAnimationAtIndex(kScaredAnimationIndex, true); + } + } + + AvHBaseBuildable::BuildableTouch(inOther); +} + +void AvHHive::SetEmergencyUse() { + if ( !this->GetEmergencyUse() ) { + GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_HIVE_DEFEND, this->entindex()); + this->mTimeEmergencyUseEnabled=gpGlobals->time; + } +} + +bool AvHHive::GetEmergencyUse() const { + return ( this->mTimeEmergencyUseEnabled > gpGlobals->time - 5.0f ); //BALANCE_VAR(kHiveEmergencyInterval) +} + +void AvHHive::TeleportUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue) +{ + if ( this->GetIsSpawning() ) { + this->SetEmergencyUse(); + return; + } + + const float kHiveScanInterval = 1.0f; + + AvHPlayer* thePlayer = dynamic_cast(inActivator); + + + if(thePlayer && (thePlayer->pev->team == this->pev->team) && (thePlayer->GetUser3() != AVH_USER3_ALIEN_EMBRYO) && thePlayer->GetCanUseHive() ) + { + vector theHives; + vector theHivesUnderAttack; + if((this->mLastTimeScannedHives == -1) || (gpGlobals->time > (this->mLastTimeScannedHives + kHiveScanInterval))) + { + this->mTeleportHiveIndex = -1; + float theFarthestDistance = 0.0f; //sqrt((kMaxMapDimension*2)*(kMaxMapDimension*2)); + // Loop through the hives for this team, look for the farthest one (hives under attack take precedence) + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if((theEntity->pev->team == this->pev->team) && theEntity != this ) + { + bool theHiveIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntity->entindex()); + // allow teleport to any built hive, or unbuilt hives under attack. + if(!theEntity->GetIsSpawning() || ( theEntity->GetIsSpawning() && ( theHiveIsUnderAttack || theEntity->GetEmergencyUse()) ) ) + { + theHives.push_back(theEntity->entindex()); + if ( theHiveIsUnderAttack ) + theHivesUnderAttack.push_back(theEntity->entindex()); + } + } + END_FOR_ALL_ENTITIES(kesTeamHive) + + this->mLastTimeScannedHives = gpGlobals->time; + } + + vector *tmpPtr=&theHives; + if ( theHivesUnderAttack.size() > 0 ) + tmpPtr=&theHivesUnderAttack; + + if ( tmpPtr->size() > 0 ) { + int myIndex=this->entindex(); + for ( int i=0; i < tmpPtr->size(); i++ ) { + int hiveIndex=(*tmpPtr)[i]; + if ( hiveIndex > myIndex ) { + this->mTeleportHiveIndex=hiveIndex; + break; + } + } + if ( this->mTeleportHiveIndex == -1 ) { + this->mTeleportHiveIndex=(*tmpPtr)[0]; + } + } + + // If we have a valid hive index, jump the player to it + if(this->mTeleportHiveIndex != -1) + { + // Play sound at this entity + EMIT_SOUND(this->edict(), CHAN_AUTO, kAlienSightOnSound, 1.0f, ATTN_NORM); + + // Move him to it! + AvHHive* theHive = NULL; + AvHSUGetEntityFromIndex(this->mTeleportHiveIndex, theHive); + if(theHive) + { + CBaseEntity* theSpawnEntity = GetGameRules()->GetRandomHiveSpawnPoint(thePlayer, theHive->pev->origin, theHive->GetMaxSpawnDistance()); + if(theSpawnEntity) + { + Vector theMinSize; + Vector theMaxSize; + thePlayer->GetSize(theMinSize, theMaxSize); + + int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(thePlayer->pev->iuser3)); + Vector theOriginToSpawn = theSpawnEntity->pev->origin; + theOriginToSpawn.z += theOffset; + + if(AvHSUGetIsEnoughRoomForHull(theOriginToSpawn, AvHMUGetHull(false, thePlayer->pev->iuser3), thePlayer->edict())) + { + thePlayer->SetTimeOfLastHiveUse(gpGlobals->time); + thePlayer->SetPosition(theOriginToSpawn); + thePlayer->pev->velocity = Vector(0, 0, 0); + thePlayer->TriggerUncloak(); + // Play teleport sound before and after + EMIT_SOUND(inActivator->edict(), CHAN_AUTO, kAlienSightOffSound, 1.0f, ATTN_NORM); + } + } + } + } + } +} diff --git a/main/source/mod/AvHHive.h b/main/source/mod/AvHHive.h index 11e8222..7049959 100644 --- a/main/source/mod/AvHHive.h +++ b/main/source/mod/AvHHive.h @@ -152,10 +152,15 @@ public: virtual int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); - virtual void TriggerDeathAudioVisuals(); + virtual void TriggerDeathAudioVisuals(bool isRecycled=false); virtual void Spawn(); + virtual void SetEmergencyUse(); + virtual bool GetEmergencyUse() const; + + void EXPORT TeleportUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue); + //virtual void UpdateReinforcements(); protected: @@ -179,6 +184,10 @@ private: float mTimeLastWoundSound; float mTimeOfNextUmbra; float mEnergy; + float mLastTimeScannedHives; + float mTimeEmergencyUseEnabled; + int mTeleportHiveIndex; + }; #endif \ No newline at end of file diff --git a/main/source/mod/AvHHud.cpp b/main/source/mod/AvHHud.cpp index f771bdc..745cd7f 100644 --- a/main/source/mod/AvHHud.cpp +++ b/main/source/mod/AvHHud.cpp @@ -1,7049 +1,7201 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: Main NS NUD, also interface to client network messages -// -// $Workfile: AvHHud.cpp $ -// $Date: 2002/10/28 20:35:32 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHHud.cpp,v $ -// Revision 1.70 2002/10/28 20:35:32 Flayra -// - Fix for gamma reset with VAC -// - Info location fix after changelevel -// -// Revision 1.69 2002/10/25 21:49:10 Flayra -// - Updated skin to sit in pev->skin -// - Reset components every tick to fix problem with disappearing team resource label -// -// Revision 1.68 2002/10/24 21:29:49 Flayra -// - Moved help client-side -// - Fixed particle/changelevel crash -// - Reworked marine upgrade drawing -// - Added lots of utility functions for help system (mirrors functions on server) -// - Removed gamma message unless it failed or if maxplayers is 1 -// - Fixed alien hive sight crash -// - Show players under reticle while in ready room and spectating -// - Removed ugly/too-prevalent user3 icons -// -// Revision 1.67 2002/10/18 22:19:49 Flayra -// - Added alien easter egg sayings -// -// Revision 1.66 2002/10/16 20:53:41 Flayra -// - Draw scan model specially so it looks right -// -// Revision 1.65 2002/10/16 00:58:02 Flayra -// - Removed hotgroups -// - Added "need order" alert -// -// Revision 1.64 2002/10/03 20:24:39 Flayra -// - Changes for "more resources required" -// -// Revision 1.63 2002/10/03 18:54:30 Flayra -// - Allow right-click to cancel building placement -// - Fixed help icons -// - Added a couple utility functions -// - Reworked order notification -// - Reworked blip network messages to avoid hard-coded limit -// - Sent max resources down with current resources -// - Countdown sound no longer prevents other hud sounds -// - Alien trigger sounds -// - New order sounds -// - No longer disable nodes out of our cost range -// -// Revision 1.62 2002/09/25 20:47:19 Flayra -// - Don't draw elements on HUD when dead -// - UI refactoring -// - Split reticle help into help text and reticle text -// - Removed use order -// - Added separate select sound for alien -// - Multiple move sounds -// - Only draw entity build/health status when under reticle (no more scanning around you) -// - Added 3 new sayings -// -// Revision 1.61 2002/09/23 22:18:25 Flayra -// - Added alien build circles -// - Game status changes so particles aren't sent every time -// - Demo playback changes (save restore basic data that HUD already has) -// - New alert sounds -// - Skin support -// -// Revision 1.60 2002/09/09 19:55:24 Flayra -// - Added hive info indicator -// - Fixed bug where reticle tooltip help text wasn't being set until a weapon was selected -// - Fixed release mode bug where tooltips weren't expiring -// - Fixed bug where marine upgrades blinked -// - "No commander" indicator now blinks -// -// Revision 1.59 2002/08/31 18:01:01 Flayra -// - Work at VALVe -// -// Revision 1.58 2002/08/16 02:37:49 Flayra -// - HUD sounds no longer cut each other off (they won't play instead of cutting off another sound) -// - Tooltip sounds -// - Selection issues -// - Draw rings around buildings that need to be built -// - Removed old overwatch code -// -// Revision 1.57 2002/08/09 01:02:40 Flayra -// - Added hooks for demo playback, removed prediction selection -// -// Revision 1.56 2002/08/02 21:59:12 Flayra -// - Added reticle help, new tooltip system and much nicer order drawing! Refactored view model drawing a bit, hoping to make texture blending work for it. -// -// Revision 1.55 2002/07/26 23:05:01 Flayra -// - Generate numerical feedback for damage events -// - Refactoring for more info when looking at something (instead of bad-looking player names only) -// -// Revision 1.54 2002/07/24 18:45:41 Flayra -// - Linux and scripting changes -// -// Revision 1.53 2002/07/23 17:07:36 Flayra -// - Added visually-smooth energy level, added versatile location code, new hive sight info, refactored to remove extra sprites (128 HUD sprites bug), commander tech help fixes -// -// Revision 1.52 2002/07/10 14:41:55 Flayra -// - Fixed bug where non-sighted particle systems weren't being drawn for players on the ground (bug #127) -// -// Revision 1.51 2002/07/08 17:07:56 Flayra -// - Started to add display of marine upgrade sprite, fixed bug where building indicators aren't displayed after a map change, info_location drawing changes, primal scream color tweak, removed old hive drawing code -// -// Revision 1.50 2002/07/01 21:35:05 Flayra -// - Removed lots of outdated sprites and sprite code, added building ranges, fixed ghost building problem (bug #82) -// -// Revision 1.49 2002/06/25 18:03:09 Flayra -// - Added info_locations, removed old weapon help system, added smooth resource swelling, lots of alien UI usability changes, fixed problem with ghost building -// -// Revision 1.48 2002/06/10 19:55:36 Flayra -// - New commander UI (bindable via hotkeys, added REMOVE_SELECTION for when clicking menu options when no players selected) -// -// Revision 1.47 2002/06/03 16:48:45 Flayra -// - Help sprites moved into one animated sprite, select sound volume reduced (now that sound is normalized) -// -// Revision 1.46 2002/05/28 17:48:14 Flayra -// - Minimap refactoring, reinforcement refactoring, new hive sight fixes, recycling support -// -// Revision 1.45 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHConstants.h" -#include "mod/AvHHud.h" -#include "cl_dll/hud.h" -#include "cl_dll/cl_util.h" -#include "vgui_label.h" -#include "ui/PieMenu.h" -#include "mod/AvHTeamHierarchy.h" -#include "mod/AvHPieMenuHandler.h" -#include "mod/AvHParticleTemplateClient.h" -#include "mod/AvHParticleSystemManager.h" -#include "mod/AvHClientVariables.h" -#include "mod/AvHSpecials.h" -#include "ui/FadingImageLabel.h" -#include "mod/AvHScrollHandler.h" -#include "mod/AvHEvents.h" -#include "pm_shared/pm_shared.h" -#include "common/cl_entity.h" -#include "mod/AvHCommanderModeHandler.h" -#include "mod/AvHParticleEditorHandler.h" -#include "mod/AvHTechTree.h" -#include "mod/AvHMovementUtil.h" -#include "mod/AvHTitles.h" -#include "mod/AvHSelectionHelper.h" -#include "mod/AvHActionButtons.h" -#include "pm_shared/pm_debug.h" -#include "util/MathUtil.h" -#include "util/STLUtil.h" -#include "mod/AvHSharedUtil.h" -#include "common/r_efx.h" -#include "cl_dll/eventscripts.h" -#include -#include "mod/AvHSprites.h" -#include "ui/UIUtil.h" -#include "mod/AvHMiniMap.h" -#include "types.h" -#include -#include "common/event_api.h" -#include "mod/AvHHulls.h" -#include "common/com_model.h" -#include "mod/AvHBasePlayerWeaponConstants.h" -#include "cl_dll/vgui_ScorePanel.h" -#include "mod/AvHAlienAbilityConstants.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHScriptManager.h" -#include "mod/AvHHudConstants.h" -#include "cl_dll/demo.h" -#include "common/demo_api.h" -#include "cl_dll/ammohistory.h" -#include "mod/AvHTechImpulsePanel.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHCommandConstants.h" -#include "mod/AvHDebugUtil.h" -#include "engine/keydefs.h" -#include "ui/ChatPanel.h" -#include "cl_dll/r_studioint.h" -#include "util/Tokenizer.h" -#include -#include "mod/AvHNetworkMessages.h" - -// SCRIPTENGINE: -#include "scriptengine/AvHLUA.h" -#include "scriptengine/AvHLUAUtil.h" -// :SCRIPTENGINE - -//#include "cl_dll/studio_util.h" -//#include "cl_dll/r_studioint.h" - -void IN_GetMousePos( int *mx, int *my ); -extern playermove_t *pmove; -void RemoveAllDecals(); -void ScorePanel_InitializeDemoRecording(); - -// Include windows for GDI and gamma functions -#include "windows.h" - -extern engine_studio_api_t IEngineStudio; - -AvHPieMenuHandler gPieMenuHandler; -AvHScrollHandler gScrollHandler; -AvHCommanderModeHandler gCommanderHandler; -AvHParticleEditorHandler gParticleEditorHandler; -extern AvHParticleTemplateListClient gParticleTemplateList; -extern DebugPointListType gTriDebugLocations; -extern extra_player_info_t g_PlayerExtraInfo[MAX_PLAYERS+1]; -extern WeaponsResource gWR; - -extern double gClientTimeLastUpdate; -extern "C" Vector gPredictedPlayerOrigin; -extern "C" Vector gPredictedPlayerVOfs; -extern void __CmdFunc_Close(void); -extern int CL_ButtonBits(int); -extern int g_iVisibleMouse; - -GammaTable AvHHud::sPregameGammaTable; -GammaTable AvHHud::sGameGammaTable; - -bool AvHHud::sShowMap = false; - -// Global because of global HUD complilation error -AvHMiniMap gMiniMap; - -#include "VGUI_RepaintSignal.h" - -//extern vec3_t v_origin; -//extern vec3_t v_angles; -//vec3_t gPlayerOrigin; -//vec3_t gPlayerAngles; - -extern AvHSelectionHelper gSelectionHelper; - -//#if defined( AVH_CLIENT ) -//extern "C" float gOverwatchTargetRange; -extern float gOverwatchTargetRange; -//#endif - -extern bool gResetViewAngles; -extern vec3_t gViewAngles; -extern char sDebugString[128]; - -float kOverwatchFlashInterval = 2.5f; -const float kReticleInfoMaxAlpha = 50; -int gVisibleMouse = 0; - -//voogru: cvar pointers, these should always remain valid once they are set. - -cvar_t *gl_monolights = NULL; -cvar_t *gl_overbright = NULL; -cvar_t *gl_clear = NULL; -cvar_t *hud_draw = NULL; -cvar_t *r_drawviewmodel = NULL; -extern cvar_t *cl_movespeedkey; -cvar_t *gl_d3dflip = NULL; -cvar_t *s_show = NULL; -cvar_t *lightgamma = NULL; -cvar_t *r_detailtextures = NULL; - -const AvHMapExtents& GetMapExtents() -{ - return gHUD.GetMapExtents(); -} - -NumericalInfoEffect::NumericalInfoEffect(float inPosition[3], float inNumber, int inEventType, float inTimeCreated) -{ - this->mPosition[0] = inPosition[0]; - this->mPosition[1] = inPosition[1]; - this->mPosition[2] = inPosition[2]; - - this->mNumber = inNumber; - this->mEventType = inEventType; - - this->mTimeCreated = inTimeCreated; -} - -void NumericalInfoEffect::GetPosition(float* outPosition) const -{ - outPosition[0] = this->mPosition[0]; - outPosition[1] = this->mPosition[1]; - outPosition[2] = this->mPosition[2]; -} - -float NumericalInfoEffect::GetNumber() const -{ - return this->mNumber; -} - -int NumericalInfoEffect::GetEventType() const -{ - return this->mEventType; -} - -float NumericalInfoEffect::GetTimeCreated() const -{ - return this->mTimeCreated; -} - -void NumericalInfoEffect::SetPosition(float inPosition[3]) -{ - this->mPosition[0] = inPosition[0]; - this->mPosition[1] = inPosition[1]; - this->mPosition[2] = inPosition[2]; -} - -void AvHHud::OnActivateSteamUI() -{ - // Set the normal gamma so the Steam UI looks correct. - sPregameGammaTable.InitializeToVideoState(); - mSteamUIActive = true; -} - -void AvHHud::OnDeactivateSteamUI() -{ - - // Set the special NS gamma. - SetGamma(mDesiredGammaSlope); - mSteamUIActive = false; - - // The Steam UI screws up the mouse cursor so reset it. - if (gViewPort != NULL) - { - gViewPort->UpdateCursorState(); - } - -} - -void AvHHud::OnLostFocus() -{ - sPregameGammaTable.InitializeToVideoState(); -} - -bool AvHHud::OnKeyEvent(int virtualKey, int scanCode, bool pressed) -{ - - if (gViewPort != NULL && !mSteamUIActive) - { - - ChatPanel* theChatPanel = gViewPort->GetChatPanel(); - - if (theChatPanel && theChatPanel->isVisible()) - { - if (pressed) - { - theChatPanel->KeyDown(virtualKey, scanCode); - return true; - } - else - { - // If the key wasn't pressed while the chat window was open, - // the key up needs to go to HL. - return theChatPanel->WasKeyPushed(virtualKey); - } - } - - if (virtualKey == VK_ESCAPE && GetInTopDownMode() && mGhostBuilding != MESSAGE_NULL) - { - if (pressed) - { - CancelBuilding(); - } - return true; - } - - } - - return false; - -} - -int AvHHud::GetGameTime() const -{ - int theGameTime = 0; - - if(this->mGameTime > 0) - { - theGameTime = (int)(this->mGameTime); - } - - return theGameTime; -} - -int AvHHud::GetGameTimeLimit() const -{ - return this->mTimeLimit; -} - -int AvHHud::GetCombatAttackingTeamNumber() const -{ - return this->mCombatAttackingTeamNumber; -} - -bool AvHHud::GetShowingMap() -{ - return sShowMap; -} - -bool AvHHud::GetGameStarted() const -{ - return (this->mGameTime >= 0) && !this->mGameEnded; -} - -bool AvHHud::GetIsAlive(bool inIncludeSpectating) const -{ - bool theIsAlive = false; - - cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); - if(inIncludeSpectating) - { - thePlayer = this->GetVisiblePlayer(); - } - - if(thePlayer) - { - int thePlayerIndex = thePlayer->index; - if((thePlayerIndex) && (thePlayerIndex <= MAX_PLAYERS)) - { - int thePlayerClass = g_PlayerExtraInfo[thePlayerIndex].playerclass; - switch(thePlayerClass) - { - case PLAYERCLASS_ALIVE_MARINE: - case PLAYERCLASS_ALIVE_HEAVY_MARINE: - case PLAYERCLASS_ALIVE_JETPACK_MARINE: - case PLAYERCLASS_ALIVE_LEVEL1: - case PLAYERCLASS_ALIVE_LEVEL2: - case PLAYERCLASS_ALIVE_LEVEL3: - case PLAYERCLASS_ALIVE_LEVEL4: - case PLAYERCLASS_ALIVE_LEVEL5: - case PLAYERCLASS_ALIVE_DIGESTING: - case PLAYERCLASS_ALIVE_GESTATING: - case PLAYERCLASS_COMMANDER: - theIsAlive = true; - } - } - } - - return theIsAlive; -} - -bool GetIsWithinRegion(float inNormX, float inNormY, float inLowX, float inLowY, float inHighX, float inHighY) -{ - bool inRegion = false; - - if((inNormX >= inLowX) && (inNormY >= inLowY) && (inNormX <= inHighX) && (inNormY < inHighY)) - { - inRegion = true; - } - - return inRegion; -} - -bool AvHHud::GetIsRegionBlockedByUI(float inNormX, float inNormY) -{ - bool theIsBlocked = true; - - if( GetIsWithinRegion(inNormX, inNormY, 0, .061, .3017, .6797) || - GetIsWithinRegion(inNormX, inNormY, .248, .0791, .7753, .6823) || - GetIsWithinRegion(inNormX, inNormY, .748, .092, 1, .6575) || - GetIsWithinRegion(inNormX, inNormY, .751, .645, .870, .678) || - GetIsWithinRegion(inNormX, inNormY, .337, .679, .729, .754) || - GetIsWithinRegion(inNormX, inNormY, .337, .717, .703, .823) ) - { - theIsBlocked = false; - - // Now check pending requests (the only HUD element not drawn as part of the outlying frame - for(PendingRequestListType::const_iterator theIterator = this->mPendingRequests.begin(); theIterator != this->mPendingRequests.end(); theIterator++) - { - AvHMessageID theMessageID = theIterator->first; - char theComponentName[256]; - sprintf(theComponentName, kPendingImpulseSpecifier, (int)theMessageID); - - AvHTechImpulsePanel* theTechImpulsePanel = NULL; - if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) - { - int thePosX, thePosY; - theTechImpulsePanel->getPos(thePosX, thePosY); - - int theWidth, theHeight; - theTechImpulsePanel->getSize(theWidth, theHeight); - - int theHighX = thePosX + theWidth; - int theHighY = thePosY + theHeight; - - float theScreenWidth = ScreenWidth(); - float theScreenHeight = ScreenHeight(); - - if(GetIsWithinRegion(inNormX, inNormY, thePosX/theScreenWidth, thePosY/theScreenHeight, theHighX/theScreenWidth, theHighY/theScreenHeight)) - { - theIsBlocked = true; - break; - } - } - } - } - - return theIsBlocked; -} - -bool AvHHud::GetIsShowingMap() const -{ - return sShowMap; -} - -void AvHHud::ClearSelection() -{ - gSelectionHelper.ClearSelection(); - this->mGroupEvent = COMMANDER_REMOVESELECTION; -} - - -void CLinkGhostBuildingCallback( struct tempent_s *ent, float frametime, float currenttime) -{ - gHUD.GhostBuildingCallback(ent, frametime, currenttime); -} - -// For easily adding message functions -#define BIND_MESSAGE(x) \ - int __MsgFunc_##x(const char *pszName, int iSize, void *pbuf) \ - { \ - return gHUD.##x(pszName, iSize, pbuf ); \ - } - -AvHHud::AvHHud(const string& inFilename, UIFactory* inFactory) : UIHud(inFilename, inFactory) -{ - this->ClearData(); - mSteamUIActive = false; -} - -void AvHHud::AddNumericalInfoMessage(float inOrigin[3], float inNumber, int inEventType) -{ - NumericalInfoEffect theEffect(inOrigin, inNumber, inEventType, this->mTimeOfLastUpdate); - this->mNumericalInfoEffects.push_back(theEffect); -} - -void AvHHud::AddTooltip(const char* inMessageText, bool inIsToolTip, float inTooltipWidth) -{ - if(!gEngfuncs.pDemoAPI->IsPlayingback() && (strlen(inMessageText) > 0)) - { - if(gEngfuncs.pfnGetCvarFloat(kvAutoHelp) || !inIsToolTip) - { - AvHTooltip theNewTooltip; - - theNewTooltip.SetText(string(inMessageText)); - theNewTooltip.SetNormalizedScreenX(1.0f - inTooltipWidth - kHelpMessageLeftEdgeInset); - theNewTooltip.SetNormalizedScreenY(0.01f); - theNewTooltip.SetCentered(false); - theNewTooltip.SetIgnoreFadeForLifetime(true); - theNewTooltip.SetNormalizedMaxWidth(inTooltipWidth); - - if(inIsToolTip) - { - this->PlayHUDSound(HUD_SOUND_TOOLTIP); - } - - this->mTooltips.push_back(theNewTooltip); - } - } -} - -bool AvHHud::AddTooltipOnce(const char* inMessageText, bool inIsToolTip) -{ - bool theAddedTooltip = false; - - string theMessage(inMessageText); - - // Check if message is in sent list - StringList::iterator theIter = std::find(this->mDisplayedToolTipList.begin(), this->mDisplayedToolTipList.end(), theMessage); - if(theIter == this->mDisplayedToolTipList.end()) - { - // If not - // Call AddTooltip - this->AddTooltip(inMessageText, inIsToolTip); - theAddedTooltip = true; - - // Add message to list - this->mDisplayedToolTipList.push_back(theMessage); - } - - return theAddedTooltip; -} - - -void AvHHud::Cancel(void) -{ - ASSERT(this->mInTopDownMode); - gCommanderHandler.CancelHit(); -} - -void AvHHud::ClearData() -{ - this->mResources = 0; - - this->mHierarchy = NULL; - this->mShowMapHierarchy = NULL; - - this->mCommanderResourceLabel = NULL; - this->mGenericProgressBar = NULL; - this->mResearchProgressBar = NULL; - this->mAlienProgressBar = NULL; - this->mResearchLabel = NULL; - //this->mArmorLevel = ARMOR_BASE; - this->mTimeOfLastUpdate = 0.0; - this->mTimeOfNextHudSound = -1; - this->mLastHUDSoundPlayed = HUD_SOUND_INVALID; - this->mTimeOfLastEntityUpdate = -1; - this->mInTopDownMode = false; - this->mLeftMouseStarted = false; - this->mLeftMouseEnded = false; - this->mPlacingBuilding = false; - this->mRightMouseStarted = false; - this->mRightMouseEnded = false; - //this->mOrderMode = ORDERTYPE_UNDEFINED; - this->mTechEvent = MESSAGE_NULL; - this->mAlienAbility = MESSAGE_NULL; - this->mGroupEvent = MESSAGE_NULL; - this->mTrackingEntity = 0; - this->mNumLocalSelectEvents = 0; - - this->mSelected.clear(); - this->mSelectionJustChanged = false; - this->mMouseOneDown = false; - this->mMouseTwoDown = false; - this->mMouseOneStartX = 0; - this->mMouseOneStartY = 0; - this->mMouseTwoStartX = 0; - this->mMouseTwoStartY = 0; - - this->mMouseCursorX = this->mMouseCursorY = 0; - this->mPieMenuControl = ""; - - this->mPreviousHelpText = ""; - this->mTimeLastHelpTextChanged = -1; - this->mCurrentCursorFrame = 0; - - this->mMapExtents.ResetMapExtents(); - this->mMapName = ""; - - this->mGhostBuilding = MESSAGE_NULL; - this->mValidatedBuilding = MESSAGE_NULL; - this->mCreatedGhost = false; - this->mCurrentGhostIsValid = false; - - this->mAmbientSounds.clear(); - - // tankefugl: 0000971 - this->mTeammateOrder.clear(); - this->mDisplayOrderIndex = 0; - this->mDisplayOrderTime = 0; - this->mDisplayOrderType = 0; - // :tankefugl -} - - -AvHHud::~AvHHud(void) -{ - //this->ResetGamma(); - //delete [] sOriginalGammaTable; - //delete [] sGammaTable; - AvHHud::ResetGammaAtExit(); -} - -void DummyFunction() -{ -} - -#ifdef DEBUG -int gGlobalDebugAuth = 0; -void TestIcon() -{ - gGlobalDebugAuth = rand() % 7; -} - -typedef struct alias_t { - alias_t* next; - char name[32]; - char* cmds; -} alias_s; - -void TestAlias() -{ - alias_s* alias = *(alias_s**)0x2D5929C; - while(alias) - { - gEngfuncs.Con_Printf("name: %s\n%x - %x\n", alias->name, alias->name, gEngfuncs); - alias = alias->next; - } -} -#endif - -// Used for console command -void AvHHud::PlayRandomSongHook() -{ - gHUD.PlayRandomSong(); -} - -void AvHHud::AddCommands() -{ - gEngfuncs.pfnAddCommand ("+popupmenu", AvHPieMenuHandler::OpenPieMenu); - gEngfuncs.pfnAddCommand ("-popupmenu", AvHPieMenuHandler::ClosePieMenu); - - gEngfuncs.pfnAddCommand ("+mousepopupmenu", AvHPieMenuHandler::OpenPieMenu); - gEngfuncs.pfnAddCommand ("-mousepopupmenu", AvHPieMenuHandler::ClosePieMenu); - - // Add scrolling commands - gEngfuncs.pfnAddCommand ("+scrollup", AvHScrollHandler::ScrollUp); - gEngfuncs.pfnAddCommand ("-scrollup", AvHScrollHandler::StopScroll); - - gEngfuncs.pfnAddCommand ("+scrolldown", AvHScrollHandler::ScrollDown); - gEngfuncs.pfnAddCommand ("-scrolldown", AvHScrollHandler::StopScroll); - - gEngfuncs.pfnAddCommand ("+scrollleft", AvHScrollHandler::ScrollLeft); - gEngfuncs.pfnAddCommand ("-scrollleft", AvHScrollHandler::StopScroll); - - gEngfuncs.pfnAddCommand ("+scrollright", AvHScrollHandler::ScrollRight); - gEngfuncs.pfnAddCommand ("-scrollright", AvHScrollHandler::StopScroll); - - gEngfuncs.pfnAddCommand ("toggleeditps", AvHParticleEditorHandler::ToggleEdit); - - gEngfuncs.pfnAddCommand ("nexttrack", AvHHud::PlayRandomSongHook); - - gEngfuncs.pfnAddCommand ("+showmap", AvHHud::ShowMap); - gEngfuncs.pfnAddCommand ("-showmap", AvHHud::HideMap); - - gEngfuncs.pfnAddCommand ("playstream", AvHHud::PlayStream); - gEngfuncs.pfnAddCommand ("stopstream", AvHHud::StopStream); - - #ifdef DEBUG - gEngfuncs.pfnAddCommand("testicon", TestIcon); - gEngfuncs.pfnAddCommand("testalias", TestAlias); - #endif - - int i = 0; - char theBinding[128]; - for(i = (int)(RESOURCE_UPGRADE); i <= (int)(BUILD_RECYCLE); i++) - { - sprintf(theBinding, "%s%d", kHotKeyPrefix, i); - gEngfuncs.pfnAddCommand(theBinding, DummyFunction); - } - - for(i = (int)(MENU_BUILD); i <= (int)(MENU_EQUIP); i++) - { - sprintf(theBinding, "%s%d", kHotKeyPrefix, i); - gEngfuncs.pfnAddCommand(theBinding, DummyFunction); - } -} - -void AvHHud::ClientProcessEntity(struct entity_state_s* inEntity) -{ - // Check if we need to create or destroy particle systems - int theIndex = inEntity->number; - bool theParticleOn = inEntity->iuser3 == AVH_USER3_PARTICLE_ON; - bool theParticleOff = inEntity->iuser3 == AVH_USER3_PARTICLE_OFF; - if(theParticleOn || theParticleOff) - { - int theHandle = -1; - if(theParticleOn) - { - // Ent index and template index stored in fuser1 - int theValue = (int)(inEntity->fuser1); - int theGenEntIndex = (0xFFFF0000 & theValue) >> 16; - //int theTemplateIndex = (0x0000FFFF & theValue); - int theTemplateIndex = (((int(inEntity->fuser1)) & 0x0000FF00) >> 8); - - //int theTemplateIndex = theValue; - - // Handle stored in fuser2 - theHandle = (int)(inEntity->fuser2); - - // Don't create particle systems marked as high-detail if we don't have that option set. Note, this could cause collision - // differences between the client and server if the particle system doesn't use this flag with care - const AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(theTemplateIndex); - if(theTemplate) - { - if(!theTemplate->GetHighDetailOnly() || gEngfuncs.pfnGetCvarFloat(kvHighDetail)) - { - AvHParticleSystemManager::Instance()->CreateParticleSystemIfNotCreated(inEntity->number, theTemplateIndex, /*theEntIndex,*/ theHandle); - - // Update postion and visibility - if(theGenEntIndex > 0) - { - cl_entity_s* theGenEntity = gEngfuncs.GetEntityByIndex(theGenEntIndex); - if(theGenEntity) - { - AvHParticleSystemManager::Instance()->SetParticleSystemGenerationEntityExtents(theGenEntity->curstate.mins, theGenEntity->curstate.maxs, theHandle); - } - } - else - { - AvHParticleSystemManager::Instance()->SetParticleSystemPosition(inEntity->origin, theHandle); - } - - // Set the particle system custom data - //uint16 theCustomData = (uint16)(((int)inEntity->fuser3) >> 16); - //uint16 theCustomData = (uint16)(inEntity->fuser3); - uint16 theCustomData = (uint16)(inEntity->weaponmodel); - AvHParticleSystemManager::Instance()->SetParticleSystemCustomData(theCustomData, theHandle); - } - } - } - else if(theParticleOff) - { - theHandle = (int)(inEntity->fuser1); - //AvHParticleSystemManager::Instance()->DestroyParticleSystemIfNotDestroyed(inEntity->number, theHandle); - AvHParticleSystemManager::Instance()->MarkParticleSystemForDeletion(inEntity->number, theHandle); - } - - // Always update visibility - bool theVisibilityState = false; - if(this->GetInTopDownMode()) - { - if(GetHasUpgrade(inEntity->iuser4, MASK_VIS_SIGHTED)) - { - theVisibilityState = true; - } - else - { - theVisibilityState = false; - } - } - else - { - theVisibilityState = true; - } - - AvHParticleSystemManager::Instance()->SetParticleSystemVisibility(theVisibilityState, theHandle); - } - else if((inEntity->iuser3 == AVH_USER3_AUDIO_ON) || (inEntity->iuser3 == AVH_USER3_AUDIO_OFF)) - { - // Read values - int theEntIndex = (int)(inEntity->fuser1) >> 16; - //int theSoundIndex = (int)(inEntity->fuser1) & 0x0000FFFF; - - // memcpy so value isn't interpreted - //int theSoundIndex = 0; - //memcpy(&theSoundIndex, &inEntity->fuser1, sizeof(float)); - //theSoundIndex = theSoundIndex & 0x0000FFFF; - int theSoundIndex = (((int(inEntity->fuser1)) & 0x0000FF00) >> 8); - - // Top byte is flags, next byte is volume, bottom two bytes are fade distance - int theFlags = inEntity->iuser4 >> 24; - int theVolume = (inEntity->iuser4 >> 16) & 0x00FF; - int theFadeDistance = (inEntity->iuser4) & 0x0000FFFF; - - float theTimeOfAction = inEntity->fuser2; - - bool theSoundOn = (inEntity->iuser3 == AVH_USER3_AUDIO_ON); - - this->ModifyAmbientSoundEntryIfChanged(theSoundOn, theSoundIndex, theEntIndex, theTimeOfAction, theVolume, theFadeDistance, theFlags, inEntity->origin); - } -} - -string LookupAndTranslate(AvHMessageID inMessageID) -{ - string theKey = string(kTechNodeLabelPrefix) + MakeStringFromInt((int)inMessageID); - - string theTranslatedTechName; - LocalizeString(theKey.c_str(), theTranslatedTechName); - - return theTranslatedTechName; -} - -void AvHHud::DisplayCombatUpgradeMenu(bool inVisible) -{ - if(inVisible) - { - // Parse current tech nodes and set text - const AvHTechID kLineStart[kNumUpgradeLines] = {TECH_ONE_LEVEL_ONE, TECH_TWO_LEVEL_ONE, TECH_THREE_LEVEL_ONE, TECH_FOUR_LEVEL_ONE, TECH_FIVE_LEVEL_ONE}; - - // Add "you are now x"! - string theYouAreNow; - LocalizeString(kYouAreNowA, theYouAreNow); - - string theRankTitle = this->GetRankTitle(false); - - string theExclamation; - LocalizeString(kExclamation, theExclamation); - - string theChooseAnUpgrade; - LocalizeString(kChooseAnUpgrade, theChooseAnUpgrade); - - string theFinalText = theYouAreNow + string(" ") + theRankTitle + theExclamation + string("\n"); - theFinalText += theChooseAnUpgrade + string("\n\n"); - - // Set the lines - this->mCombatUpgradeMenu.SetText(theFinalText); - } - - // Parse text above every time, but only set position once so it doesn't keep animating - if(inVisible && !this->mDrawCombatUpgradeMenu) - { - // Start off screen, and scroll right - const float kWidth = .4f; - this->mCombatUpgradeMenu.SetNormalizedScreenX(-kWidth); - this->mCombatUpgradeMenu.SetNormalizedScreenY(.25f); - this->mCombatUpgradeMenu.SetNormalizedMaxWidth(kWidth); - } - - this->mDrawCombatUpgradeMenu = inVisible; -} - -void AvHHud::DisplayMessage(const char* inMessage) -{ - this->m_Message.MessageAdd(inMessage, this->m_flTime); - - // Remember the time -- to fix up level transitions - //this->m_Message.m_parms.time = this->m_flTime; - - // Turn on drawing - if ( !(this->m_Message.m_iFlags & HUD_ACTIVE) ) - this->m_Message.m_iFlags |= HUD_ACTIVE; -} - -//int AvHHud::GetArmorLevel(void) const -//{ -// return this->mArmorLevel; -//} - -int AvHHud::GetFrameForOrderType(AvHOrderType inOrderType) const -{ - int theFrame = 0; - - switch(inOrderType) - { - case ORDERTYPEL_DEFAULT: - theFrame = 2; - break; - - case ORDERTYPEL_MOVE: - theFrame = 2; - break; - - case ORDERTYPET_ATTACK: - theFrame = 4; - break; - - case ORDERTYPET_BUILD: - theFrame = 5; - break; - - case ORDERTYPET_GUARD: - theFrame = 6; - break; - - case ORDERTYPET_WELD: - theFrame = 7; - break; - - case ORDERTYPET_GET: - theFrame = 8; - break; - } - - return theFrame; -} - -AvHPlayMode AvHHud::GetPlayMode(void) const -{ - AvHPlayMode thePlayMode = PLAYMODE_UNDEFINED; - - cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); - if(thePlayer) - { - if(gEngfuncs.IsSpectateOnly()) - { - thePlayMode = PLAYMODE_OBSERVER; - } - else - { - thePlayMode = AvHPlayMode(thePlayer->curstate.playerclass); - } - } - - return thePlayMode; -} - -AvHPlayMode AvHHud::GetHUDPlayMode() const -{ - AvHPlayMode thePlayMode = this->GetPlayMode(); - - cl_entity_s* thePlayer = this->GetVisiblePlayer(); - if(thePlayer) - { - thePlayMode = AvHPlayMode(thePlayer->curstate.playerclass); - } - - return thePlayMode; -} - -cl_entity_s* AvHHud::GetVisiblePlayer() const -{ - cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); - if(g_iUser1 == OBS_IN_EYE) - { - cl_entity_t* theEnt = gEngfuncs.GetEntityByIndex(g_iUser2); - if(theEnt) - { - thePlayer = theEnt; - } - } - - return thePlayer; -} - -int AvHHud::GetLocalUpgrades() const -{ - static int theUpgrades = 0; - - cl_entity_s* thePlayer = this->GetVisiblePlayer(); - if(thePlayer) - { - theUpgrades = thePlayer->curstate.iuser4; - } - - return theUpgrades; -} - -// Players could hack their client dll and see all the orders on their team. Minor cheat but definitely possible. -EntityListType AvHHud::GetDrawPlayerOrders() const -{ - EntityListType theList; - - cl_entity_s* theVisiblePlayer = this->GetVisiblePlayer(); - if(theVisiblePlayer) - { - int theVisiblePlayerIndex = theVisiblePlayer->index; - - if(this->GetHUDUser3() == AVH_USER3_MARINE_PLAYER) - { - // Only draw orders for us - theList.push_back(theVisiblePlayerIndex); - } - else if(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) - { - // Add everyone that he has selected! - return this->mSelected; - } - } - - return theList; -} - -bool AvHHud::GetInTopDownMode() const -{ - return this->mInTopDownMode; -} - -bool AvHHud::GetIsSelecting() const -{ - return mSelectionBoxVisible; -} - -OrderListType AvHHud::GetOrderList() const -{ - return this->mOrders; -} - -//AvHOrderType AvHHud::GetOrderMode() const -//{ -// return this->mOrderMode; -//} - -bool AvHHud::GetCenterPositionForGroup(int inGroupNumber, vec3_t& outCenterPosition) const -{ - bool theSuccess = false; - - if((inGroupNumber >= 1) && (inGroupNumber <= kNumHotkeyGroups)) - { - vec3_t theCenterPosition; - VectorClear(theCenterPosition); - - int theNumFound = 0; - - const EntityListType& theGroup = this->mGroups[inGroupNumber - 1]; - if(theGroup.size() > 0) - { - for(EntityListType::const_iterator theIter = theGroup.begin(); theIter != theGroup.end(); theIter++) - { - int theEntIndex = *theIter; - - Vector thePosition; - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(theEntIndex); - if(theEntity) - { - thePosition = theEntity->curstate.origin; - } - - if(AvHSHUGetEntityLocation(theEntIndex, thePosition)) - { - theCenterPosition.x += thePosition.x; - theCenterPosition.y += thePosition.y; - theNumFound++; - } - } - - if(theNumFound > 0) - { - theCenterPosition.x /= theNumFound; - theCenterPosition.y /= theNumFound; - - outCenterPosition = theCenterPosition; - - theSuccess = true; - } - } - } - - return theSuccess; -} - -void AvHHud::GetMousePos(int& outX, int& outY) const -{ - gEngfuncs.GetMousePosition(&outX, &outY); - - // Clip mouse to window (weird) - outX = min(max(0, outX), ScreenWidth()); - outY = min(max(0, outY), ScreenHeight()); - - //char theMouseMessage[256]; - //sprintf(theMouseMessage, "Mouse coords: %d, %d", outX, outY); - //CenterPrint(theMouseMessage); -} - -bool AvHHud::GetAndClearTopDownScrollAmount(int& outX, int& outY, int& outZ) -{ - bool theSuccess = false; - outX = 0; - outY = 0; - outZ = 0; - - // Don't scroll if the the commander is dragging a selection box. - - if(this->GetInTopDownMode() && !GetIsSelecting()) - { - const int kScreenWidth = ScreenWidth(); - const int kScreenHeight = ScreenHeight(); - const kScrollHorizontal = .0152f*kScreenWidth; - const kScrollVertical = .015f*kScreenHeight; - - // Left side - if(this->GetIsMouseInRegion(0, 0, kScrollHorizontal, kScreenHeight) || (gScrollHandler.GetXScroll() < 0)) - { - outX = -1; - } - // Right side - else if(this->GetIsMouseInRegion(kScreenWidth - kScrollHorizontal, 0, kScrollHorizontal, kScreenHeight) || (gScrollHandler.GetXScroll() > 0)) - { - outX = 1; - } - - // Top edge - if(this->GetIsMouseInRegion(0, 0, kScreenWidth, kScrollVertical) || (gScrollHandler.GetYScroll() > 0)) - { - outY = 1; - } - // Bottom edge - else if(this->GetIsMouseInRegion(0, kScreenHeight - kScrollVertical, kScreenWidth, kScrollVertical) || (gScrollHandler.GetYScroll() < 0)) - { - outY = -1; - } - - // Only clear z scroll because of the way events work (invnext/invprev vs. holding a key down) - //gScrollHandler.ClearScrollHeight(); - - theSuccess = true; - } - - return theSuccess; -} - -bool AvHHud::GetAndClearSelectionEvent(vec3_t& outSelection, AvHMessageID& outMessageID) -{ - bool theSuccess = false; - - // Return a build event if there is one, else return a COMMANDER_MOUSECOORD event - if(this->mLeftMouseStarted) - { - if(this->mValidatedBuilding == MESSAGE_NULL) - { - VectorCopy(this->mLeftMouseWorldStart, outSelection); - outMessageID = COMMANDER_MOUSECOORD; - } - else - { - VectorCopy(this->mNormBuildLocation, outSelection); - outMessageID = this->mValidatedBuilding; - this->mValidatedBuilding = this->mGhostBuilding = MESSAGE_NULL; - this->mPlacingBuilding = true; - } - theSuccess = true; - } - else if(this->mLeftMouseEnded) - { - if(!this->mPlacingBuilding) - { - outSelection = this->mLeftMouseWorldEnd; - outMessageID = COMMANDER_MOUSECOORD; - theSuccess = true; - } - this->mPlacingBuilding = false; - } - else if(this->mRightMouseStarted) - { - // Cancel building placement - if(this->mGhostBuilding != MESSAGE_NULL) - { - CancelBuilding(); - } - else - { - outSelection = this->mRightMouseWorldStart; - outMessageID = COMMANDER_MOUSECOORD; - theSuccess = true; - } - } - else if(this->mRightMouseEnded) - { - outSelection = this->mRightMouseWorldEnd; - outMessageID = COMMANDER_MOUSECOORD; - theSuccess = true; - } - else - { - outSelection = this->mMouseWorldPosition; - outMessageID = COMMANDER_MOUSECOORD; - theSuccess = true; - } - - return theSuccess; -} - -EntityListType AvHHud::GetSelected() const -{ - return this->mSelected; -} - -const AvHTechSlotManager& AvHHud::GetTechSlotManager() const -{ - return this->mTechSlotManager; -} - -bool AvHHud::GetAndClearAlienAbility(AvHMessageID& outMessageID) -{ - bool theAlienAbilityWaiting = false; - - if(this->mAlienAbility != MESSAGE_NULL) - { - outMessageID = this->mAlienAbility; - theAlienAbilityWaiting = true; - this->mAlienAbility = MESSAGE_NULL; - } - - return theAlienAbilityWaiting; -} - -bool AvHHud::GetAndClearGroupEvent(AvHMessageID& outMessageID) -{ - bool theGroupEventWaiting = false; - - if(this->mGroupEvent != MESSAGE_NULL) - { - outMessageID = this->mGroupEvent; - theGroupEventWaiting = true; - -// if(!this->mIsTracking) -// { - this->mGroupEvent = MESSAGE_NULL; -// } - } - - return theGroupEventWaiting; -} - -int AvHHud::GetTrackingEntity() const -{ - return this->mTrackingEntity; -} - -void AvHHud::ClearTrackingEntity() -{ - this->mTrackingEntity = 0; -} - -void AvHHud::SetSelectionEffects(EntityListType& inUnitList) -{ - // Make sure we have an effect created for each unit in this list. If there are units that - // have selection effects that aren't in this list, delete them. This is called locally when the - // selection is predicted, then it's called again when the selection is confirmed. - this->mSelectionEffects.clear(); - - for(EntityListType::iterator theIter = inUnitList.begin(); theIter != inUnitList.end(); theIter++) - { - SelectionEffect theNewEffect; - theNewEffect.mEntIndex = *theIter; - theNewEffect.mAngleOffset = 0; - this->mSelectionEffects.push_back(theNewEffect); - } -} - -UIMode AvHHud::GetUIMode() const -{ - return this->mCurrentUIMode; -} - -bool AvHHud::SwitchUIMode(UIMode inNewMode) -{ - bool theSuccess = false; - - // Only allow switching to a non-main mode when we're in main, always allow switching back to main mode - if((inNewMode == MAIN_MODE) || (this->mCurrentUIMode == MAIN_MODE)) - { - if(inNewMode != this->mCurrentUIMode) - { - // Move pop-up menu components away or back so they don't block other compoments...ugh - if(inNewMode != MAIN_MODE) - { - gHUD.GetManager().TranslateComponent(kSoldierMenu, true); - gHUD.GetManager().TranslateComponent(kSoldierCombatMenu, true); - gHUD.GetManager().TranslateComponent(kAlienMenu, true); - gHUD.GetManager().TranslateComponent(kAlienCombatMenu, true); - gHUD.GetManager().TranslateComponent(kAlienMembrane, true); - gHUD.GetManager().TranslateComponent(kScroller, true); - gHUD.GetManager().TranslateComponent(kSelectionText, true); - //CenterPrint("Pop-up controls moved off screen"); - } - else - { - gHUD.GetManager().TranslateComponent(kSoldierMenu, false); - gHUD.GetManager().TranslateComponent(kSoldierCombatMenu, false); - gHUD.GetManager().TranslateComponent(kAlienMenu, false); - gHUD.GetManager().TranslateComponent(kAlienCombatMenu, false); - gHUD.GetManager().TranslateComponent(kAlienMembrane, false); - gHUD.GetManager().TranslateComponent(kScroller, false); - gHUD.GetManager().TranslateComponent(kSelectionText, false); - //CenterPrint("Pop-up controls moved on screen"); - } - - this->mCurrentUIMode = inNewMode; - } - - theSuccess = true; - } - - return theSuccess; -} - -bool AvHHud::Update(float inCurrentTime, string& outErrorString) -{ - bool theSuccess = false; - - if(inCurrentTime > this->mTimeOfLastUpdate) - { - - this->mTimeOfCurrentUpdate = inCurrentTime; - - // Predict game time - if(this->GetGameStarted()) - { - this->mGameTime += (inCurrentTime - this->mTimeOfLastUpdate); - } - - AvHParticleSystemManager::Instance()->Start(); - - // This component must always be visible to allow us to hook mouse cursor sprite drawing - this->ResetComponentsForUser3(); - - this->UpdateDataFromVuser4(inCurrentTime); - - this->UpdateSpectating(); - - this->GetManager().UnhideComponent(kLastComponent); - - this->UpdateDemoRecordPlayback(); - - theSuccess = UIHud::Update(inCurrentTime, outErrorString); - if(!theSuccess) - { - this->AddTooltip(outErrorString.c_str()); - } - - this->UpdateProgressBar(); - - this->UpdateCommonUI(); - - this->UpdateAlienUI(inCurrentTime); - - this->UpdateMarineUI(inCurrentTime); - - this->UpdateCountdown(inCurrentTime); - - this->UpdateExploitPrevention(); - this->UpdateFromEntities(inCurrentTime); - - this->UpdateEntityID(inCurrentTime); - - this->UpdateHelpText(); - - this->UpdateTooltips(inCurrentTime); - - this->UpdateStructureNotification(inCurrentTime); - - this->UpdateMusic(inCurrentTime); - - this->UpdatePieMenuControl(); - - // Reset cursor every tick, update selection may change it - // This cursor is used when we're on the ground for pie menus as well - this->SetCursor(ORDERTYPE_UNDEFINED); - - this->UpdateHierarchy(); - - this->UpdateInfoLocation(); - - if(this->GetInTopDownMode()) - { - this->UpdateSelection(); - gCommanderHandler.Update(this->mTechNodes, this->mResources); - -// char theDebugString[128]; -// sprintf(theDebugString, "norm X/Y: %f, %f", (float)this->mMouseCursorX/ScreenWidth, (float)this->mMouseCursorY/ScreenHeight); -// CenterPrint(theDebugString); - } - else - { - this->ResetTopDownUI(); - } - - // Update orders - //for(OrderListType::iterator theIter = this->mOrders.begin(); theIter != this->mOrders.end(); theIter++) - //{ - // theIter->Update(); - //} - - this->UpdateTechNodes(); - - this->UpdateAmbientSounds(); - - this->UpdateViewModelEffects(); - - mOverviewMap.UpdateOrders(mOrders, GetDrawPlayerOrders()); - mOverviewMap.Update(inCurrentTime); - - float theTimePassed = inCurrentTime - this->mTimeOfLastUpdate; - - AvHParticleSystemManager::Instance()->Update(theTimePassed); - - AvHScriptManager::Instance()->ClientUpdate(theTimePassed); - - this->UpdateResources(theTimePassed); - - if((this->GetHUDPlayMode() == PLAYMODE_PLAYING) && !this->GetIsAlive()) - { - this->mCommanderResourceLabel->setVisible(false); - this->mHierarchy->setVisible(false); - this->mShowMapHierarchy->setVisible(false); - } - - if(cl_particleinfo->value) - { - char theDebugText[128]; - int theNumVisible = AvHParticleSystemManager::Instance()->GetNumVisibleParticleSystems(); - int theNum = AvHParticleSystemManager::Instance()->GetNumberParticleSystems(); - int theNumTemplates = gParticleTemplateList.GetNumberTemplates(); - sprintf(theDebugText, "(vis, total, list): %d, %d, %d", theNumVisible, theNum, theNumTemplates); - - //sprintf(theDebugText, "step interval: %d", pmove->flTimeStepSound); - - /* - if(this->mMarineResourceLabel) - { - this->mMarineResourceLabel->setText(theDebugText); - this->mMarineResourceLabel->setVisible(true); - } - */ - - } - - if(!gEngfuncs.pDemoAPI->IsPlayingback()) - { - IN_GetMousePos(&this->mMouseCursorX, &this->mMouseCursorY); - } - - // Update user3 and team - this->mLastUser3 = this->GetHUDUser3(); - this->mLastTeamNumber = this->GetHUDTeam(); - this->mLastPlayMode = this->GetPlayMode(); - - this->mTimeOfLastUpdate = inCurrentTime; - - // Save view origin and angles before we do crazy viewport stuff - // gPlayerOrigin = v_origin; - // gPlayerAngles = v_angles; - } - - return theSuccess; -} - -//void AvHHud::UpdateSelectionEffects(float inTimePassed) -//{ -// // Radians/sec -// const float kSpinRate = 1.5f; -// for(SelectionListType::iterator theIter = this->mSelectionEffects.begin(); theIter != this->mSelectionEffects.end(); theIter++) -// { -// theIter->mAngleOffset = (theIter->mAngleOffset += inTimePassed*kSpinRate) % 360; -// } -//} - -bool AvHHud::GetAndClearTechEvent(AvHMessageID& outMessageID) -{ - bool theTechEventWaiting = false; - - if(this->mTechEvent != MESSAGE_NULL) - { - outMessageID = this->mTechEvent; - theTechEventWaiting = true; - this->mTechEvent = MESSAGE_NULL; - } - - return theTechEventWaiting; -} - -bool AvHHud::GetLastHotkeySelectionEvent(AvHMessageID& outMessageID) -{ - bool theSuccess = false; - - switch(this->mLastHotkeySelectionEvent) - { - case GROUP_SELECT_1: - case GROUP_SELECT_2: - case GROUP_SELECT_3: - case GROUP_SELECT_4: - case GROUP_SELECT_5: - outMessageID = this->mLastHotkeySelectionEvent; - theSuccess = true; - break; - } - - return theSuccess; -} - -void AvHHud::SetLastHotkeySelectionEvent(AvHMessageID inMessageID) -{ - switch(inMessageID) - { - case MESSAGE_NULL: - case GROUP_CREATE_1: - case GROUP_CREATE_2: - case GROUP_CREATE_3: - case GROUP_CREATE_4: - case GROUP_CREATE_5: - case GROUP_SELECT_1: - case GROUP_SELECT_2: - case GROUP_SELECT_3: - case GROUP_SELECT_4: - case GROUP_SELECT_5: - case COMMANDER_REMOVESELECTION: - this->mLastHotkeySelectionEvent = inMessageID; - break; - - default: - ASSERT(false); - break; - } -} - -bool AvHHud::GetIsAlien() const -{ - bool theIsAlien = false; - - AvHUser3 theUser3 = this->GetHUDUser3(); - - switch(theUser3) - { - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: - case AVH_USER3_ALIEN_EMBRYO: - theIsAlien = true; - break; - } - - return theIsAlien; -} - -bool AvHHud::GetIsBeingDigested() const -{ - bool theIsBeingDigested = false; - - int theUpgrades = this->GetHUDUpgrades(); - - if(GetHasUpgrade(theUpgrades, MASK_DIGESTING)) - { - cl_entity_t* theVisiblePlayer = this->GetVisiblePlayer(); - if(theVisiblePlayer && (theVisiblePlayer->curstate.effects & EF_NODRAW)) - { - theIsBeingDigested = true; - } - } - - return theIsBeingDigested; -} - -bool AvHHud::GetIsEnsnared() const -{ - int theUpgrades = this->GetHUDUpgrades(); - return GetHasUpgrade(theUpgrades, MASK_ENSNARED); -} - -bool AvHHud::GetIsStunned() const -{ - int theUpgrades = this->GetHUDUpgrades(); - return GetHasUpgrade(theUpgrades, MASK_PLAYER_STUNNED); -} - -bool AvHHud::GetIsDigesting() const -{ - bool theIsDigesting = false; - - int theUpgrades = this->GetHUDUpgrades(); - - if(GetHasUpgrade(theUpgrades, MASK_DIGESTING)) - { - cl_entity_t* theVisiblePlayer = this->GetVisiblePlayer(); - if(theVisiblePlayer && !(theVisiblePlayer->curstate.effects & EF_NODRAW)) - { - theIsDigesting = true; - } - } - - return theIsDigesting; -} - -bool AvHHud::GetIsNotInControl() const -{ - return GetIsBeingDigested() || !IEngineStudio.IsHardware(); -} - -bool AvHHud::GetIsInTopDownMode() const -{ - bool theIsInTopDownMode = false; - - if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_TOPDOWN)) - { - theIsInTopDownMode = true; - } - - return theIsInTopDownMode; -} - -int AvHHud::GetCommanderIndex() const -{ - int theCommanderIndex = -1; - - for(int i = 1; i <= MAX_PLAYERS; i++) - { - extra_player_info_t* theExtraPlayerInfo = &g_PlayerExtraInfo[i]; - ASSERT(theExtraPlayerInfo); - int thePlayerClass = theExtraPlayerInfo->playerclass; - if(thePlayerClass == PLAYERCLASS_COMMANDER) - { - theCommanderIndex = i; - break; - } - } - - return theCommanderIndex; -} - - -bool AvHHud::GetHasJetpack() const -{ - int theLocalUpgrades = this->GetHUDUpgrades(); - bool theHasJetpackUpgrade = GetHasUpgrade(theLocalUpgrades, MASK_UPGRADE_7) && this->GetIsMarine(); - - return theHasJetpackUpgrade; -} - -bool AvHHud::GetHasAlienUpgradesAvailable() const -{ - bool theHasUpgradesAvailable = false; - - if(this->GetIsAlien() && this->GetIsRelevant() && !this->GetIsBeingDigested()) - { - int theUpgradeVar = this->GetLocalUpgrades(); - bool theHasDefensiveUpgradesAvailable = AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_DEFENSE, this->mUpgrades, theUpgradeVar); - bool theHasMovementUpgradesAvailable = AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_MOVEMENT, this->mUpgrades, theUpgradeVar); - bool theHasSensoryUpgradesAvailable = AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_SENSORY, this->mUpgrades, theUpgradeVar); - - theHasUpgradesAvailable = theHasDefensiveUpgradesAvailable || theHasMovementUpgradesAvailable || theHasSensoryUpgradesAvailable; - } - - return theHasUpgradesAvailable; -} - -bool AvHHud::GetIsMarine() const -{ - bool theIsMarine = false; - - AvHUser3 theUser3 = this->GetHUDUser3(); - - switch(theUser3) - { - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_COMMANDER_PLAYER: - theIsMarine = true; - break; - } - - return theIsMarine; -} - -bool AvHHud::GetIsRelevant() const -{ - bool theIsRelevant = false; - - if(this->GetIsAlive() && (this->GetPlayMode() == PLAYMODE_PLAYING) /*&& !this->GetIsSpectator()*/) - { - theIsRelevant = true; - } - - return theIsRelevant; -} - -vec3_t AvHHud::GetVisualOrigin() const -{ - vec3_t theVisualOrigin = gPredictedPlayerOrigin; - - theVisualOrigin.z += gPredictedPlayerVOfs[2]; - - return theVisualOrigin; -} - -AvHMessageID AvHHud::HotKeyHit(char inChar) -{ - return gCommanderHandler.HotKeyHit(inChar); -} - -float AvHHud::GetGammaSlope() const -{ - return sGameGammaTable.GetGammaSlope(); -} - -string AvHHud::GetMapName(bool inLocalOnly) const -{ - string theMapName = this->mMapName; - - if((theMapName == "") && !inLocalOnly ) - { - const char* theLevelName = gEngfuncs.pfnGetLevelName(); - if(theLevelName) - { - theMapName = string(theLevelName); - - // Remove maps/ from the beginning and .bsp from the end. - StringVector theVector; - Tokenizer::split(theMapName, "/.", theVector); - if(theVector.size() >= 2) - { - theMapName = theVector[1]; - } - } - } - - return theMapName; -} - -int AvHHud::GetNumActiveHives() const -{ - int theNumActiveHives = 0; - - if(this->GetIsAlien()) - { - for(HiveInfoListType::const_iterator theIter = this->mHiveInfoList.begin(); theIter != this->mHiveInfoList.end(); theIter++) - { - if(theIter->mStatus == kHiveInfoStatusBuilt) - { - theNumActiveHives++; - } - } - } - - return theNumActiveHives; -} - -int AvHHud::GetMaxAlienResources() const -{ - int theMaxAlienResources = kMaxAlienResources; - - if(this->mMaxResources >= 0) - { - theMaxAlienResources = this->mMaxResources; - } - - return theMaxAlienResources; -} - -bool AvHHud::SetGamma(float inSlope) -{ - bool theSuccess = false; - - // Disable gamma stuff in debug for sanity -// #ifndef DEBUG - - HDC theDC = GetDC(NULL); - if(theDC != 0) - { - const float kGammaIncrement = 0.05f; - float theGammaToTry = inSlope + kGammaIncrement; - while(!theSuccess && (theGammaToTry > 1.0f)) - { - theGammaToTry -= kGammaIncrement; - - sGameGammaTable.ProcessSlope(theGammaToTry); - // tankefugl: fakes a successful gamma ramp change if cl_gammaramp is set to 0 - if((CVAR_GET_FLOAT(kvGammaRamp) == 0) || sGameGammaTable.InitializeToVideoState()) - { - // Tell UI components so they can change shading to look the same - this->GetManager().NotifyGammaChange(theGammaToTry); - - // aww yeah - theSuccess = true; - } - } - - char theMessage[256]; - if(theSuccess) - { - sprintf(theMessage, "Gamma set to %f.", theGammaToTry); - } - else - { - sprintf(theMessage, "Display doesn't support downloadable gamma ramps."); - } - - if(!theSuccess || (gEngfuncs.GetMaxClients() == 1)) - { - CenterPrint(theMessage); - } - - if(!ReleaseDC(NULL, theDC)) - { - // emit error about leak - } - } - - //#endif - - return theSuccess; -} - -bool AvHHud::SlotInput(int inSlot) -{ - bool theHandled = false; - - if(this->mInTopDownMode) - { - if((inSlot >= 0) && (inSlot < kNumHotkeyGroups)) - { - // TODO: Read state of control/duck here - bool theCreateGroup = false; - - int theButtonBits = CL_ButtonBits(0); - if(theButtonBits & IN_DUCK) - { - theCreateGroup = true; - } - - int theBaseOffset = theCreateGroup ? GROUP_CREATE_1 : GROUP_SELECT_1; - - this->mGroupEvent = (AvHMessageID)(theBaseOffset + inSlot); - - theHandled = true; - } - } - - return theHandled; -} - -int AvHHud::Redraw( float flTime, int intermission ) -{ - - if (!gViewPort->IsOptionsMenuVisible() && - !gParticleEditorHandler.GetInEditMode()) - { - Render(); - } - - int theRC = UIHud::Redraw(flTime, intermission); - - return theRC; -} - -void AvHHud::ResetGammaAtExit() -{ - sPregameGammaTable.InitializeToVideoState(); -} - -int AvHHud::ResetGammaAtExitForOnExit() -{ - sPregameGammaTable.InitializeToVideoState(); - return TRUE; -} - -void AvHHud::ResetGammaAtExit(int inSig) -{ - AvHHud::ResetGammaAtExit(); -} - -void AvHHud::ResetTopDownUI() -{ - this->mGhostBuilding = MESSAGE_NULL; - this->mSelected.clear(); - this->mSelectionEffects.clear(); - - gCommanderHandler.Reset(); - - for(int i = 0; i < kNumHotkeyGroups; i++) - { - this->mGroups[i].clear(); - this->mGroupTypes[i] = AVH_USER3_NONE; - this->mGroupAlerts[i] = ALERT_NONE; - } - - this->mSelectAllGroup.clear(); -} - -void AvHHud::SetSelectingWeaponID(int inWeaponID, int inR, int inG, int inB) -{ - if(gEngfuncs.pfnGetCvarFloat(kvAutoHelp)) - { - if(inR != -1) - { - this->mHelpMessage.SetR(inR); - } - if(inG != -1) - { - this->mHelpMessage.SetG(inG); - } - if(inB != -1) - { - this->mHelpMessage.SetB(inB); - } - - this->mSelectingWeaponID = inWeaponID; - } -} - -void AvHHud::SetTechHelpText(const string& inTechHelpText) -{ - this->mTechHelpText = inTechHelpText; -} - -BIND_MESSAGE(Countdown); -int AvHHud::Countdown(const char* pszName, int iSize, void* pbuf) -{ - NetMsg_UpdateCountdown( pbuf, iSize, this->mNumTicksToPlay ); - this->mLastTickPlayed = 1; - this->mCountDownClock = this->m_flTime; - - return 1; -} - -bool AvHHud::GetAmbientSoundNameFromIndex(string& outSoundName, int inSoundIndex) const -{ - bool theFoundName = false; - - if(inSoundIndex < (int)(this->mSoundNameList.size())) - { - outSoundName = this->mSoundNameList[inSoundIndex]; - theFoundName = true; - } - - return theFoundName; -} - -void AvHHud::ModifyAmbientSoundEntryIfChanged(bool inSoundOn, int inSoundIndex, int inEntIndex, float inTimeStarted, int inVolume, int inFadeDistance, int inFlags, Vector inOrigin) -{ - bool theFoundSound = false; - - // Look up sound using inSoundIndex - string theSoundName; - if(this->GetAmbientSoundNameFromIndex(theSoundName, inSoundIndex)) - { - // Loop through current sounds - for(AmbientSoundListType::iterator theIter = this->mAmbientSounds.begin(); theIter != this->mAmbientSounds.end(); ) - { - bool theErasedSound = false; - if(theIter->GetEntityIndex() == inEntIndex) - { - // If found, remember that we found it - theFoundSound = true; - - // Set position - theIter->SetPosition(inOrigin); - - // If we're turning off sound, kill the sound - if(!inSoundOn) - { - theIter->ClearData(); - theIter = this->mAmbientSounds.erase(theIter); - theErasedSound = true; - } - } - if(!theErasedSound) - { - theIter++; - } - } - - // If we're turning a sound on, and we didn't find one - if(inSoundOn && !theFoundSound) - { - bool theLooping = inFlags & 2; - float theTimeElapsed = this->mTimeOfLastUpdate - inTimeStarted; - - // Add new entry with these values - this->mAmbientSounds.push_back(AvHAmbientSound(theSoundName, inVolume, inFadeDistance, theLooping, inOrigin, inEntIndex, theTimeElapsed)); - } - } - else - { - // We may not have the sound list yet, it's OK - //ASSERT(false); - } -} - -// tankefugl: -void AvHHud::SetCenterText(const char* inText) -{ - LocalizeString(inText, this->mCenterText); - this->mCenterTextTime = this->mTimeOfLastUpdate; -} - -void AvHHud::ClearCenterText() -{ - this->mCenterText.clear(); - this->mCenterTextTime = -1; -} - -// :tankefugl - -// Look at incoming order. If we are one of the receivers, play a HUD sound -// indicating our new order -void AvHHud::OrderNotification(const AvHOrder& inOrder) -{ - //if(!inOrder.GetOrderCompleted()) - //{ - // If we are commander, or we are in receiver list - int theLocalPlayer = gEngfuncs.GetLocalPlayer()->index; - if((this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) || (inOrder.GetHasReceiver(theLocalPlayer))) - { - // Do a switch on the order type - AvHOrderType theOrderType = inOrder.GetOrderType(); - AvHHUDSound theSound = HUD_SOUND_INVALID; - - // tankefugl: 0000992 - // popup indicator for order - bool thePopup = false; - - // Play HUD sound depending on order - switch(theOrderType) - { - case ORDERTYPEL_MOVE: - theSound = HUD_SOUND_ORDER_MOVE; - thePopup = true; - break; - - case ORDERTYPET_ATTACK: - theSound = HUD_SOUND_ORDER_ATTACK; - thePopup = true; - break; - - case ORDERTYPET_BUILD: - theSound = HUD_SOUND_ORDER_BUILD; - thePopup = true; - break; - - case ORDERTYPET_GUARD: - theSound = HUD_SOUND_ORDER_GUARD; - thePopup = true; - break; - - case ORDERTYPET_WELD: - theSound = HUD_SOUND_ORDER_WELD; - thePopup = true; - break; - - case ORDERTYPET_GET: - theSound = HUD_SOUND_ORDER_GET; - thePopup = true; - break; - } - - if((this->GetHUDUser3() == AVH_USER3_MARINE_PLAYER) && (inOrder.GetOrderCompleted())) - { - theSound = HUD_SOUND_ORDER_COMPLETE; - } - - this->PlayHUDSound(theSound); - - // tankefugl: 0000992 | 0001052 - if (thePopup && (this->GetInTopDownMode() == false) && (inOrder.GetOrderActive())) - { - this->SetDisplayOrder(2, this->GetFrameForOrderType(theOrderType), "", "", ""); - } - // :tankefugl - } - //} -} - -void AvHHud::ResetComponentsForUser3() -{ - - this->mPieMenuControl = ""; - - this->GetManager().HideComponents(); - - if(gParticleEditorHandler.GetInEditMode()) - { - gHUD.GetManager().UnhideComponent(kPSESizeSlider); - gHUD.GetManager().UnhideComponent(kPSESizeLabel); - - gHUD.GetManager().UnhideComponent(kPSEScaleSlider); - gHUD.GetManager().UnhideComponent(kPSEScaleLabel); - - gHUD.GetManager().UnhideComponent(kPSEGenerationRateSlider); - gHUD.GetManager().UnhideComponent(kPSEGenerationRateLabel); - - gHUD.GetManager().UnhideComponent(kPSEParticleLifetimeSlider); - gHUD.GetManager().UnhideComponent(kPSEParticleLifetimeLabel); - - gHUD.GetManager().UnhideComponent(kPSEParticleSystemLifetimeSlider); - gHUD.GetManager().UnhideComponent(kPSEParticleSystemLifetimeLabel); - - gHUD.GetManager().UnhideComponent(kPSEMaxParticlesSlider); - gHUD.GetManager().UnhideComponent(kPSEMaxParticlesLabel); - - gHUD.GetManager().UnhideComponent(kPSEDrawModeSlider); - gHUD.GetManager().UnhideComponent(kPSEDrawModeLabel); - - gHUD.GetManager().UnhideComponent(PSEGenVelToggleSlider); - gHUD.GetManager().UnhideComponent(kPSEGenVelToggleLabel); - - gHUD.GetManager().UnhideComponent(kPSEGenVelShapeSlider); - gHUD.GetManager().UnhideComponent(kPSEGenVelShapeLabel); - - gHUD.GetManager().UnhideComponent(kPSEGenVelParamNumSlider); - gHUD.GetManager().UnhideComponent(kPSEGenVelParamNumLabel); - - gHUD.GetManager().UnhideComponent(kPSEGenVelParamValueSlider); - gHUD.GetManager().UnhideComponent(kPSEGenVelParamValueLabel); - } - else - { - bool theIsCombatMode = (this->mMapMode == MAP_MODE_CO); - bool theIsNSMode = (this->mMapMode == MAP_MODE_NS); - - if((this->GetHUDPlayMode() == PLAYMODE_PLAYING) && !this->GetIsNotInControl() && !gViewPort->IsOptionsMenuVisible()) - { - switch(this->GetHUDUser3()) - { - case AVH_USER3_MARINE_PLAYER: - if(theIsCombatMode) - { - this->mPieMenuControl = kSoldierCombatMenu; - } - else if(theIsNSMode) - { - this->mPieMenuControl = kSoldierMenu; - } - - if (g_iUser1 == OBS_NONE) - { - this->GetManager().UnhideComponent(mPieMenuControl.c_str()); - } - - // Removed these for recording footage until they look better - //this->GetManager().UnhideComponent(kReinforcementsLabel); - //this->GetManager().UnhideComponent(kResourceLabel); - //this->GetManager().UnhideComponent(kMouseCursorLabel); - //this->GetManager().UnhideComponent(kDebugCSPServerLabel); - //this->GetManager().UnhideComponent(kDebugCSPClientLabel); - break; - - case AVH_USER3_COMMANDER_PLAYER: - if(this->mInTopDownMode) - { - this->GetManager().UnhideComponent(kSelectionBox); - this->GetManager().UnhideComponent(kCommanderResourceLabel); - //this->GetManager().UnhideComponent(kMouseCursorLabel); - this->GetManager().UnhideComponent(kLeaveCommanderButton); - this->GetManager().UnhideComponent(kScroller); - this->GetManager().UnhideComponent(kTechHelpText); - this->GetManager().UnhideComponent(kHierarchy); - this->GetManager().UnhideComponent(kResearchBackgroundPanel); - this->GetManager().UnhideComponent(kActionButtonsComponents); - this->GetManager().UnhideComponent(kSelectAllImpulsePanel); - //this->GetManager().UnhideComponent(kTopDownHUDTopSpritePanel); - //this->GetManager().UnhideComponent(kTopDownHUDBottomSpritePanel); - } - //this->GetManager().UnhideComponent(kReinforcementsLabel); - break; - - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: - if(theIsCombatMode) - { - this->mPieMenuControl = kAlienCombatMenu; - } - else if(theIsNSMode) - { - this->mPieMenuControl = kAlienMenu; - } - - if (g_iUser1 == OBS_NONE) - { - this->GetManager().UnhideComponent(mPieMenuControl.c_str()); - } - - //this->GetManager().UnhideComponent(kMouseCursorLabel); - //this->GetManager().UnhideComponent(kDebugCSPServerLabel); - //this->GetManager().UnhideComponent(kDebugCSPClientLabel); - break; - - case AVH_USER3_ALIEN_EMBRYO: - //this->GetManager().UnhideComponent(kAlienMembrane); - break; - } - - if(sShowMap) - { - this->GetManager().UnhideComponent(kShowMapHierarchy); - } - - mOverviewMap.SetUser3(this->GetHUDUser3()); - - // Update command hierarchy so it can potentially display differently - if(this->mHierarchy) - { - this->mHierarchy->setPos(.0105f*ScreenWidth(), .728*ScreenHeight()); - this->mHierarchy->setSize(.265*ScreenWidth(), .247*ScreenHeight()); - } - - } - } -} - -BIND_MESSAGE(BalanceVar); -int AvHHud::BalanceVar(const char* pszName, int iSize, void* pbuf) -{ - string name; - BalanceMessageAction action; - int ivalue; - float fvalue; - string svalue; - NetMsg_BalanceVar( pbuf, iSize, name, action, ivalue, fvalue, svalue ); - BalanceValueContainer* container = BalanceValueContainerFactory::get(); - - switch( action ) - { - case BALANCE_ACTION_INSERT_INT: - container->insert(name,ivalue); - break; - case BALANCE_ACTION_INSERT_FLOAT: - container->insert(name,fvalue); - break; - case BALANCE_ACTION_INSERT_STRING: - container->insert(name,svalue); - break; - case BALANCE_ACTION_REMOVE: - container->remove(name); - break; - case BALANCE_ACTION_CLEAR: - container->clear(); - break; - } - return 1; -} - -BIND_MESSAGE(GameStatus); -int AvHHud::GameStatus(const char* pszName, int iSize, void* pbuf) -{ - int status, game_time, timelimit, misc_data; - AvHMapMode map_mode; - NetMsg_GameStatus( pbuf, iSize, status, map_mode, game_time, timelimit, misc_data ); - - this->mMapMode = map_mode; - - switch( status ) - { - case kGameStatusReset: - case kGameStatusResetNewMap: - if(this->mInTopDownMode) - { - this->ToggleMouse(); - } - - this->ResetGame( status == kGameStatusResetNewMap ? true : false ); - break; - case kGameStatusEnded: // Victor determined, but we are still in the cooldown time - this->mGameEnded = true; // Stop research - break; - case kGameStatusGameTime: - this->mGameTime = game_time; - this->mTimeLimit = timelimit; - this->mCombatAttackingTeamNumber = misc_data; - break; - case kGameStatusUnspentLevels: - this->mExperienceLevelSpent = misc_data; - break; - } - - return 1; -} - -BIND_MESSAGE(MiniMap); -int AvHHud::MiniMap(const char* pszName, int iSize, void* pbuf) -{ - gMiniMap.ReceiveFromNetworkStream( pbuf, iSize ); - return 1; -} - -// tankefugl: 0000971 -BIND_MESSAGE(IssueOrder); -int AvHHud::IssueOrder(const char* pszName, int iSize, void* pbuf) -{ - int ordertype, ordersource, ordertarget; - NetMsg_IssueOrder( pbuf, iSize, ordertype, ordersource, ordertarget); - - float now = this->GetTimeOfLastUpdate(); - TeammateOrderListType::iterator theIter = this->mTeammateOrder.find(ordersource); - if (theIter == this->mTeammateOrder.end()) - { - this->mTeammateOrder.insert(theIter, pair(ordersource, TeammateOrderType(ordertype, now))); - } - else - { - TeammateOrderType *theOrder = &((*theIter).second); - (*theOrder).first = ordertype; - (*theOrder).second = now; - } - - if (this->GetInTopDownMode() == false) - { - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - if (theLocalPlayer->index == ordertarget) - { - hud_player_info_t info; - memset(&info, 0, sizeof(info)); - GetPlayerInfo(ordersource, &info); - - string temp; - string nameFormat; - // fetch from titles.txt - sprintf(temp, "TeammateOrder%d", ordertype); - LocalizeString(temp.c_str(), nameFormat); - sprintf(temp, nameFormat.c_str(), info.name); - - this->SetDisplayOrder(1, ordertype, temp, "", ""); - } - if (theLocalPlayer->index == ordersource) - { - this->mCurrentOrderTarget = ordertarget; - this->mCurrentOrderType = ordertype; - this->mCurrentOrderTime = now; - } - } - - return 1; -} -// :tankefugl - -BIND_MESSAGE(ServerVar); -int AvHHud::ServerVar(const char* pszName, int iSize, void* pbuf) -{ - string name, value; - NetMsg_ServerVar( pbuf, iSize, name, value ); - - mServerVariableMap[name] = value; - return 1; -} - -BIND_MESSAGE(Progress); -int AvHHud::Progress(const char* pszName, int iSize, void* pbuf) -{ - NetMsg_ProgressBar( pbuf, iSize, this->mProgressBarEntityIndex, this->mProgressBarParam ); - return 1; -} - -// Start the game over. Called after the game ends and also after a map change -void AvHHud::ResetGame(bool inMapChanged) -{ - UIHud::ResetGame(); - - this->mResources = 0; - this->mMaxResources = -1; - this->mVisualResources = 0; - this->mUser2OfLastResourceMessage = 0; - this->mTimeOfLastEntityUpdate = -1; - this->mVisualEnergyLevel = 0; - this->mUser2OfLastEnergyLevel = 0; - - // Don't use a menu yet - //this->ResetUpgradeCosts(); - - // Reset armor as well. - //this->mArmorLevel = ARMOR_BASE; - - // Clear out all particle systems and templates - if(inMapChanged) - { - gParticleTemplateList.Clear(); - this->mTimeOfLastUpdate = 0.0f; - this->mInfoLocationList.clear(); - } - - // puzl: 1066 reset overview map on game restart - gHUD.GetOverviewMap().Clear(); - - AvHParticleSystemManager::Instance()->Reset(); - - this->mTechSlotManager.Clear(); - - this->mTechNodes.Clear(); - - this->mTimeOfCurrentUpdate = 0.0f; - - // On game reset, clear blips (happens on server as well) - - this->mEntityHierarchy.Clear(); - - // Clear selection effects - this->mSelectionEffects.clear(); - - // End any jetpack effects - //EndJetpackEffects(); - - // Clear client scripts - AvHScriptManager::Instance()->Reset(); - - // Selection and commander variables - this->mNumLocalSelectEvents = 0; - this->mMapMode = MAP_MODE_UNDEFINED; - this->mInTopDownMode = false; - this->mLeftMouseStarted = false; - this->mLeftMouseEnded = false; - this->mPlacingBuilding = false; - - sShowMap = false; - - this->StopMusic(); - - for(AmbientSoundListType::iterator theIter = this->mAmbientSounds.begin(); theIter != this->mAmbientSounds.end(); theIter++) - { - theIter->ClearData(); - } - this->mAmbientSounds.clear(); - - this->SetReinforcements(0); - - this->mOrders.clear(); - - this->mCurrentCursorFrame = 0; - this->mProgressBarEntityIndex = -1; - this->mProgressBarParam = -1; - - this->mEnemyBlips.Clear(); - this->mFriendlyBlips.Clear(); - - // Reset view angles (in case player was in commander mode) - gViewAngles.z = 0.0f; - gResetViewAngles = true; - - // Clear location - this->mLocationText = ""; - - this->mUpgrades.clear(); - - this->mNumUpgradesAvailable = 0; - int i; - for(i = 0; i < ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE; i++) - { - this->mCurrentUpgradeCategory[i] = ALIEN_UPGRADE_CATEGORY_INVALID; - } - - // Remove all decals (no idea how to get the total decals) - //for(i = 0; i < 1024; i++) - //{ - // gEngfuncs.pEfxAPI->R_DecalRemoveAll(i); - //} - RemoveAllDecals(); - - // Remove temp ghost building - if(this->mLastGhostBuilding) - { - this->mLastGhostBuilding->die = -1; - } - - this->mNumericalInfoEffects.clear(); - this->mTimeOfNextHudSound = -1; - this->mLastHUDSoundPlayed = HUD_SOUND_INVALID; - - //this->mTooltips.clear(); - - this->mHiveInfoList.clear(); - - this->mDesiredGammaSlope = kDefaultMapGamma; - this->mRecordingLastFrame = false; - this->mTimeOfLastHelpText = -1; - this->mDisplayedToolTipList.clear(); - this->mCurrentWeaponID = -1; - this->mCurrentWeaponEnabled = false; - - // Is this needed? - //this->mCurrentUIMode = MAIN_MODE; - - this->mMenuTechSlots = 0; - this->mPendingRequests.clear(); - - for(i = 0; i < kNumHotkeyGroups; i++) - { - this->mGroups[i].clear(); - this->mGroupTypes[i] = AVH_USER3_NONE; - this->mGroupAlerts[i] = ALERT_NONE; - } - this->mSelectAllGroup.clear(); - - this->mCurrentSquad = 0; - this->mBlinkingAlertType = 0; - - this->mLastTeamSpectated = TEAM_IND; - - this->mStructureNotificationList.clear(); - - this->mGameTime = -1; - this->mTimeLimit = -1; - this->mCombatAttackingTeamNumber = 0; - this->mGameEnded = false; - - this->mExperience = 0; - this->mExperienceLevel = 1; - this->mExperienceLevelLastDrawn = 1; - this->mExperienceLevelSpent = 0; - this->mTimeOfLastLevelUp = -1; - - memset(this->mMenuImpulses, MESSAGE_NULL, sizeof(AvHMessageID)*kNumUpgradeLines); - - // tankefugl: 0000992 & 0000971 - this->mTeammateOrder.clear(); - this->mCurrentOrderTarget = 0; - this->mCurrentOrderType = 0; - this->mCurrentOrderTime = 0.0f; - - this->mDisplayOrderTime = 0.0f; - this->mDisplayOrderType = 0; - this->mDisplayOrderIndex = 0; - this->mDisplayOrderText1 = ""; - this->mDisplayOrderText2 = ""; - this->mDisplayOrderText3 = ""; - - this->mCenterText.clear(); - this->mCenterTextTime = -1; - // :tankefugl - - // SCRIPTENGINE: - if (inMapChanged) - { - gLUA->Init(); - gLUA->LoadLUAForMap(this->GetMapName().c_str()); - } - // :SCRIPTENGINE -} - -// SCRIPTENGINE: -BIND_MESSAGE(LUAmsg); -int AvHHud::LUAmsg(const char* pszName, int iSize, void* pbuf) -{ - int arguments; - lua_State *L = lua_newthread(gLUA->mGlobalContext); - - NetMsg_LUAMessage(pbuf, iSize, L, arguments); - - if (lua_resume(L, arguments)) - { - AvHLUA_OnError(lua_tostring(L, -1)); - } - return 1; -} -// :SCRIPTENGINE - -BIND_MESSAGE(SetGmma); -int AvHHud::SetGmma(const char* pszName, int iSize, void* pbuf) -{ - NetMsg_SetGammaRamp( pbuf, iSize, this->mDesiredGammaSlope ); - if (!mSteamUIActive) - { - this->SetGamma(this->mDesiredGammaSlope); - } - - return 1; -} - -BIND_MESSAGE(BlipList); -int AvHHud::BlipList(const char* pszName, int iSize, void* pbuf) -{ - bool friendly_blips; - AvHVisibleBlipList list; - NetMsg_BlipList( pbuf, iSize, friendly_blips, list ); - - float theCurrentTime = gEngfuncs.GetClientTime(); - - if( friendly_blips ) - { - this->mFriendlyBlips.Clear(); - this->mFriendlyBlips.AddBlipList(list); - this->mFriendlyBlips.SetTimeBlipsReceived(theCurrentTime); - } - else - { - this->mEnemyBlips.Clear(); - this->mEnemyBlips.AddBlipList(list); - this->mEnemyBlips.SetTimeBlipsReceived(theCurrentTime); - } - return 1; -} - -BIND_MESSAGE(ClScript); -int AvHHud::ClScript(const char *pszName, int iSize, void *pbuf) -{ - StringList script_names; - NetMsg_ClientScripts( pbuf, iSize, script_names ); - StringList::iterator current, end = script_names.end(); - for( current = script_names.begin(); current != end; ++current ) - { - AvHScriptManager::Instance()->RunScript(current->c_str()); - } - - return 1; -} - -BIND_MESSAGE(Particles); -int AvHHud::Particles(const char *pszName, int iSize, void *pbuf) -{ - AvHParticleTemplate particle_template; - NetMsg_SetParticleTemplate( pbuf, iSize, particle_template ); - gParticleTemplateList.Insert( particle_template ); - - return 1; -} - -BIND_MESSAGE(SoundNames); -int AvHHud::SoundNames(const char *pszName, int iSize, void *pbuf) -{ - bool theClearSoundList; - string sound_name; - NetMsg_SetSoundNames( pbuf, iSize, theClearSoundList, sound_name ); - - if(theClearSoundList) - { - this->mSoundNameList.clear(); - } - else - { - this->mSoundNameList.push_back(sound_name); - } - - return 1; -} - -BIND_MESSAGE(SetSelect); -int AvHHud::SetSelect(const char* pszName, int iSize, void* pbuf) -{ - Selection selection; - NetMsg_SetSelect( pbuf, iSize, selection ); - - EntityListType theGroup; - - switch( selection.group_number ) - { - case 0: - this->mTrackingEntity = selection.tracking_entity; - if( selection.selected_entities != this->mSelected ) - { - this->mSelectionJustChanged = true; - this->mSelected = selection.selected_entities; - } - break; - case kSelectAllHotGroup: - this->mSelectAllGroup = selection.selected_entities; - break; - default: - this->mGroups[selection.group_number-1] = selection.selected_entities; - this->mGroupTypes[selection.group_number-1] = selection.group_type; - this->mGroupAlerts[selection.group_number-1] = selection.group_alert; - } - - return 1; -} - -BIND_MESSAGE(SetRequest); -int AvHHud::SetRequest(const char* pszName, int iSize, void* pbuf) -{ - int request_type, request_count; - NetMsg_SetRequest( pbuf, iSize, request_type, request_count ); - this->mPendingRequests[(AvHMessageID)request_type] = request_count; - - return 1; -} - -BIND_MESSAGE(SetOrder); -int AvHHud::SetOrder(const char* pszName, int iSize, void* pbuf) -{ - AvHOrder theNewOrder; - NetMsg_SetOrder( pbuf, iSize, theNewOrder ); - - AvHChangeOrder(this->mOrders, theNewOrder); - - // Give feedback on order - this->OrderNotification(theNewOrder); - - // Run through orders, deleting any that are complete - for(OrderListType::iterator theIter = this->mOrders.begin(); theIter != this->mOrders.end(); /* no inc */) - { - if(theIter->GetOrderCompleted()) - { - this->mOrders.erase(theIter); - } - else - { - theIter++; - } - } - - return 1; -} - -BIND_MESSAGE(SetupMap); -int AvHHud::SetupMap(const char* pszName, int iSize, void* pbuf) -{ - bool is_location, draw_background; - float min_extents[3], max_extents[3]; - string name; - NetMsg_SetupMap( pbuf, iSize, is_location, name, min_extents, max_extents, draw_background ); - - if( is_location ) - { - vec3_t theMaxExtent; - theMaxExtent.x = max_extents[0]; - theMaxExtent.y = max_extents[1]; - theMaxExtent.z = max_extents[2]; - vec3_t theMinExtent; - theMinExtent.x = min_extents[0]; - theMinExtent.y = min_extents[1]; - theMinExtent.z = min_extents[2]; - - this->mInfoLocationList.push_back( AvHBaseInfoLocation( name, theMaxExtent, theMinExtent ) ); - } - else - { - this->mMapName = name; - this->mMapExtents.SetMaxMapX( max_extents[0] ); - this->mMapExtents.SetMaxMapY( max_extents[1] ); - this->mMapExtents.SetMaxViewHeight( max_extents[2] ); - this->mMapExtents.SetMinMapX( min_extents[0] ); - this->mMapExtents.SetMinMapY( min_extents[1] ); - this->mMapExtents.SetMinViewHeight( min_extents[2] ); - this->mMapExtents.SetDrawMapBG( draw_background ); - } - - return 1; -} - - -BIND_MESSAGE(SetTopDown); -int AvHHud::SetTopDown(const char* pszName, int iSize, void* pbuf) -{ - bool is_menu_tech, is_top_down; - float position[3]; - int tech_slots; - NetMsg_SetTopDown( pbuf, iSize, is_menu_tech, is_top_down, position, tech_slots ); - - if(is_menu_tech) - { - this->mMenuTechSlots = tech_slots; - } - else - { - if(this->mInTopDownMode && !is_top_down) - { - // Switch away from top down mode - this->mInTopDownMode = false; - this->ToggleMouse(); - - // Reset angles - gViewAngles[0] = position[0]; - gViewAngles[1] = position[1]; - gViewAngles[2] = position[2]; - - gResetViewAngles = true; - - this->mSelectionEffects.clear(); - } - else if(!this->mInTopDownMode && is_top_down) - { - // Switch to top down mode! - this->mInTopDownMode = true; - this->ToggleMouse(); - } - - if(is_top_down) - { - // Read new PAS - this->mCommanderPAS.x = position[0]; - this->mCommanderPAS.y = position[1]; - this->mCommanderPAS.z = position[2]; - } - } - - return 1; -} - - -BIND_MESSAGE(EntHier); -int AvHHud::EntHier(const char *pszName, int iSize, void *pbuf) -{ - MapEntityMap new_items; - EntityListType old_items; - NetMsg_UpdateEntityHierarchy( pbuf, iSize, new_items, old_items ); - - MapEntityMap::iterator current, end = new_items.end(); - for( current = new_items.begin(); current != end; ++current ) - { - this->mEntityHierarchy.InsertEntity( current->first, current->second ); - } - - EntityListType::iterator d_current, d_end = old_items.end(); - for( d_current = old_items.begin(); d_current != d_end; ++d_current ) - { - this->mEntityHierarchy.DeleteEntity( *d_current ); - } - - return 0; -} - -BIND_MESSAGE(EditPS); -int AvHHud::EditPS(const char* pszName, int iSize, void* pbuf) -{ - int particle_index; - NetMsg_EditPS( pbuf, iSize, particle_index ); - AvHParticleEditorHandler::SetEditIndex((uint32)particle_index); - - return 1; -} - -BIND_MESSAGE(Fog); -int AvHHud::Fog(const char* pszName, int iSize, void* pbuf) -{ - bool enabled; - int R, G, B; - float start, end; - NetMsg_Fog( pbuf, iSize, enabled, R, G, B, start, end ); - - this->mFogActive = enabled; - if(enabled) - { - this->mFogColor.x = R; - this->mFogColor.y = G; - this->mFogColor.z = B; - this->mFogStart = start; - this->mFogEnd = end; - } - - return 1; -} - -BIND_MESSAGE(ListPS); -int AvHHud::ListPS(const char* pszName, int iSize, void* pbuf) -{ - string name; - NetMsg_ListPS( pbuf, iSize, name ); - this->m_SayText.SayTextPrint(name.c_str(), 256, 0); - - return 1; -} - -BIND_MESSAGE(PlayHUDNot); -int AvHHud::PlayHUDNot(const char* pszName, int iSize, void* pbuf) -{ - int message_id, sound; - float location_x, location_y; - NetMsg_PlayHUDNotification( pbuf, iSize, message_id, sound, location_x, location_y ); - - if(message_id == 0) - { - // Hack to avoid adding another network message (at max) - if(!this->GetInTopDownMode()) - { - switch(sound) - { - case HUD_SOUND_SQUAD1: - this->mCurrentSquad = 1; - break; - case HUD_SOUND_SQUAD2: - this->mCurrentSquad = 2; - break; - case HUD_SOUND_SQUAD3: - this->mCurrentSquad = 3; - break; - case HUD_SOUND_SQUAD4: - this->mCurrentSquad = 4; - break; - case HUD_SOUND_SQUAD5: - this->mCurrentSquad = 5; - break; - } - } - else - { - switch((AvHHUDSound)sound) - { - // Danger - case HUD_SOUND_MARINE_SOLDIER_UNDER_ATTACK: - case HUD_SOUND_MARINE_BASE_UNDER_ATTACK: - case HUD_SOUND_MARINE_SENTRYDAMAGED: - case HUD_SOUND_MARINE_SOLDIERLOST: - case HUD_SOUND_MARINE_CCUNDERATTACK: - this->mBlinkingAlertType = 2; - AddMiniMapAlert(location_x, location_y); - break; - - // Just research or something like that - case HUD_SOUND_MARINE_UPGRADE_COMPLETE: - case HUD_SOUND_MARINE_RESEARCHCOMPLETE: - this->mBlinkingAlertType = 1; - AddMiniMapAlert(location_x, location_y); - break; - } - } - - this->PlayHUDSound((AvHHUDSound)sound); - } - else - { - // Push back icon - HUDNotificationType theNotification; - theNotification.mStructureID = (AvHMessageID)sound; //message_id; - theNotification.mTime = this->mTimeOfCurrentUpdate; - theNotification.mLocation = Vector(location_x, location_y, 0.0f); - - if(CVAR_GET_FLOAT(kvBuildMessages)) - { - this->mStructureNotificationList.push_back(theNotification); - } - } - - return 1; -} - -BIND_MESSAGE(AlienInfo); -int AvHHud::AlienInfo(const char* pszName, int iSize, void* pbuf) -{ - bool was_hive_info; - AvHAlienUpgradeListType upgrades; - HiveInfoListType hives; - NetMsg_AlienInfo( pbuf, iSize, was_hive_info, this->mUpgrades, this->mHiveInfoList ); - return 1; -} - -void AvHHud::PlayHUDSound(const char *szSound, float vol, float inSoundLength) -{ - if((this->mTimeOfNextHudSound == -1) || (this->mTimeOfCurrentUpdate > this->mTimeOfNextHudSound)) - { - if(szSound) - { - char theSoundName[512]; - strcpy(theSoundName, szSound); - gEngfuncs.pfnPlaySoundByName(theSoundName, vol); - this->mTimeOfNextHudSound = this->mTimeOfCurrentUpdate + inSoundLength; - } - } -} - -void AvHHud::PlayHUDSound(int iSound, float vol, float inSoundLength) -{ - if((this->mTimeOfNextHudSound == -1) || (this->mTimeOfCurrentUpdate > this->mTimeOfNextHudSound)) - { - gEngfuncs.pfnPlaySoundByIndex(iSound, vol); - this->mTimeOfNextHudSound = this->mTimeOfCurrentUpdate + inSoundLength; - } -} - -void AvHHud::PlayHUDSound(AvHHUDSound inSound) -{ - char* theSoundPtr = NULL; - float theVolume = 1.0f; - - // Some sounds are forced, but don't allow them to be spammed or cut themselves off - bool theForceSound = AvHSHUGetForceHUDSound(inSound) && (inSound != this->mLastHUDSoundPlayed); - - // tankefugl: 0000407 - bool theAutoHelpEnabled = gEngfuncs.pfnGetCvarFloat(kvAutoHelp); - // :tankefugl - - if((this->mTimeOfNextHudSound == -1) || (this->mTimeOfCurrentUpdate >= this->mTimeOfNextHudSound) || theForceSound) - { - float theSoundLength = 2.0f; - - switch(inSound) - { - case HUD_SOUND_POINTS_SPENT: - theSoundPtr = kPointsSpentSound; - theSoundLength = .2f; - break; - case HUD_SOUND_COUNTDOWN: - theSoundPtr = kCountdownSound; - theSoundLength = 0.0f; - break; - case HUD_SOUND_SELECT: - if(gHUD.GetIsAlien()) - theSoundPtr = kSelectAlienSound; - else - theSoundPtr = kSelectSound; - - // Set to 0 so it never blocks other sounds - theVolume = .2f; - theSoundLength = 0.0f; - break; - case HUD_SOUND_SQUAD1: - if(this->GetInTopDownMode()) - { - theSoundPtr = kSquad1Sound; - theSoundLength = 1.2f; - } - else - { - theSoundPtr = kMarineSquad1Sound; - theSoundLength = 2.0f; - } - break; - case HUD_SOUND_SQUAD2: - if(this->GetInTopDownMode()) - { - theSoundPtr = kSquad2Sound; - theSoundLength = 1.2f; - } - else - { - theSoundPtr = kMarineSquad2Sound; - theSoundLength = 2.0f; - } - break; - case HUD_SOUND_SQUAD3: - if(this->GetInTopDownMode()) - { - theSoundPtr = kSquad3Sound; - theSoundLength = 1.2f; - } - else - { - theSoundPtr = kMarineSquad3Sound; - theSoundLength = 2.0f; - } - break; - case HUD_SOUND_SQUAD4: - if(this->GetInTopDownMode()) - { - theSoundPtr = kSquad4Sound; - theSoundLength = 1.2f; - } - else - { - theSoundPtr = kMarineSquad4Sound; - theSoundLength = 2.0f; - } - break; - case HUD_SOUND_SQUAD5: - if(this->GetInTopDownMode()) - { - theSoundPtr = kSquad5Sound; - theSoundLength = 1.2f; - } - else - { - theSoundPtr = kMarineSquad5Sound; - theSoundLength = 2.0f; - } - break; - case HUD_SOUND_PLACE_BUILDING: - theSoundPtr = kPlaceBuildingSound; - theSoundLength = .2f; - break; - - case HUD_SOUND_MARINE_POINTS_RECEIVED: - theSoundPtr = kMarinePointsReceivedSound; - theSoundLength = 1.42f; - break; - - case HUD_SOUND_MARINE_RESEARCHCOMPLETE: - theSoundPtr = kMarineResearchComplete; - theSoundLength = 2.0f; - break; - case HUD_SOUND_MARINE_SOLDIER_UNDER_ATTACK: - theSoundPtr = kMarineSoldierUnderAttack; - theSoundLength = 3.0f; - break; - case HUD_SOUND_MARINE_CCONLINE: - if(rand() % 2) - { - theSoundPtr = kMarineCCOnline1; - } - else - { - theSoundPtr = kMarineCCOnline2; - } - theSoundLength = 2.3f; - break; - case HUD_SOUND_MARINE_CCUNDERATTACK: - if(rand() % 2) - { - theSoundPtr = kMarineCCUnderAttack1; - } - else - { - theSoundPtr = kMarineCCUnderAttack2; - } - theSoundLength = 3.0f; - break; - case HUD_SOUND_MARINE_COMMANDER_EJECTED: - theSoundPtr = kMarineCommanderEjected; - theSoundLength = 3.0f; - break; - case HUD_SOUND_MARINE_BASE_UNDER_ATTACK: - if(rand() % 2) - { - theSoundPtr = kMarineBaseUnderAttack1; - } - else - { - theSoundPtr = kMarineBaseUnderAttack2; - } - theSoundLength = 3.0f; - break; - case HUD_SOUND_MARINE_UPGRADE_COMPLETE: - theSoundPtr = kMarineUpgradeComplete; - theSoundLength = 1.0f; - break; - case HUD_SOUND_MARINE_MORE: - theSoundPtr = kMarineMoreResources; - theSoundLength = 1.8f; - break; - case HUD_SOUND_MARINE_RESOURCES_LOW: - theSoundPtr = kMarineLowResources; - theSoundLength = 2.0f; - break; - case HUD_SOUND_MARINE_NEEDS_AMMO: - if(rand() % 2) - theSoundPtr = kMarineNeedsAmmo1; - else - theSoundPtr = kMarineNeedsAmmo2; - theSoundLength = 1.5f; - break; - case HUD_SOUND_MARINE_NEEDS_HEALTH: - if(rand() % 2) - theSoundPtr = kMarineNeedsHealth1; - else - theSoundPtr = kMarineNeedsHealth2; - theSoundLength = 1.3f; - break; - - case HUD_SOUND_MARINE_NEEDS_ORDER: - if(rand() % 2) - theSoundPtr = kMarineNeedsOrder1; - else - theSoundPtr = kMarineNeedsOrder2; - theSoundLength = 1.5f; - break; - - case HUD_SOUND_MARINE_SOLDIER_LOST: - if(rand() % 2) - theSoundPtr = kMarineSoldierLost1; - else - theSoundPtr = kMarineSoldierLost2; - theSoundLength = 1.3f; - break; - case HUD_SOUND_MARINE_SENTRYFIRING: - if(rand() % 2) - theSoundPtr = kMarineSentryFiring1; - else - theSoundPtr = kMarineSentryFiring2; - theSoundLength = 1.3f; - break; - case HUD_SOUND_MARINE_SENTRYDAMAGED: - if(rand() % 2) - theSoundPtr = kMarineSentryTakingDamage1; - else - theSoundPtr = kMarineSentryTakingDamage2; - theSoundLength = 1.5f; - break; - - case HUD_SOUND_MARINE_GIVEORDERS: - // tankefugl: 0000407 - if (theAutoHelpEnabled) - { - theSoundPtr = kMarineGiveOrders; - theSoundLength = 2.2f; - } - // :tankefugl - break; - - case HUD_SOUND_MARINE_NEEDPORTAL: - // tankefugl: 0000407 - if (theAutoHelpEnabled) - { - if(rand() % 2) - theSoundPtr = kMarineNeedPortal1; - else - theSoundPtr = kMarineNeedPortal2; - theSoundLength = 1.8f; - } - // :tankefugl - break; - - case HUD_SOUND_MARINE_GOTOALERT: - // tankefugl: 0000407 - if (theAutoHelpEnabled) - { - theSoundPtr = kMarineGotoAlert; - theSoundLength = 2.2f; - } - // :tankefugl - break; - - case HUD_SOUND_MARINE_COMMANDERIDLE: - // tankefugl: 0000407 - if (theAutoHelpEnabled) - { - if(rand() % 2) - theSoundPtr = kMarineCommanderIdle1; - else - theSoundPtr = kMarineCommanderIdle2; - theSoundLength = 1.5f; - } - // :tankefugl - break; - - case HUD_SOUND_MARINE_ARMORYUPGRADING: - theSoundPtr = kMarineArmoryUpgrading; - theSoundLength = 3.4; - break; - - case HUD_SOUND_ALIEN_ENEMY_APPROACHES: - if(rand() %2) - theSoundPtr = kAlienEnemyApproaches1; - else - theSoundPtr = kAlienEnemyApproaches2; - theSoundLength = 1.6; - break; - - case HUD_SOUND_ALIEN_GAMEOVERMAN: - theSoundPtr = kAlienGameOverMan; - theSoundLength = 2.2f; - break; - - case HUD_SOUND_ALIEN_HIVE_ATTACK: - theSoundPtr = kAlienHiveAttack; - theSoundLength = 1.6f; - break; - case HUD_SOUND_ALIEN_HIVE_COMPLETE: - if(rand() % 2) - theSoundPtr = kAlienHiveComplete1; - else - theSoundPtr = kAlienHiveComplete2; - theSoundLength = 2.1f; - break; - case HUD_SOUND_ALIEN_HIVE_DYING: - if(rand() % 2) - { - theSoundPtr = kAlienHiveDying1; - theSoundLength = 1.7f; - } - else - { - theSoundPtr = kAlienHiveDying2; - theSoundLength = 2.4f; - } - break; - case HUD_SOUND_ALIEN_LIFEFORM_ATTACK: - if(rand() % 2) - theSoundPtr = kAlienLifeformAttack1; - else - theSoundPtr = kAlienLifeformAttack2; - theSoundLength = 1.8f; - break; - case HUD_SOUND_ALIEN_RESOURCES_LOW: - theSoundPtr = kAlienLowResources; - theSoundLength = 1.7f; - break; - case HUD_SOUND_ALIEN_MESS: - theSoundPtr = kAlienMess; - theSoundLength = 2.0f; - break; - case HUD_SOUND_ALIEN_MORE: - if(rand() % 2) - theSoundPtr = kAlienMoreResources1; - else - theSoundPtr = kAlienMoreResources2; - theSoundLength = 1.8f; - break; - case HUD_SOUND_ALIEN_NEED_BUILDERS: - if(rand() % 2) - theSoundPtr = kAlienNeedBuilders1; - else - theSoundPtr = kAlienNeedBuilders2; - theSoundLength = 1.4f; - break; - - case HUD_SOUND_ALIEN_NEED_BETTER: - theSoundPtr = kAlienNeedBetter; - theSoundLength = 2.5f; - break; - - case HUD_SOUND_ALIEN_NOW_DONCE: - theSoundPtr = kAlienNowDonce; - theSoundLength = 2.1f; - break; - - case HUD_SOUND_ALIEN_NEW_TRAIT: - if(rand() % 2) - theSoundPtr = kAlienNewTrait1; - else - theSoundPtr = kAlienNewTrait2; - theSoundLength = 1.5f; - break; - - case HUD_SOUND_ALIEN_POINTS_RECEIVED: - theSoundPtr = kAlienPointsReceivedSound; - theSoundLength = 1.57f; - break; - - case HUD_SOUND_ALIEN_RESOURCES_ATTACK: - if(rand() % 2) - theSoundPtr = kAlienResourceAttack1; - else - theSoundPtr = kAlienResourceAttack2; - theSoundLength = 2.1f; - break; - case HUD_SOUND_ALIEN_STRUCTURE_ATTACK: - if(rand() % 2) - theSoundPtr = kAlienStructureAttack1; - else - theSoundPtr = kAlienStructureAttack2; - theSoundLength = 1.9f; - break; - case HUD_SOUND_ALIEN_UPGRADELOST: - theSoundPtr = kAlienUpgradeLost; - theSoundLength = 1.5f; - break; - - case HUD_SOUND_ORDER_MOVE: - switch(rand() % 4) - { - case 0: - theSoundPtr = kSoundOrderMove1; - theSoundLength = 1.8f; - break; - case 1: - theSoundPtr = kSoundOrderMove2; - theSoundLength = 2.3f; - break; - case 2: - theSoundPtr = kSoundOrderMove3; - theSoundLength = 1.9f; - break; - case 3: - theSoundPtr = kSoundOrderMove4; - theSoundLength = 2.3f; - break; - } - break; - case HUD_SOUND_ORDER_ATTACK: - theSoundPtr = kSoundOrderAttack; - break; - case HUD_SOUND_ORDER_BUILD: - theSoundPtr = kSoundOrderBuild; - break; - case HUD_SOUND_ORDER_WELD: - theSoundPtr = kSoundOrderWeld; - break; - case HUD_SOUND_ORDER_GUARD: - theSoundPtr = kSoundOrderGuard; - break; - case HUD_SOUND_ORDER_GET: - theSoundPtr = kSoundOrderGet; - break; - - case HUD_SOUND_ORDER_COMPLETE: - switch(rand() % 6) - { - case 0: - theSoundPtr = kSoundOrderComplete1; - theSoundLength = 1.6f; - break; - case 1: - theSoundPtr = kSoundOrderComplete2; - theSoundLength = 1.9f; - break; - case 2: - theSoundPtr = kSoundOrderComplete3; - theSoundLength = 1.9f; - break; - case 3: - theSoundPtr = kSoundOrderComplete4; - theSoundLength = 1.4f; - break; - case 4: - theSoundPtr = kSoundOrderComplete5; - theSoundLength = 1.6f; - break; - case 5: - theSoundPtr = kSoundOrderComplete6; - theSoundLength = 1.4f; - break; - } - break; - - case HUD_SOUND_GAMESTART: - if(this->GetIsMarine()) - { - if(rand() % 2) - theSoundPtr = kMarineGameStart1; - else - theSoundPtr = kMarineGameStart2; - theSoundLength = 1.9f; - } - else if(this->GetIsAlien()) - { - if(rand() % 2) - theSoundPtr = kAlienGameStart1; - else - theSoundPtr = kAlienGameStart2; - theSoundLength = 2.2f; - } - break; - - case HUD_SOUND_YOU_WIN: - theSoundPtr = kYouWinSound; - theSoundLength = 6.0f; - break; - case HUD_SOUND_YOU_LOSE: - theSoundPtr = kYouLoseSound; - theSoundLength = 6.0f; - break; - case HUD_SOUND_TOOLTIP: - theSoundPtr = kTooltipSound; - // Tooltip sounds should never stop other sounds - theSoundLength = -1.0f; - theVolume = .6f; - break; - // joev: bug 0000767 - case HUD_SOUND_PLAYERJOIN: - theSoundPtr = kPlayerJoinedSound; - theSoundLength = 3.0f; - theVolume = 1.1; - break; - // :joev - } - - if(theSoundPtr) - { - gEngfuncs.pfnPlaySoundByName(theSoundPtr, theVolume); - if(theSoundLength >= 0.0f) - { - this->mTimeOfNextHudSound = this->mTimeOfCurrentUpdate + theSoundLength; - } - this->mLastHUDSoundPlayed = inSound; - } - } -} - -BIND_MESSAGE(SetTech); -int AvHHud::SetTech(const char* pszName, int iSize, void* pbuf) -{ - AvHTechNode* theTechNode = NULL; - NetMsg_SetTechNode( pbuf, iSize, theTechNode ); - this->mTechNodes.InsertNode(theTechNode); - delete theTechNode; - - return 1; -} - -BIND_MESSAGE(TechSlots); -int AvHHud::TechSlots(const char* pszName, int iSize, void* pbuf) -{ - AvHTechSlots theNewTechSlots; - NetMsg_SetTechSlots( pbuf, iSize, theNewTechSlots ); - - this->mTechSlotManager.AddTechSlots(theNewTechSlots); - - return 1; -} - -BIND_MESSAGE(DebugCSP); -int AvHHud::DebugCSP(const char* pszName, int iSize, void* pbuf) -{ - weapon_data_t weapon_data; - float next_attack; - NetMsg_DebugCSP( pbuf, iSize, weapon_data, next_attack ); - - char theServerInfoString[512]; - sprintf(theServerInfoString, "Server: id: %d, clip: %d, prim attack: %f, idle: %f, next attack: %f", - weapon_data.m_iId, - weapon_data.m_iClip, - weapon_data.m_flNextPrimaryAttack, - weapon_data.m_flTimeWeaponIdle, - weapon_data.m_flNextSecondaryAttack, - next_attack - ); - - vgui::Label* theLabel = NULL; - if(this->GetManager().GetVGUIComponentNamed(kDebugCSPServerLabel, theLabel)) - { - theLabel->setText(theServerInfoString); - } - - return 1; -} - -AvHVisibleBlipList& AvHHud::GetEnemyBlipList() -{ - return this->mEnemyBlips; -} - -AvHEntityHierarchy& AvHHud::GetEntityHierarchy() -{ - return this->mEntityHierarchy; -} - -AvHVisibleBlipList& AvHHud::GetFriendlyBlipList() -{ - return this->mFriendlyBlips; -} - -bool AvHHud::GetMouseOneDown() const -{ - return this->mMouseOneDown; -} - -bool AvHHud::GetMouseTwoDown() const -{ - return this->mMouseTwoDown; -} - -void AvHHud::HideProgressStatus() -{ - if(this->mGenericProgressBar) - { - this->mGenericProgressBar->setVisible(false); - } - - if(this->mAlienProgressBar) - { - this->mAlienProgressBar->setVisible(false); - } -} - -void AvHHud::HideResearchProgressStatus() -{ - if(this->mResearchProgressBar) - { - this->mResearchProgressBar->setVisible(false); - this->mResearchLabel->setVisible(false); - } -} - -void AvHHud::Init(void) -{ - UIHud::Init(); - - HOOK_MESSAGE(EntHier); - HOOK_MESSAGE(Particles); - HOOK_MESSAGE(SoundNames); - HOOK_MESSAGE(PlayHUDNot); - HOOK_MESSAGE(BalanceVar); - HOOK_MESSAGE(GameStatus); - HOOK_MESSAGE(Progress); - HOOK_MESSAGE(Countdown); - HOOK_MESSAGE(MiniMap); - HOOK_MESSAGE(SetOrder); - HOOK_MESSAGE(SetSelect); - HOOK_MESSAGE(SetRequest); - HOOK_MESSAGE(SetupMap); - HOOK_MESSAGE(SetTopDown); - HOOK_MESSAGE(SetTech); - HOOK_MESSAGE(EditPS); - HOOK_MESSAGE(Fog); - HOOK_MESSAGE(ListPS); - HOOK_MESSAGE(SetGmma); - HOOK_MESSAGE(BlipList); - HOOK_MESSAGE(ClScript); - HOOK_MESSAGE(AlienInfo); - HOOK_MESSAGE(DebugCSP); - HOOK_MESSAGE(TechSlots); - // tankefugl: 0000971 - HOOK_MESSAGE(IssueOrder); - // :tankefugl - - HOOK_MESSAGE(ServerVar); - - this->AddCommands(); - - this->mCountDownClock = -1; - - mOverviewMap.Clear(); - - this->mEntityHierarchy.Clear(); - //this->mUpgradeCosts.clear(); - - sPregameGammaTable.InitializeFromVideoState(); - sGameGammaTable.InitializeFromVideoState(); - - int theRC = atexit(AvHHud::ResetGammaAtExit); - _onexit_t theExit = _onexit(AvHHud::ResetGammaAtExitForOnExit); - - signal(SIGILL, AvHHud::ResetGammaAtExit); - signal(SIGFPE, AvHHud::ResetGammaAtExit); - signal(SIGSEGV, AvHHud::ResetGammaAtExit); - signal(SIGTERM, AvHHud::ResetGammaAtExit); - signal(SIGBREAK, AvHHud::ResetGammaAtExit); - signal(SIGABRT, AvHHud::ResetGammaAtExit); - - //memset(this->mAlienUILifeforms, 0, sizeof(HSPRITE)*kNumAlienLifeforms); - this->mAlienUIUpgrades = 0; - this->mAlienUIUpgradeCategories = 0; - this->mOrderSprite = 0; - this->mCursorSprite = 0; - this->mMarineCursor = 0; - this->mAlienCursor = 0; - - this->mMarineUIJetpackSprite = 0; - this->mMembraneSprite = 0; - this->mBackgroundSprite = 0; - - this->mProgressBarEntityIndex = -1; - this->mProgressBarParam = -1; - this->mSelectedNodeResourceCost = -1; - this->mCurrentUseableEnergyLevel = 0; - this->mVisualEnergyLevel = 0.0f; - - this->mFogActive = false; - this->mFogColor.x = this->mFogColor.y = this->mFogColor.z = 0; - this->mFogStart = 0; - this->mFogEnd = 0; - - this->mLocationText = ""; - this->mInfoLocationList.clear(); - - this->mLastHotkeySelectionEvent = MESSAGE_NULL; - this->mUpgrades.clear(); - - this->mLastGhostBuilding = NULL; - this->mReticleInfoText = ""; - this->mSelectingWeaponID = -1; - this->mSelectingNodeID = MESSAGE_NULL; - this->mDesiredGammaSlope = 1; - this->mTimeOfLastHelpText = -1; - this->mCurrentWeaponID = -1; - this->mCurrentWeaponEnabled = false; - this->mCurrentUIMode = MAIN_MODE; - this->mMenuTechSlots = 0; - this->mBlinkingAlertType = 0; - this->mLastUser3 = AVH_USER3_NONE; - this->mLastTeamNumber = TEAM_IND; - this->mLastPlayMode = PLAYMODE_UNDEFINED; - - this->mCrosshairShowCount = 1; - this->mCrosshairSprite = 0; - this->mCrosshairR = 0; - this->mCrosshairG = 0; - this->mCrosshairB = 0; - - this->mDrawCombatUpgradeMenu = false; - - // Initialize viewport - this->mViewport[0] = this->mViewport[1] = this->mViewport[2] = this->mViewport[3] = 0; - - gl_monolights = gEngfuncs.pfnGetCvarPointer("gl_monolights"); - gl_overbright = gEngfuncs.pfnGetCvarPointer("gl_overbright"); - gl_clear = gEngfuncs.pfnGetCvarPointer("gl_clear"); - hud_draw = gEngfuncs.pfnGetCvarPointer("hud_draw"); - r_drawviewmodel = gEngfuncs.pfnGetCvarPointer("r_drawviewmodel"); - gl_d3dflip = gEngfuncs.pfnGetCvarPointer("gl_d3dflip"); - s_show = gEngfuncs.pfnGetCvarPointer("s_show"); - lightgamma = gEngfuncs.pfnGetCvarPointer("lightgamma"); - r_detailtextures = gEngfuncs.pfnGetCvarPointer("r_detailtextures"); -} - -// This gives the HUD a chance to draw after the VGUI. A component must allow itself to be hooked by calling this function -// at the end of it's paint() routine. This is done so the mouse cursor can draw on top of the other components. -void AvHHud::ComponentJustPainted(Panel* inPanel) -{ -// if(this->GetInTopDownMode()) -// { -// AvHTeamHierarchy* theComponent = dynamic_cast(inPanel); -// if(theComponent) -// { -// int theBasePosX; -// int theBasePosY; -// theComponent->getPos(theBasePosX, theBasePosY); -// this->DrawMouseCursor(theBasePosX, theBasePosY); -// } -// } -// else -// { -// PieMenu* theComponent = dynamic_cast(inPanel); -// if(theComponent) -// { -// int theBasePosX; -// int theBasePosY; -// theComponent->getPos(theBasePosX, theBasePosY); -// this->DrawMouseCursor(theBasePosX, theBasePosY); -// } -// } - - - DummyPanel* theComponent = dynamic_cast(inPanel); - if(theComponent) - { - int theBasePosX; - int theBasePosY; - theComponent->getPos(theBasePosX, theBasePosY); - this->DrawMouseCursor(theBasePosX, theBasePosY); - } - -} - -bool AvHHud::SetCursor(AvHOrderType inOrderType) -{ - bool theSuccess = false; - - if(!this->GetIsAlien()) - { - this->mCursorSprite = this->mMarineCursor; - - if((inOrderType >= 0) && (inOrderType < ORDERTYPE_MAX)) - { - this->mCurrentCursorFrame = (int)(inOrderType); - theSuccess = true; - } - // Change cursor when over a valid choice - if(this->mSelectingNodeID > 0) - { - this->mCurrentCursorFrame = 1; - theSuccess = true; - } - } - else - { - this->mCursorSprite = this->mAlienCursor; - this->mCurrentCursorFrame = 0; - - if(this->mSelectingNodeID > 0) - { - // Set cursor to lifeform to evolve to - switch(this->mSelectingNodeID) - { - case ALIEN_LIFEFORM_ONE: - case ALIEN_LIFEFORM_TWO: - case ALIEN_LIFEFORM_THREE: - case ALIEN_LIFEFORM_FOUR: - case ALIEN_LIFEFORM_FIVE: - this->mCurrentCursorFrame = 2 + (int)this->mSelectingNodeID - (int)ALIEN_LIFEFORM_ONE; - break; - - default: - this->mCurrentCursorFrame = 1; - break; - } - } - - theSuccess = true; - } - - // Scheme::SchemeCursor theSchemeCursor = Scheme::SchemeCursor::scu_arrow; - // //Scheme::SchemeCursor theSchemeCursor = Scheme::SchemeCursor::scu_no; - // - // switch(inOrderType) - // { - //// case ORDERTYPE_UNDEFINED: - //// theSchemeCursor = Scheme::SchemeCursor::scu_no; - //// theSuccess = true; - //// break; - // - // //case ORDERTYPEL_MOVE: - // - // case ORDERTYPET_ATTACK: - // case ORDERTYPET_GUARD: - // case ORDERTYPET_WELD: - // case ORDERTYPET_BUILD: - // case ORDERTYPEL_USE: - // case ORDERTYPET_GET: - // theSchemeCursor = Scheme::SchemeCursor::scu_hand; - // theSuccess = true; - // break; - // } - // - // App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(theSchemeCursor)); - - return theSuccess; -} - -void AvHHud::GetCursor(HSPRITE& outSprite, int& outFrame) -{ - - if (g_iUser1 == 0) - { - outSprite = mCursorSprite; - outFrame = mCurrentCursorFrame; - } - else - { - // Always use the marine cursor in spectator mode. - outSprite = mMarineCursor; - outFrame = 0; - } - -} - -void AvHHud::SetHelpMessage(const string& inHelpText, bool inForce, float inNormX, float inNormY) -{ - if(inForce || gEngfuncs.pfnGetCvarFloat(kvAutoHelp)) - { - float theReticleX = kHelpMessageLeftEdgeInset; - float theReticleY = kHelpMessageTopEdgeInset - 0.03f; // + 0.15f; - bool theCentered = false; - - if(this->GetInTopDownMode()) - { - int theMouseX, theMouseY; - this->GetMousePos(theMouseX, theMouseY); - theReticleX = theMouseX/(float)ScreenWidth(); - theReticleY = theMouseY/(float)ScreenHeight(); - - // Move text up a bit so it doesn't obscure - theReticleY -= .1f; - - theCentered = true; - } - // Alien HUD forces this to be inset a bit - else - { - if(this->GetIsAlien()) - { - theReticleX = kHelpMessageAlienLeftedgeInset; - theReticleY = kHelpMessageAlienTopEdgeInset - 0.15f; - } - - if(inNormX != -1) - { - theReticleX = inNormX; - } - if(inNormY != -1) - { - theReticleY = inNormY; - } - } - - this->mHelpMessage.SetText(inHelpText); - - this->mHelpMessage.SetNormalizedScreenX(theReticleX); - this->mHelpMessage.SetNormalizedScreenY(theReticleY); - this->mHelpMessage.SetCentered(theCentered); - this->mHelpMessage.SetNormalizedMaxWidth(kReticleMaxWidth); - //this->mHelpMessage.SetIgnoreFadeForLifetime(true); - - float theTimePassed = (this->mTimeOfCurrentUpdate - this->mTimeOfLastUpdate); - this->mHelpMessage.FadeText(theTimePassed, false); - - // Set color - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, true, false); - this->mHelpMessage.SetRGB(theR, theG, theB); - } -} - -void AvHHud::SetActionButtonHelpMessage(const string& inHelpText) -{ - this->mTopDownActionButtonHelp.SetText(inHelpText); - - const float kNormX = .73f; - this->mTopDownActionButtonHelp.SetNormalizedScreenX(kNormX); - - // Treat this as the bottom of the tooltip. Scale the tooltip so it's bottom is here. - const float kNormY = .68f; - int theHeight = this->mTopDownActionButtonHelp.GetScreenHeight(); - float theNormalizedHeight = (float)theHeight/ScreenHeight(); - this->mTopDownActionButtonHelp.SetNormalizedScreenY(kNormY - theNormalizedHeight); - - this->mTopDownActionButtonHelp.SetBackgroundA(128); - - this->mTopDownActionButtonHelp.SetCentered(false); - this->mTopDownActionButtonHelp.SetNormalizedMaxWidth(1.0f - kNormX - .01f); - //this->mTopDownActionButtonHelp.SetIgnoreFadeForLifetime(true); - - float theTimePassed = (this->mTimeOfCurrentUpdate - this->mTimeOfLastUpdate); - this->mTopDownActionButtonHelp.FadeText(theTimePassed, false); - - // Set color - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, true, false); - this->mTopDownActionButtonHelp.SetRGB(theR, theG, theB); -} - -void AvHHud::SetReticleMessage(const string& inHelpText) -{ - float theReticleX; - float theReticleY; - bool theIsCentered; - this->GetReticleTextDrawingInfo(theReticleX, theReticleY, theIsCentered); - - this->mReticleMessage.SetText(inHelpText); - - this->mReticleMessage.SetNormalizedScreenX(theReticleX); - this->mReticleMessage.SetNormalizedScreenY(theReticleY); - this->mReticleMessage.SetCentered(theIsCentered); - this->mReticleMessage.SetNormalizedMaxWidth(kReticleMaxWidth); - - // Need instant fade-in and slow fade down for player names and info - this->mReticleMessage.SetFadeDownSpeed(-100); - this->mReticleMessage.SetFadeUpSpeed(10000); - //this->mReticleMessage.SetIgnoreFadeForLifetime(true); - - float theTimePassed = (this->mTimeOfCurrentUpdate - this->mTimeOfLastUpdate); - this->mReticleMessage.FadeText(theTimePassed, false); -} - -void AvHHud::SetCurrentUseableEnergyLevel(float inEnergyLevel) -{ - this->mCurrentUseableEnergyLevel = inEnergyLevel; -} - -const AvHMapExtents& AvHHud::GetMapExtents() -{ - return this->mMapExtents; -} - -void AvHHud::InitCommanderMode() -{ - Panel* thePanel = NULL; - - if(this->GetManager().GetVGUIComponentNamed(kLeaveCommanderButton, thePanel)) - { - thePanel->addInputSignal(&gCommanderHandler); - } - - if(this->GetManager().GetVGUIComponentNamed(kHierarchy, thePanel)) - { - thePanel->addInputSignal(&gCommanderHandler); - } - - if(this->GetManager().GetVGUIComponentNamed(kActionButtonsComponents, thePanel)) - { - thePanel->addInputSignal(&gCommanderHandler); - } - - if(this->GetManager().GetVGUIComponentNamed(kSelectAllImpulsePanel, thePanel)) - { - thePanel->addInputSignal(&gCommanderHandler); - } - - // Add handler for all pending request buttons - for(int i = 0; i < MESSAGE_LAST; i++) - { - AvHMessageID theMessageID = AvHMessageID(i); - - char theComponentName[256]; - sprintf(theComponentName, kPendingImpulseSpecifier, (int)theMessageID); - - AvHTechImpulsePanel* theTechImpulsePanel = NULL; - if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) - { - theTechImpulsePanel->addInputSignal(&gCommanderHandler); - } - } - - //this->GetManager().GetVGUIComponentNamed(kScroller, thePanel); - //if(thePanel) - //{ - // thePanel->addInputSignal(&gScrollHandler); - //} - - // Get input from every control - this->GetManager().AddInputSignal(&gScrollHandler); - - // TODO: Add others here -} - -// Read in base state from stream (called by Demo_ReadBuffer) -int AvHHud::InitializeDemoPlayback(int inSize, unsigned char* inBuffer) -{ - // Read in base state - int theBytesRead = 0; - - // Read in num upgrades - int theNumUpgrades = 0; - //this->mUpgradeCosts.clear(); - LoadData(&theNumUpgrades, inBuffer, sizeof(int), theBytesRead); - - for(int i = 0; i < theNumUpgrades; i++) - { - // Read in upgrades (for backwards-compatibility) - int theFirst = 0; - LoadData(&theFirst, inBuffer, sizeof(int), theBytesRead); - - int theSecond = 0; - LoadData(&theSecond, inBuffer, sizeof(int), theBytesRead); - } - - // Read in gamma - LoadData(&this->mDesiredGammaSlope, inBuffer, sizeof(this->mDesiredGammaSlope), theBytesRead); - - if (!mSteamUIActive) - { - this->SetGamma(this->mDesiredGammaSlope); - } - - // Read in resources - LoadData(&this->mResources, inBuffer, sizeof(this->mResources), theBytesRead); - - // Read in commander (TODO: REMOVE) - int theCommander; - LoadData(&theCommander, inBuffer, sizeof(theCommander), theBytesRead); - - // Read in number of hive infos - this->mHiveInfoList.clear(); - - int theNumHiveInfos = 0; - LoadData(&theNumHiveInfos, inBuffer, sizeof(int), theBytesRead); - - // For each one, add a new hive info - for(i = 0; i < theNumHiveInfos; i++) - { - AvHHiveInfo theHiveInfo; - LoadData(&theHiveInfo, inBuffer, sizeof(AvHHiveInfo), theBytesRead); - this->mHiveInfoList.push_back(theHiveInfo); - } - - // Load and set current pie menu control - int thePieMenuControlLength = 0; - LoadData(&thePieMenuControlLength, inBuffer, sizeof(int), theBytesRead); - - char thePieMenuControl[256]; - memset(thePieMenuControl, 0, 256); - LoadData(thePieMenuControl, inBuffer, thePieMenuControlLength, theBytesRead); - this->mPieMenuControl = string(thePieMenuControl); - - // Read in selected units size - this->mSelected.clear(); - int theNumSelected = 0; - LoadData(&theNumSelected, inBuffer, sizeof(theNumSelected), theBytesRead); - - for(i = 0; i < theNumSelected; i++) - { - // Read in selected units - EntityInfo theSelectedEntity = 0; - LoadData(&theSelectedEntity, inBuffer, sizeof(theSelectedEntity), theBytesRead); - } - - ASSERT((theBytesRead + (int)sizeof(int)) == inSize); - - // Clear existing particle system templates - gParticleTemplateList.Clear(); - AvHParticleSystemManager::Instance()->Reset(); - - // Clear weapon info - gWR.Reset(); - - return theBytesRead; -} - -int AvHHud::InitializeDemoPlayback2(int inSize, unsigned char* inBuffer) -{ - // Read in base state 2 - int theBytesRead = 0; - - LOAD_DATA(this->mTimeOfLastUpdate); - LOAD_DATA(this->mTimeOfNextHudSound); - LOAD_DATA(this->mTimeOfCurrentUpdate); - LOAD_DATA(this->mCountDownClock); - LOAD_DATA(this->mLastTickPlayed); - LOAD_DATA(this->mNumTicksToPlay); - LoadStringData(this->mMapName, inBuffer, theBytesRead); - - float theMinViewHeight; - LOAD_DATA(theMinViewHeight); - this->mMapExtents.SetMinViewHeight(theMinViewHeight); - - float theMaxViewHeight; - LOAD_DATA(theMaxViewHeight); - this->mMapExtents.SetMaxViewHeight(theMaxViewHeight); - - float theMinMapX; - LOAD_DATA(theMinMapX); - this->mMapExtents.SetMinMapX(theMinMapX); - - float theMaxMapX; - LOAD_DATA(theMaxMapX); - this->mMapExtents.SetMaxMapX(theMaxMapX); - - float theMinMapY; - LOAD_DATA(theMinMapY); - this->mMapExtents.SetMinMapY(theMinMapY); - - float theMaxMapY; - LOAD_DATA(theMaxMapY); - this->mMapExtents.SetMaxMapY(theMaxMapY); - - bool theDrawMapBG; - LOAD_DATA(theDrawMapBG); - this->mMapExtents.SetDrawMapBG(theDrawMapBG); - - // Clear then load sound names - int theSoundNameListSize; - LOAD_DATA(theSoundNameListSize); - this->mSoundNameList.clear(); - - for(int i = 0; i < theSoundNameListSize; i++) - { - string theCurrentSoundName; - LoadStringData(theCurrentSoundName, inBuffer, theBytesRead); - this->mSoundNameList.push_back(theCurrentSoundName); - } - - ASSERT((theBytesRead + (int)sizeof(int)) == inSize); - - return theBytesRead; -} - -// Write out base HUD data to stream -void AvHHud::InitializeDemoRecording() -{ - // Figure out total size of buffer needed - - // Write number of upgrades, then each upgrade - // No longer done, but need to add in upgrades for backwards compatibility - int theUpgrades = 0; - int theUpgradesSize = sizeof(theUpgrades); - - // Gamma, resources - int theGammaSize = sizeof(this->mDesiredGammaSlope); - int theResourcesSizes = sizeof(this->mResources); - - // Save commander index (TODO: REMOVE) - int theCommanderIndex = this->GetCommanderIndex(); - int theCommanderSize = sizeof(theCommanderIndex); - - int theNumHiveInfoRecords = (int)this->mHiveInfoList.size(); - int theHiveInfoSize = sizeof(int) + theNumHiveInfoRecords*sizeof(AvHHiveInfo); - - string thePieMenuControl = gPieMenuHandler.GetPieMenuControl(); - int theCurrentPieMenuControlSize = sizeof(int) + (int)thePieMenuControl.size(); - - int theSelectedSize = sizeof(int) + (int)this->mSelected.size()*sizeof(EntityInfo); - - int theTotalSize = theUpgradesSize + theGammaSize + theResourcesSizes + theCommanderSize + theHiveInfoSize + theCurrentPieMenuControlSize + theSelectedSize; - - // New a char array of this size - int theCounter = 0; - - unsigned char* theCharArray = new unsigned char[theTotalSize]; - if(theCharArray) - { - // Write out number of upgrades (for backwards-compatibility) - int theNumUpgradeCosts = 0; - SaveData(theCharArray, &theNumUpgradeCosts, sizeof(theNumUpgradeCosts), theCounter); - - // Write out gamma - SaveData(theCharArray, &this->mDesiredGammaSlope, theGammaSize, theCounter); - SaveData(theCharArray, &this->mResources, theResourcesSizes, theCounter); - SaveData(theCharArray, &theCommanderIndex, theCommanderSize, theCounter); - - // Write out num hive info records - SaveData(theCharArray, &theNumHiveInfoRecords, sizeof(int), theCounter); - for(HiveInfoListType::iterator theHiveInfoIter = this->mHiveInfoList.begin(); theHiveInfoIter != this->mHiveInfoList.end(); theHiveInfoIter++) - { - SaveData(theCharArray, &(*theHiveInfoIter), sizeof(AvHHiveInfo), theCounter); - } - - // Save length of pie menu control name - int thePieMenuControlNameLength = (int)thePieMenuControl.size(); - SaveData(theCharArray, &thePieMenuControlNameLength, sizeof(int), theCounter); - SaveData(theCharArray, thePieMenuControl.c_str(), thePieMenuControlNameLength, theCounter); - - // Save out size of selected - int theNumSelected = (int)this->mSelected.size(); - SaveData(theCharArray, &theNumSelected, sizeof(theNumSelected), theCounter); - - for(EntityListType::const_iterator theSelectedIter = this->mSelected.begin(); theSelectedIter != this->mSelected.end(); theSelectedIter++) - { - EntityInfo theCurrentInfo = *theSelectedIter; - SaveData(theCharArray, &theCurrentInfo, sizeof(EntityInfo), theCounter); - } - - ASSERT(theCounter == theTotalSize); - - // Write it out - Demo_WriteBuffer(TYPE_BASESTATE, theTotalSize, theCharArray); - - // Delete char array - delete [] theCharArray; - theCharArray = NULL; - - // Save out particle templates - gParticleTemplateList.InitializeDemoRecording(); - } - - theTotalSize = theCounter = 0; - theTotalSize += sizeof(this->mTimeOfLastUpdate); - theTotalSize += sizeof(this->mTimeOfNextHudSound); - theTotalSize += sizeof(this->mTimeOfCurrentUpdate); - theTotalSize += sizeof(this->mCountDownClock); - theTotalSize += sizeof(this->mLastTickPlayed); - theTotalSize += sizeof(this->mNumTicksToPlay); - theTotalSize += GetDataSize(this->mMapName); - theTotalSize += sizeof(this->mMapExtents.GetMinViewHeight()); - theTotalSize += sizeof(this->mMapExtents.GetMaxViewHeight()); - theTotalSize += sizeof(this->mMapExtents.GetMinMapX()); - theTotalSize += sizeof(this->mMapExtents.GetMaxMapX()); - theTotalSize += sizeof(this->mMapExtents.GetMinMapY()); - theTotalSize += sizeof(this->mMapExtents.GetMaxMapY()); - theTotalSize += sizeof(this->mMapExtents.GetDrawMapBG()); - - // Save sound names - int theSoundNameListSize = (int)this->mSoundNameList.size(); - theTotalSize += sizeof(theSoundNameListSize); - - for(int i = 0; i < theSoundNameListSize; i++) - { - string theCurrentSoundName = this->mSoundNameList[i]; - theTotalSize += GetDataSize(theCurrentSoundName); - } - - theCharArray = new unsigned char[theTotalSize]; - if(theCharArray) - { - SAVE_DATA(this->mTimeOfLastUpdate); - SAVE_DATA(this->mTimeOfNextHudSound); - SAVE_DATA(this->mTimeOfCurrentUpdate); - SAVE_DATA(this->mCountDownClock); - SAVE_DATA(this->mLastTickPlayed); - SAVE_DATA(this->mNumTicksToPlay); - SaveStringData(theCharArray, this->mMapName, theCounter); - - float theMinViewHeight = this->mMapExtents.GetMinViewHeight(); - SAVE_DATA(theMinViewHeight); - float theMaxViewHeight = this->mMapExtents.GetMaxViewHeight(); - SAVE_DATA(theMaxViewHeight); - float theMinMapX = this->mMapExtents.GetMinMapX(); - SAVE_DATA(theMinMapX); - float theMaxMapX = this->mMapExtents.GetMaxMapX(); - SAVE_DATA(theMaxMapX); - float theMinMapY = this->mMapExtents.GetMinMapY(); - SAVE_DATA(theMinMapY); - float theMaxMapY = this->mMapExtents.GetMaxMapY(); - SAVE_DATA(theMaxMapY); - bool theDrawMapBG = this->mMapExtents.GetDrawMapBG(); - SAVE_DATA(theDrawMapBG); - - SAVE_DATA(theSoundNameListSize); - for(int i = 0; i < theSoundNameListSize; i++) - { - string theCurrentSoundName = this->mSoundNameList[i]; - SaveStringData(theCharArray, theCurrentSoundName, theCounter); - } - - // TODO: Save out locations? - - ASSERT(theCounter == theTotalSize); - - // Write out TYPE_BASESTATE2 chunk separately for backwards-compatibility - Demo_WriteBuffer(TYPE_BASESTATE2, theTotalSize, theCharArray); - - delete [] theCharArray; - theCharArray = NULL; - } - - ScorePanel_InitializeDemoRecording(); - - // Write out weapon info - for(i = 0; i < MAX_WEAPONS; i++) - { - WEAPON* theWeapon = gWR.GetWeapon(i); - if( theWeapon ) - { - theTotalSize = sizeof(theWeapon->szName) + sizeof(theWeapon->iAmmoType) + sizeof(theWeapon->iAmmo2Type) + sizeof(theWeapon->iMax1) + sizeof(theWeapon->iMax2) + sizeof(theWeapon->iSlot) + sizeof(theWeapon->iSlotPos) + sizeof(theWeapon->iFlags) + sizeof(theWeapon->iId) + sizeof(theWeapon->iClip) + sizeof(theWeapon->iCount);// + sizeof(int); // last one is for ammo - theCharArray = new unsigned char[theTotalSize]; - - int theWeaponCoreDataLength = theTotalSize;// - sizeof(int); - memcpy(theCharArray, theWeapon, theWeaponCoreDataLength); // Everything but ammo - //int theAmmo = gWR.GetAmmo(theWeapon->iId); - //memcpy(theCharArray + theWeaponCoreDataLength, &theAmmo, sizeof(int)); - - Demo_WriteBuffer(TYPE_WEAPONINFO, theTotalSize, (unsigned char*)theWeapon); - - delete [] theCharArray; - theCharArray = NULL; - } - } -} - -int AvHHud::InitializeWeaponInfoPlayback(int inSize, unsigned char* inBuffer) -{ - // Make sure weapons are cleared out first - WEAPON theWeapon; - memset(&theWeapon, 0, sizeof(theWeapon)); - - int theWeaponCoreDataLength = inSize;// - sizeof(int); - memcpy(&theWeapon, inBuffer, theWeaponCoreDataLength); - - if(theWeapon.iId) - { - gWR.AddWeapon( &theWeapon ); - //int theAmmo = 0; - //memcpy(&theAmmo, inBuffer + theWeaponCoreDataLength, sizeof(int)); - //gWR.SetAmmo(theWeapon.iId, theAmmo); - } - - return inSize; -} - -void AvHHud::InitMenu(const string& inMenuName) -{ - PieMenu* theMenu = NULL; - if(this->GetManager().GetVGUIComponentNamed(inMenuName, theMenu)) - { - theMenu->AddInputSignalForNodes(&gPieMenuHandler); - //outMenu->DisableNodesGreaterThanCost(this->mResources); - this->GetManager().HideComponent(inMenuName); - } -} - -void AvHHud::PostUIInit(void) -{ - this->InitMenu(kSoldierMenu); - this->InitMenu(kSoldierCombatMenu); - this->InitMenu(kAlienMenu); - this->InitMenu(kAlienCombatMenu); - - this->InitCommanderMode(); - - this->GetManager().GetVGUIComponentNamed(kCommanderResourceLabel, this->mCommanderResourceLabel); - this->GetManager().GetVGUIComponentNamed(kGenericProgress, this->mGenericProgressBar); - this->GetManager().GetVGUIComponentNamed(kResearchProgress, this->mResearchProgressBar); - this->GetManager().GetVGUIComponentNamed(kAlienProgress, this->mAlienProgressBar); - this->GetManager().GetVGUIComponentNamed(kSelectionBox, this->mSelectionBox); - this->GetManager().GetVGUIComponentNamed(kResearchingLabel, this->mResearchLabel); - - // Init particle editor - gParticleEditorHandler.Setup(); - - if(this->GetManager().GetVGUIComponentNamed(kHierarchy, this->mHierarchy)) - { - this->mHierarchy->setEnabled(false); - } - if(this->GetManager().GetVGUIComponentNamed(kShowMapHierarchy, this->mShowMapHierarchy)) - { - this->mShowMapHierarchy->setEnabled(false); - } - - // Don't turn on gamma by default, it is annoying for testing - //this->SetGamma(); -} - -AvHMessageID AvHHud::GetGhostBuilding() const -{ - return this->mGhostBuilding; -} - -void AvHHud::SetGhostBuildingMode(AvHMessageID inGhostBuilding) -{ - this->mGhostBuilding = inGhostBuilding; - this->mCreatedGhost = false; -} - -void AvHHud::SetClientDebugCSP(weapon_data_t* inWeaponData, float inNextPlayerAttack) -{ - char theClientInfoString[512]; - sprintf(theClientInfoString, "Client: id: %d, clip: %d, prim attack: %f, idle: %f, next attack: %f", inWeaponData->m_iId, inWeaponData->m_iClip, inWeaponData->m_flNextPrimaryAttack, inWeaponData->m_flTimeWeaponIdle, inNextPlayerAttack); - - vgui::Label* theLabel = NULL; - if(this->GetManager().GetVGUIComponentNamed(kDebugCSPClientLabel, theLabel)) - { - theLabel->setText(theClientInfoString); - } -} - -void AvHHud::SetCurrentWeaponData(int inCurrentWeaponID, bool inEnabled) -{ - this->mCurrentWeaponID = inCurrentWeaponID; - this->mCurrentWeaponEnabled = inEnabled; -} - -int AvHHud::GetCurrentWeaponID(void) -{ - return this->mCurrentWeaponID; -} - -void AvHHud::SetAlienAbility(AvHMessageID inAlienAbility) -{ - this->mAlienAbility = inAlienAbility; -} - -void AvHHud::SetProgressStatus(float inPercentage) -{ - if(this->mGenericProgressBar) - { - this->mGenericProgressBar->setVisible(false); - } - - if(this->mAlienProgressBar) - { - this->mAlienProgressBar->setVisible(false); - } - - ProgressBar* theProgressBar = this->mGenericProgressBar; - if(this->GetIsAlien()) - { - theProgressBar = this->mAlienProgressBar; - } - - if(theProgressBar) - { - theProgressBar->setVisible(true); - - int theNumSegments = theProgressBar->getSegmentCount(); - int theSegment = inPercentage*theNumSegments; - theProgressBar->setProgress(theSegment); - } -} - -void AvHHud::SetReinforcements(int inReinforcements) -{ - Label* theLabel = NULL; - if(this->GetManager().GetVGUIComponentNamed(kReinforcementsLabel, theLabel)) - { - string thePrefix; - if(LocalizeString(kReinforcementsText, thePrefix)) - { - string theText = thePrefix + string(" ") + MakeStringFromInt(inReinforcements); - theLabel->setText(theText.c_str()); - } - } -} - -void AvHHud::SetResearchProgressStatus(float inPercentage) -{ - if(this->mResearchProgressBar) - { - this->mResearchProgressBar->setVisible(true); - - int theNumSegments = this->mResearchProgressBar->getSegmentCount(); - int theSegment = inPercentage*theNumSegments; - this->mResearchProgressBar->setProgress(theSegment); - - this->mResearchLabel->setVisible(true); - - // Center research label - int theX, theY; - this->mResearchLabel->getPos(theX, theY); - int theTextWidth, theTextHeight; - this->mResearchLabel->getTextSize(theTextWidth, theTextHeight); - this->mResearchLabel->setPos(ScreenWidth()/2 - theTextWidth/2, theY); - } -} - -void AvHHud::UpdateDemoRecordPlayback() -{ - // If the mouse is visible, allow it to work with demos - if(gEngfuncs.pDemoAPI->IsRecording()) - { - // Write out first frame if needed - if(!this->mRecordingLastFrame) - { - this->InitializeDemoRecording(); - this->mRecordingLastFrame = true; - } - - // Write view origin (and angles?) - //Demo_WriteVector(TYPE_VIEWANGLES, v_angles); - //Demo_WriteVector(TYPE_VIEWORIGIN, v_origin); -// Demo_WriteByte(TYPE_MOUSEVIS, g_iVisibleMouse); -// -// if(g_iVisibleMouse) -// { -// int theMouseScreenX, theMouseScreenY; -// -// IN_GetMousePos(&theMouseScreenX, &theMouseScreenY); -// -// //theMouseScreenX += ScreenWidth/2; -// //theMouseScreenY += ScreenHeight/2; -// -// // Write mouse position -// float theNormX = (float)theMouseScreenX/ScreenWidth; -// float theNormY = (float)theMouseScreenY/ScreenHeight; -// -// Demo_WriteFloat(TYPE_MOUSEX, theNormX); -// Demo_WriteFloat(TYPE_MOUSEY, theNormY); -// -// //char theBuffer[256]; -// //sprintf(theBuffer, "Saving mouse coords %f %f\n", theNormX, theNormY); -// //CenterPrint(theBuffer); -// } - } - else if(gEngfuncs.pDemoAPI->IsPlayingback()) - { - //char theBuffer[256]; - //sprintf(theBuffer, "Restoring mouse coords %f %f\n", gNormMouseX, gNormMouseY); - //CenterPrint(theBuffer); - - //int theCurrentMouseX = gNormMouseX*ScreenWidth; - //int theCurrentMouseY = gNormMouseY*ScreenHeight; - // - ////App::getInstance()->setCursorPos(theCurrentMouseX, theCurrentMouseY); - // - //SetCursorPos(theCurrentMouseX, theCurrentMouseY); - // - //this->mMouseCursorX = theCurrentMouseX; - //this->mMouseCursorY = theCurrentMouseY; - - this->mRecordingLastFrame = false; - } - else - { - this->mRecordingLastFrame = false; - } -} - -void AvHHud::UpdateDataFromVuser4(float inCurrentTime) -{ - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - if(theLocalPlayer) - { - // Fetch data from vuser4 - vec3_t theVUser = theLocalPlayer->curstate.vuser4; - - //int theVUserVar0 = 0; - //int theVUserVar1 = 0; - //int theVUserVar2 = 0; - // - //memcpy(&theVUserVar0, (int*)(&theVUser.x), 4); - //memcpy(&theVUserVar1, (int*)(&theVUser.y), 4); - //memcpy(&theVUserVar2, (int*)(&theVUser.z), 4); - - // Fetch new resource level - //theVUserVar2;// & 0x0000FFFF; - - if(this->GetIsCombatMode()) - { - this->mExperience = (int)ceil(theVUser.z/kNumericNetworkConstant); - this->mExperienceLevel = AvHPlayerUpgrade::GetPlayerLevel(this->mExperience); - - const float kShowMenuInterval = 5.0f; - - // If we are at a level greater then we've drawn, set visible again to reparse - if(this->mExperienceLevel > this->mExperienceLevelLastDrawn) - { - this->DisplayCombatUpgradeMenu(true); - this->mTimeOfLastLevelUp = inCurrentTime; - this->mExperienceLevelLastDrawn = this->mExperienceLevel; - } - // else if we have no more levels to spend or if we've been displaying it longer then the time out (+1 because we start at level 1) - else if((this->mExperienceLevel == (this->mExperienceLevelSpent + 1)) || (inCurrentTime > (this->mTimeOfLastLevelUp + kShowMenuInterval))) - { - // stop displaying it - this->DisplayCombatUpgradeMenu(false); - } - } - else - { - int theNewValue = (int)ceil(theVUser.z/kNumericNetworkConstant); - - if(theNewValue != this->mResources) - { - this->mResources = theNewValue; - } - - // Don't smooth resources nicely when switching targets - if((g_iUser1 == OBS_IN_EYE) && (g_iUser2 != this->mUser2OfLastResourceMessage)) - { - this->mVisualResources = this->mResources; - } - } - this->mUser2OfLastResourceMessage = g_iUser2; - } -} - -void AvHHud::UpdateSpectating() -{ - // If we're spectating and the team of our target switched, delete all blips - if((this->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) || (this->GetPlayMode() == PLAYMODE_OBSERVER)) - { - AvHTeamNumber theCurrentTargetTeam = TEAM_IND; - - int theCurrentTarget = g_iUser2;//thePlayer->curstate.iuser2; - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(theCurrentTarget); - if(theEntity) - { - theCurrentTargetTeam = AvHTeamNumber(theEntity->curstate.team); - } - - if((theCurrentTargetTeam != this->mLastTeamSpectated) && (theCurrentTargetTeam != TEAM_IND)) - { - this->mEnemyBlips.Clear(); - this->mFriendlyBlips.Clear(); - this->mLastTeamSpectated = theCurrentTargetTeam; - //CenterPrint("Clearing blips."); - } - } -} - -void AvHHud::UpdateCommonUI() -{ - // Find currently selected node and draw help text if it's been open for a little bit - PieMenu* thePieMenu = NULL; - AvHMessageID theCurrentNodeID = MESSAGE_NULL; - - this->mSelectingNodeID = MESSAGE_NULL; - - if(this->GetManager().GetVGUIComponentNamed(this->mPieMenuControl, thePieMenu)) - { - // tankefugl: Added check to ensure that it is only updated when a menu is visible - if (thePieMenu->getChildCount() > 0) - { - vgui::Panel *rootMenu = thePieMenu->getChild(0); - if (rootMenu && rootMenu->isVisible()) - { - this->UpdateUpgradeCosts(); - } - } - - PieNode* theCurrentNode = NULL; - if(thePieMenu && thePieMenu->GetSelectedNode(theCurrentNode)) - { - theCurrentNodeID = (AvHMessageID)theCurrentNode->GetMessageID(); - - if(theCurrentNodeID > 0) - { - this->mSelectingNodeID = theCurrentNodeID; - } - } - } - - // Clear squad on team change - if(!this->GetIsMarine()) - { - this->mCurrentSquad = 0; - } -} - -#define FORCE_CVAR(a,b) if(a)a->value = b; - -void AvHHud::UpdateExploitPrevention() -{ - //Note: Sometimes some clients will not have these cvars, so be sure to check that they are not null. - FORCE_CVAR(gl_monolights, 0.0f); - FORCE_CVAR(gl_overbright, 0.0f); - FORCE_CVAR(gl_clear, 0.0f); - FORCE_CVAR(hud_draw, 1.0f); - FORCE_CVAR(r_drawviewmodel, 1.0f); - FORCE_CVAR(cl_movespeedkey, AvHMUGetWalkSpeedFactor(this->GetHUDUser3())); - FORCE_CVAR(gl_d3dflip, 1.0f); - FORCE_CVAR(s_show, 0.0f); - FORCE_CVAR(r_detailtextures, 0.0f); - - if(lightgamma && lightgamma->value < 2.0) - lightgamma->value = 2.0f; -} - -void AvHHud::UpdateAlienUI(float inCurrentTime) -{ - // Always hide it by default - this->GetManager().HideComponent(kPieHelpText); - - if(this->GetIsAlien() && this->GetIsAlive()) - { - float theTimePassed = inCurrentTime - this->mTimeOfLastUpdate; -// int theNumFrames = SPR_Frames(this->mAlienUIHiveSprite); -// -// for(AnimatedSpriteListType::iterator theIter = this->mAlienUIHiveList.begin(); theIter != this->mAlienUIHiveList.end(); theIter++) -// { -// const float kAnimationSpeed = 16.0f; -// -// float theCurrentFrame = theIter->mCurrentFrame + theTimePassed*kAnimationSpeed; -// if(theCurrentFrame >= theNumFrames) -// { -// theCurrentFrame -= theNumFrames; -// } -// theIter->mCurrentFrame = theCurrentFrame; -// } - - // Check to see if we have any unspent upgrades. If so, change the pie menu and HUD state to reflect this - this->mNumUpgradesAvailable = 0; - for(int i = 0; i < ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE; i++) - { - this->mCurrentUpgradeCategory[i] = ALIEN_UPGRADE_CATEGORY_INVALID; - } - - int theUpgradeVar = this->GetLocalUpgrades(); - - if(AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_SENSORY, this->mUpgrades, theUpgradeVar)) - { - this->mCurrentUpgradeCategory[this->mNumUpgradesAvailable] = ALIEN_UPGRADE_CATEGORY_SENSORY; - this->mNumUpgradesAvailable += 1; - } - if(AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_MOVEMENT, this->mUpgrades, theUpgradeVar)) - { - this->mCurrentUpgradeCategory[this->mNumUpgradesAvailable] = ALIEN_UPGRADE_CATEGORY_MOVEMENT; - this->mNumUpgradesAvailable += 1; - } - if(AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_OFFENSE, this->mUpgrades, theUpgradeVar)) - { - this->mCurrentUpgradeCategory[this->mNumUpgradesAvailable] = ALIEN_UPGRADE_CATEGORY_OFFENSE; - this->mNumUpgradesAvailable += 1; - } - if(AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_DEFENSE, this->mUpgrades, theUpgradeVar)) - { - this->mCurrentUpgradeCategory[this->mNumUpgradesAvailable] = ALIEN_UPGRADE_CATEGORY_DEFENSE; - this->mNumUpgradesAvailable += 1; - } - } -} - -bool AvHHud::GetCommanderLabelText(std::string& outCommanderName) const -{ - - int theCommander = this->GetCommanderIndex(); - bool theHasCommander = (theCommander > 0) && (theCommander <= gEngfuncs.GetMaxClients()); - - if(!theHasCommander) - { - LocalizeString(kNoCommander, outCommanderName); - return false; // No commander - } - else - { - - std::stringstream theStream; - - string theCommanderText; - LocalizeString(kCommander, theCommanderText); - - theStream << theCommanderText; - theStream << ": "; - - hud_player_info_t thePlayerInfo; - gEngfuncs.pfnGetPlayerInfo(theCommander, &thePlayerInfo); - - if(thePlayerInfo.name) - { - const int kMaxCommNameLen = 8; - theStream << string(thePlayerInfo.name).substr(0, kMaxCommNameLen); - } - - outCommanderName = theStream.str(); - - return true; - - } - -} - -void AvHHud::UpdateMarineUI(float inCurrentTime) -{ - // Find commander label - Label* theCommanderStatusLabel = NULL; - - - if(this->mMapMode == MAP_MODE_NS) - { - - // Add handler for all pending request buttons (this does hotgroups too) - for(int i = 0; i < MESSAGE_LAST; i++) - { - AvHMessageID theMessageID = AvHMessageID(i); - - char theComponentName[256]; - sprintf(theComponentName, kPendingImpulseSpecifier, (int)theMessageID); - - AvHTechImpulsePanel* theTechImpulsePanel = NULL; - if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) - { - bool theVisibility = false; - - // Do we have any requests pending? - if(this->GetIsInTopDownMode()) - { - PendingRequestListType::iterator theIterator; - for(theIterator = this->mPendingRequests.begin(); theIterator != this->mPendingRequests.end(); theIterator++) - { - if(theIterator->first == theMessageID) - { - if(theIterator->second > 0) - { - theVisibility = true; - } - } - } - - switch(theMessageID) - { - case GROUP_SELECT_1: - case GROUP_SELECT_2: - case GROUP_SELECT_3: - case GROUP_SELECT_4: - case GROUP_SELECT_5: - case COMMANDER_SELECTALL: - theVisibility = true; - break; - } - } - - theTechImpulsePanel->setVisible(theVisibility); - } - } - } -} - - -void AvHHud::UpdateCountdown(float inCurrentTime) -{ - if(this->mCountDownClock != -1) - { - if(inCurrentTime - this->mCountDownClock > this->mLastTickPlayed) - { - // Play tick - this->PlayHUDSound(HUD_SOUND_COUNTDOWN); - this->mLastTickPlayed++; - } - - if(this->mLastTickPlayed > this->mNumTicksToPlay) - { - this->AddTooltip(kGameBegun, false); - this->mCountDownClock = -1; - } - } -} - -void AvHHud::UpdateHierarchy() -{ - - // Start the starting point on the map to our local position - this->mOverviewMap.SetMapExtents(this->GetMapName(), this->mMapExtents); - this->mOverviewMap.SetWorldPosition(gPredictedPlayerOrigin[0], gPredictedPlayerOrigin[1]); - - if(this->mHierarchy) - { - this->mHierarchy->SetFullScreen(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER); - } - if(this->mShowMapHierarchy) - { - this->mShowMapHierarchy->SetFullScreen(true); - } - -} - -string AvHHud::GetNameOfLocation(vec3_t inLocation) const -{ - string theLocationName; - - AvHSHUGetNameOfLocation(this->mInfoLocationList, inLocation, theLocationName); - - return theLocationName; -} - - -void AvHHud::UpdateInfoLocation() -{ - if(this->GetIsAlive() && this->GetHUDUser3() != AVH_USER3_ALIEN_EMBRYO) - { - // Don't clear our location, disconcerting to see our location disappear - //this->mLocationText = ""; - - this->mLocationText = this->GetNameOfLocation(gPredictedPlayerOrigin); - } - else - { - this->mLocationText = ""; - } -} - -bool AvHHud::GetIsCombatMode() const -{ - return (this->mMapMode == MAP_MODE_CO); -} - -bool AvHHud::GetIsNSMode() const -{ - return (this->mMapMode == MAP_MODE_NS); -} - -bool AvHHud::GetIsScriptedMode() const -{ - return (this->mMapMode == MAP_MODE_NSC); -} - -bool AvHHud::GetIsMouseInRegion(int inX, int inY, int inWidth, int inHeight) -{ - bool theMouseIsInRegion = false; - - if(this->GetInTopDownMode()) - { - int theMouseX, theMouseY; - this->GetMousePos(theMouseX, theMouseY); - - if((theMouseX >= inX) && (theMouseX <= (inX + inWidth))) - { - if((theMouseY >= inY) && (theMouseY <= (inY + inHeight))) - { - theMouseIsInRegion = true; - } - } - } - - return theMouseIsInRegion; -} - -void AvHHud::TraceEntityID(int& outEntityID) -{ - pmtrace_t tr; - vec3_t up, right, forward; - - // Trace forward to see if we see a player - gEngfuncs.pEventAPI->EV_PushPMStates(); - - // Now add in all of the players. - gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); - - gEngfuncs.pEventAPI->EV_SetTraceHull(2); - - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - int theLocalPlayerIndex = theLocalPlayer->index; - //AngleVectors(theLocalPlayer->curstate.angles, forward, right, up); - - pVector theRealView; - gEngfuncs.pfnAngleVectors(pmove->angles, forward, right, up); - - Vector theStartTrace; - //VectorMA(gPredictedPlayerOrigin, kMaxPlayerHullWidth, forward, theStartTrace); - VectorCopy(gPredictedPlayerOrigin, theStartTrace); - - Vector theEndTrace; - VectorMA(gPredictedPlayerOrigin, 8192, forward, theEndTrace); - bool theDone = false; - - do - { - gEngfuncs.pEventAPI->EV_PlayerTrace(theStartTrace, theEndTrace, PM_NORMAL, -1, &tr); - - // Ignore local player, and ignore the player we're spectating - int theHit = gEngfuncs.pEventAPI->EV_IndexFromTrace(&tr); - if(theHit == theLocalPlayerIndex) - { - VectorMA(tr.endpos, kHitOffsetAmount, forward, theStartTrace); - } - // We hit something - else if(tr.fraction < 1.0) - { - physent_t* pe = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); - if(pe) - { - int thePotentialEntity = pe->info; - //if((thePotentialEntity >= 1) && (thePotentialEntity < gEngfuncs.GetMaxClients())) - //{ - outEntityID = pe->info; - //} - } - theDone = true; - } - } while(!theDone); - - gEngfuncs.pEventAPI->EV_PopPMStates(); -} - -bool AvHHud::GetAlienHelpForMessage(int inMessageID, string& outHelpText, int& outPointCost) const -{ - bool theSuccess = false; - - char theNumber[8]; - sprintf(theNumber, "%d", inMessageID); - string theNumberString(theNumber); - - string theTechHelpText; - string theKey = string(kTechNodeHelpPrefix) + theNumberString; - if(LocalizeString(theKey.c_str(), theTechHelpText)) - { - string thePointString; - if(LocalizeString(kPointsSuffix, thePointString)) - { -/* - // Lookup point cost - for(UpgradeCostListType::const_iterator theIter = this->mPersonalUpgradeCosts.begin(); theIter != this->mPersonalUpgradeCosts.end(); theIter++) - { - if(theIter->first == inMessageID) - { - char theCostSuffix[128]; - sprintf(theCostSuffix, " (%d %s)", theIter->second, thePointString.c_str()); - outPointCost = theIter->second; - theTechHelpText += string(theCostSuffix); - outHelpText = theTechHelpText; - theSuccess = true; - break; - } - } -*/ - } - } - return theSuccess; -} - -bool AvHHud::GetDoesPlayerHaveOrder() const -{ - bool thePlayerHasOrder = false; - - for(OrderListType::const_iterator theIter = this->mOrders.begin(); theIter != this->mOrders.end(); theIter++) - { - // Draw the order if the order is for any plays that are in our draw player list - EntityInfo theReceiverPlayer = theIter->GetReceiver(); - - cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); - if(thePlayer) - { - int thePlayerIndex = thePlayer->index; - if (thePlayerIndex == theReceiverPlayer ) - { - thePlayerHasOrder = true; - break; - } - } - } - - return thePlayerHasOrder; -} - - -bool AvHHud::GetHelpForMessage(int inMessageID, string& outHelpText) const -{ - bool theSuccess = false; - - char theNumber[8]; - sprintf(theNumber, "%d", (int)inMessageID); - string theNumberString(theNumber); - - string theKey = string(kTechNodeLabelPrefix) + theNumberString; - string theTechNodeLabel; - if(LocalizeString(theKey.c_str(), theTechNodeLabel)) - { - theKey = string(kTechNodeHelpPrefix) + theNumberString; - string theTechNodeHelp; - if(LocalizeString(theKey.c_str(), theTechNodeHelp)) - { - outHelpText = /*theTechNodeLabel + " " +*/ theTechNodeLabel; - theSuccess = true; - - int theCost; - bool theResearchable; - float theTime; - this->mTechNodes.GetResearchInfo((AvHMessageID)(inMessageID), theResearchable, theCost, theTime); - - // Add cost - if(theCost > 0) - { - string theCostString; - if(AvHSHUGetDoesTechCostEnergy((AvHMessageID)inMessageID)) - { - LocalizeString(kEnergyPrefix, theCostString); - } - else - { - LocalizeString(kMessageButtonCost, theCostString); - } - - outHelpText += " "; - outHelpText += theCostString; - outHelpText += " "; - outHelpText += MakeStringFromInt(theCost); - - // Draw description below - //outHelpText += "\n"; - //outHelpText += theTechNodeHelp; - } - } - } - - return theSuccess; -} - -void AvHHud::UpdateMusic(float inCurrentTime) -{ - bool theMusicEnabled = false; - - if(this->GetGameStarted()) - { - // If we're entering play mode and music is enabled, allow playing of music - if(this->GetHUDPlayMode() != PLAYMODE_READYROOM && (cl_musicenabled->value == 1.0f) && (cl_musicvolume->value > 0)) - { - theMusicEnabled = true; - } - } - - this->SetMusicAllowed(theMusicEnabled); - - UIHud::UpdateMusic(inCurrentTime); -} - -void AvHHud::UpdatePieMenuControl() -{ - // Set which pie menu to use ("" for none) - bool theScoreBoardIsOpen = false; - - ScorePanel* theScoreBoard = gViewPort->GetScoreBoard(); - if(theScoreBoard && theScoreBoard->isVisible()) - { - theScoreBoardIsOpen = true; - } - - if(theScoreBoardIsOpen) - { - AvHPieMenuHandler::SetPieMenuControl(""); - } - else - { - AvHPieMenuHandler::SetPieMenuControl(this->mPieMenuControl); - } - - // Clear all nodes in case VGUI didn't catch events (seems to happen with lag) - if(!gPieMenuHandler.GetIsPieMenuOpen()) - { - PieMenu* theCurrentPieMenu = gPieMenuHandler.GetActivePieMenu(); - if(theCurrentPieMenu) - { - theCurrentPieMenu->SetFadeState(false); - } - } - - // If we're dead, make sure the popup menu is closed - if(!this->GetIsAlive(false)) - { - gPieMenuHandler.ClosePieMenu(); - } -} - -bool AvHHud::GetEntityInfoString(int inEntityID, string& outEntityInfoString, bool& outIsEnemy) -{ - bool theSuccess = false; - bool theIsEnemy = false; - - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(inEntityID); - if(theEntity) - { - AvHTeamNumber theTeam = this->GetHUDTeam(); - - if((inEntityID >= 1) && (inEntityID <= gEngfuncs.GetMaxClients())) - { - hud_player_info_t thePlayerInfo; - gEngfuncs.pfnGetPlayerInfo(inEntityID, &thePlayerInfo); - - string thePrePendString; - - // Don't show cloaked enemies - if((theTeam != TEAM_IND) && !this->GetInTopDownMode() && ((theEntity->curstate.team == theTeam) || (theEntity->curstate.rendermode == kRenderNormal))) - { - //string thePostPendString; - if((theEntity->curstate.team == theTeam)) - { - //LocalizeString(kFriendText, thePrePendString); - //sprintf(thePostPendString, " (health: %d)", theEntity->curstate.health); - } - else - { - LocalizeString(kEnemyText, thePrePendString); - theIsEnemy = true; - } - - if(thePrePendString != "") - { - outEntityInfoString = thePrePendString + " - "; - } - } - - if(thePlayerInfo.name) - { - outEntityInfoString += thePlayerInfo.name;// + thePostPendString; - - // Get string from status bar and append it - const char* theStatusCStr = this->m_StatusBar.GetStatusString(); - if(strlen(theStatusCStr) > 0) - { - outEntityInfoString += string(theStatusCStr); - } - - outIsEnemy = theIsEnemy; - theSuccess = true; - } - - // sprintf(thePlayerName, "%s (health: %d)", thePlayerInfo.name, thePlayer->curstate.health); - //} - } - else - { - bool theAutoHelpEnabled = gEngfuncs.pfnGetCvarFloat(kvAutoHelp); - - // Return the type of thing it is - AvHUser3 theUser3 = (AvHUser3)(theEntity->curstate.iuser3); - - if(this->GetTranslatedUser3Name(theUser3, outEntityInfoString)) - { - if((theEntity->curstate.team != TEAM_IND) && (theEntity->curstate.team != theTeam)) - { - outIsEnemy = true; - } - - // Upper case first character - if(outEntityInfoString.length() > 0) - { - int theFirstChar = outEntityInfoString[0]; - int theUpperFirstChar = toupper(theFirstChar); - outEntityInfoString[0] = theUpperFirstChar; - } - - // Assume status bar set to health/armor/info data - const char* theStatusCStr = this->m_StatusBar.GetStatusString(); - bool theHasStatusString = (strlen(theStatusCStr) > 0); - if(theHasStatusString) - { - outEntityInfoString += string(theStatusCStr); - } - - if(theAutoHelpEnabled) - { - string theDescription; - bool theIsFriendly = (this->GetHUDTeam() == theEntity->curstate.team); - if(this->GetTranslatedUser3Description(theUser3, theIsFriendly, theDescription)) - { - outEntityInfoString += " - " + theDescription; - } - } - - // Only display help when asked for or important - if(theAutoHelpEnabled || theHasStatusString) - { - theSuccess = true; - } - } - } - } - - return theSuccess; -} - -void AvHHud::UpdateEntityID(float inCurrentTime) -{ - this->mBuildingEffectsEntityList.clear(); - - bool theSetHelpMessage = false; - bool theSetReticleMessage = false; - - //char* theNewPlayerName = NULL; - int theEntityIndex = -1; - - if(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) - { - int theCurrentX, theCurrentY; - this->GetMousePos(theCurrentX, theCurrentY); - - // Don't show help when mouse is over the UI - float theNormX = ((float)theCurrentX)/(ScreenWidth()); - float theNormY = ((float)theCurrentY)/(ScreenHeight()); - if(!this->GetIsRegionBlockedByUI(theNormX, theNormY)) - { - Vector theNormRay; - - CreatePickingRay(theCurrentX, theCurrentY, theNormRay); - - // Look for player and entities under/near the mouse - AvHSHUGetEntityAtRay(this->GetVisualOrigin(), theNormRay, theEntityIndex); - } - } - else if(g_iUser1 != OBS_IN_EYE) - { - // Trace entity id in front of us - this->TraceEntityID(theEntityIndex); - } - - if(this->mSelectingWeaponID != -1) - { - // Look up help, set our current help to it - string theWeaponHelpKey; - sprintf(theWeaponHelpKey, kWeaponHelpText, this->mSelectingWeaponID); - - string theHelpText; - if(LocalizeString(theWeaponHelpKey.c_str(), theHelpText)) - { - // Get damage amount from weapon - ASSERT(this->mSelectingWeaponID >= 0); - ASSERT(this->mSelectingWeaponID < 32); - WEAPON* theWeapon = gWR.GetWeapon(this->mSelectingWeaponID); - if( theWeapon ) - { - int theDamage = theWeapon->iMax2; - - char theHelpTextWithDamage[1024]; - sprintf(theHelpTextWithDamage, theHelpText.c_str(), theDamage); - - this->SetHelpMessage(theHelpTextWithDamage); - theSetHelpMessage = true; - } - } - } - else if(this->mTechHelpText != "") - { - this->SetHelpMessage(this->mTechHelpText, false, .8f, .6f); - theSetHelpMessage = true; - } - else if(this->mSelectingNodeID > 0) - { - char theNumber[8]; - sprintf(theNumber, "%d", this->mSelectingNodeID); - string theNumberString(theNumber); - - string theKey = string(kTechNodeHelpPrefix) + theNumberString; - if(LocalizeString(theKey.c_str(), this->mReticleInfoText)) - { - string theCostString; - if(LocalizeString(kCostMarker, theCostString)) - { - string thePointsString; - if(LocalizeString(kPointsSuffix, thePointsString)) - { - } - - // Don't draw marine sayings, as they are printed on the controls and the aliens are using the titles.txt entries - if(this->GetIsMarine()) - { - switch(this->mSelectingNodeID) - { - case SAYING_1: - case SAYING_2: - case SAYING_3: - case SAYING_4: - case SAYING_5: - case SAYING_6: - case SAYING_7: - case SAYING_8: - case SAYING_9: - this->mReticleInfoText = ""; - break; - } - } - - this->SetHelpMessage(this->mReticleInfoText); - theSetHelpMessage = true; - } - } - } - - if(theEntityIndex > 0) - { - // Don't draw info for cloaked structures - cl_entity_s* theProgressEntity = gEngfuncs.GetEntityByIndex(theEntityIndex); - if(theProgressEntity) - { - if((theProgressEntity->curstate.rendermode != kRenderTransTexture) || (theProgressEntity->curstate.renderamt > kAlienStructureCloakAmount)) - { - if(std::find(this->mBuildingEffectsEntityList.begin(), this->mBuildingEffectsEntityList.end(), theEntityIndex) == this->mBuildingEffectsEntityList.end()) - { - this->mBuildingEffectsEntityList.push_back(theEntityIndex); - } - - bool theEntityIsPlayer = ((theEntityIndex > 0) && (theEntityIndex <= gEngfuncs.GetMaxClients())); - - string theHelpText; - bool theIsEnemy = false; - - if(this->GetEntityInfoString(theEntityIndex, theHelpText, theIsEnemy)) - { - // Set color to red if enemy, otherise to our HUD color - int theR, theG, theB; - if(theIsEnemy) - { - theR = 255; - theG = theB = 0; - } - else - { - this->GetPrimaryHudColor(theR, theG, theB, true, false); - } - - // If we have auto help on, or we're in top down mode, display as help - if(gEngfuncs.pfnGetCvarFloat(kvAutoHelp) || this->GetInTopDownMode()) - { - this->SetHelpMessage(theHelpText); - - this->mHelpMessage.SetRGB(theR, theG, theB); - - theSetHelpMessage = true; - } - // else, display as reticle message - else - { - this->SetReticleMessage(theHelpText); - - this->mReticleMessage.SetRGB(theR, theG, theB); - - theSetReticleMessage = true; - } - } - } - else - { - //char theMessage[128]; - //sprintf(theMessage, "Entity %d cloaked, not drawing circle.", theProgressEntity->curstate.iuser3); - //CenterPrint(theMessage); - } - } - } - - float theTimePassed = (inCurrentTime - this->mTimeOfLastUpdate); - if(!theSetHelpMessage) - { - this->mHelpMessage.FadeText(theTimePassed, true); - } - if(!theSetReticleMessage) - { - this->mReticleMessage.FadeText(theTimePassed, true); - } -} - -int AvHHud::GetMenuTechSlots() const -{ - return this->mMenuTechSlots; -} - -const AvHTechTree& AvHHud::GetTechNodes() const -{ - return this->mTechNodes; -} - -void AvHHud::GetTooltipDrawingInfo(float& outNormX, float& outNormY) const -{ - outNormX = kHelpMessageLeftEdgeInset; - outNormY = kHelpMessageTopEdgeInset; - - if(this->GetPlayMode() == PLAYMODE_READYROOM) - { - outNormY = 1.0f; - } - else if(this->GetInTopDownMode()) - { - outNormY = kHelpMessageCommanderTopEdgeInset; - } - else if(this->GetIsAlien()) - { - outNormY = kHelpMessageAlienTopEdgeInset; - } - - outNormY -= kToolTipVerticalSpacing; -} - -string AvHHud::GetRankTitle(bool inShowUnspentLevels) const -{ - string theText; - - char* theTeamName = this->GetIsMarine() ? "Marine" : "Alien"; - int theCurrentLevel = this->GetHUDExperienceLevel(); - - char theCharArray[512]; - sprintf(theCharArray, kRankText, theTeamName, theCurrentLevel); - LocalizeString(theCharArray, theText); - - // Add unspent levels, if any - int theUnspentLevels = (this->mExperienceLevel - this->mExperienceLevelSpent - 1); - if(inShowUnspentLevels && (theUnspentLevels > 0)) - { - // We can't accurately draw player's unspent levels when we're dead, as we only know our own - if(this->GetIsAlive(false)) - { - string theUnspentLevelText = "(+" + MakeStringFromInt(theUnspentLevels) + ")"; - theText += theUnspentLevelText; - } - } - - return theText; -} - -void AvHHud::GetReticleTextDrawingInfo(float& outNormX, float& outNormY, bool& outCentered) const -{ - outCentered = false; - - if(this->GetInTopDownMode()) - { - int theCurrentX, theCurrentY; - this->GetMousePos(theCurrentX, theCurrentY); - outNormX = theCurrentX/(float)ScreenWidth(); - outNormY = theCurrentY/(float)ScreenHeight(); - - // Move text up a bit so it doesn't obscure - outNormY -= .1f; - - outCentered = true; - } - else - { - if(gEngfuncs.pfnGetCvarFloat(kvCenterEntityID)) - { - outNormX = .5f; - outNormY = .5f; - outCentered = true; - } - else - { - outNormX = kHelpMessageLeftEdgeInset; - outNormY = kHelpMessageTopEdgeInset - kReticleMessageHeight; - - // Alien HUD forces this to be inset a bit - if(this->GetIsAlien()) - { - outNormX = kHelpMessageAlienLeftedgeInset + kReticleMessageAlienLeftInset; - outNormY = kHelpMessageAlienTopEdgeInset - kReticleMessageHeight; - } - } - } -} - -// Assumes that the tooltips aren't centered -void AvHHud::UpdateTooltips(float inCurrentTime) -{ - float theReticleX; - float theReticleY; - this->GetTooltipDrawingInfo(theReticleX, theReticleY); - - float theBottomMostY = theReticleY; - - float theTimePassed = inCurrentTime - this->mTimeOfLastUpdate; - float kMovement = 1.0f*theTimePassed; - - int theBottomScreenY = theBottomMostY*ScreenHeight(); - - // Clear all tooltips on a role change, or on a playmode change - AvHUser3 theCurrentUser3 = this->GetHUDUser3(); - AvHTeamNumber theCurrentTeam = this->GetHUDTeam(); - AvHPlayMode thePlayMode = this->GetPlayMode(); - - if((theCurrentUser3 != this->mLastUser3) || (this->mLastTeamNumber != theCurrentTeam) || ((thePlayMode != this->mLastPlayMode) && (this->mLastPlayMode != PLAYMODE_UNDEFINED)) ) - { - this->mTooltips.clear(); - } - - // Stuff to get reset on a team change - if(this->mLastTeamNumber != theCurrentTeam) - { - this->mExperienceLevelLastDrawn = 1; - } - - // Run through list, dropping them down as far as they can go. Assumes the first one in the list is the bottommost one - for(AvHTooltipListType::iterator theIter = this->mTooltips.begin(); theIter != this->mTooltips.end(); /* no inc */) - { - // Recompute settings, if it hasn't been updated before - theIter->RecomputeIfNeccessary(); - - // Get height of current tool tip - int theTooltipScreenHeight = theIter->GetScreenHeight(); - - // Move it down towards the current limit - float theCurrentMinScreenY = theIter->GetNormalizedScreenY(); - int theCurrentTipScreenBottom = (int)(theCurrentMinScreenY) + theTooltipScreenHeight; - - float theCurrentMaxNormY = (float)(theBottomScreenY - theTooltipScreenHeight)/ScreenHeight(); - float theNewNormY = min(theCurrentMaxNormY, (theCurrentMinScreenY + kMovement)); - - // Now this is a crazy bug! In release mode without the following two statements, theNewNormY isn't > theCurrentMinScreenY when it should be. - // It's as if a little time is needed between the computation of theNewNormY and using theNewNormY for it to work - char theMessage[256]; - sprintf(theMessage, "theNewNormY %f, minScreenY: %f)", theNewNormY, theCurrentMinScreenY); - //CenterPrint(theMessage); - - // If we moved it down - if(theNewNormY > theCurrentMinScreenY) - { - // Fade it in while it's dropping - theIter->FadeText(theTimePassed, false); - } - else - { - if(theIter == this->mTooltips.begin()) - { - // If it's at the bottom of the list, start fading it out - theIter->FadeText(theTimePassed, true); - } - } - - // Set the new position - theIter->SetNormalizedScreenY(theNewNormY); - - // Subtract it's height to the current limit - theBottomScreenY -= theTooltipScreenHeight; - - // Subtract out small spacing amount - theBottomScreenY -= max(1, kToolTipVerticalSpacing*ScreenHeight()); - - // If it's totally faded out, remove it from the list, else process next - int theAlpha = theIter->GetA(); - if(theAlpha == 0) - { - theIter = this->mTooltips.erase(theIter); - } - else - { - theIter++; - } - - //char theTempBuffer[256]; - //sprintf(theTempBuffer, "UpdateTooltips, alpha: %d\n", theAlpha); - //CenterPrint(theTempBuffer); - } - - // Update combat upgrade menu too - if(this->GetIsCombatMode()) - { - const int kScrollDirection = this->mDrawCombatUpgradeMenu ? 1 : -1; - const float kScrollingSpeed = .7f*kScrollDirection; - - float theXDiff = (theTimePassed*kScrollingSpeed); - const float kNormalizedWidth = this->mCombatUpgradeMenu.GetNormalizedMaxWidth(); - - const float kLeftEdgeInset = .02f; - float theNewX = this->mCombatUpgradeMenu.GetNormalizedScreenX() + theXDiff; - theNewX = max(-kNormalizedWidth, theNewX); - theNewX = min(theNewX, kLeftEdgeInset); - - this->mCombatUpgradeMenu.SetNormalizedScreenX(theNewX); - } -} - -void AvHHud::UpdateStructureNotification(float inCurrentTime) -{ - const float kTimeToDisplayIcon = 6.0f; - const int kMaxIcons = 5; - - for(StructureHUDNotificationListType::iterator theIter = this->mStructureNotificationList.begin(); theIter != this->mStructureNotificationList.end(); /* no inc */) - { - if((inCurrentTime > (theIter->mTime + kTimeToDisplayIcon)) || (this->mStructureNotificationList.size() > kMaxIcons)) - { - theIter = this->mStructureNotificationList.erase(theIter); - } - else - { - theIter++; - } - } -} - - -//void AvHHud::FadeText(float inCurrentTime, bool inFadeDown) -//{ -// const float kReticleInfoFadeUpSpeed = 500; -// const float kReticleInfoFadeDownSpeed = -150; -// -// // Fade reticle nicely -// int theFadeSpeed = inFadeDown ? kReticleInfoFadeDownSpeed : kReticleInfoFadeUpSpeed; -// -// float theTimePassed = (inCurrentTime - this->mTimeOfLastUpdate); -// float theNewAlpha = this->mReticleInfoColorA + theTimePassed*theFadeSpeed; -// this->mReticleInfoColorA = max(0, min(255, theNewAlpha)); -//} - -float AvHHud::GetTimeOfLastUpdate() const -{ - return this->mTimeOfLastUpdate; -} - -void AvHHud::UpdateProgressBar() -{ - this->HideProgressStatus(); - - float thePercentage; - if(gMiniMap.GetIsProcessing(&thePercentage)) - { - if(gMiniMap.WriteSpritesIfJustFinished()) - { - this->HideProgressStatus(); - } - else - { - this->SetProgressStatus(thePercentage); - } - } -// else if(this->mProgressBarEntityIndex == -1) -// { -// this->HideGenericProgressStatus(); -// } - else - { - // Look up entity, and set progress according to it's fuser1 - cl_entity_s* theProgressEntity = gEngfuncs.GetEntityByIndex(this->mProgressBarEntityIndex); - if(theProgressEntity) - { - ASSERT(this->mProgressBarParam >= 1); - ASSERT(this->mProgressBarParam <= 4); - - float theProgress = 0.0f; - switch(this->mProgressBarParam) - { - case 1: - theProgress = theProgressEntity->curstate.fuser1; - break; - case 2: - theProgress = theProgressEntity->curstate.fuser2; - break; - case 3: - theProgress = theProgressEntity->curstate.fuser3; - break; - case 4: // NOTE: check delta.lst for fuser4, it isn't propagated currently - theProgress = theProgressEntity->curstate.fuser4; - break; - } - - if((this->GetHUDUser3() == AVH_USER3_ALIEN_EMBRYO) || this->GetIsDigesting()) - { - theProgress = pmove->fuser3; - } - - thePercentage = theProgress/kNormalizationNetworkFactor; - if(thePercentage < 1.0f) - { - this->SetProgressStatus(thePercentage); - } -// else -// { -// this->HideGenericProgressStatus(); -// } - } - else - { - // Look at selection. If selection has research and research isn't done, draw research bar. Else, hide research bar - if(this->mSelected.size() == 1) - { - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(*this->mSelected.begin()); - if(theEntity) - { - this->HideProgressStatus(); - this->HideResearchProgressStatus(); - - bool theIsBuilding, theIsResearching; - float thePercentage; - AvHSHUGetBuildResearchState(theEntity->curstate.iuser3, theEntity->curstate.iuser4, theEntity->curstate.fuser1, theIsBuilding, theIsResearching, thePercentage); - - if(theIsBuilding && (thePercentage > 0.0f) && (thePercentage < 1.0f)) - { - // Turned off progress bar now that we have circular build icons - //this->SetGenericProgressStatus(thePercentage); - } - else if(theIsResearching && (thePercentage > 0) && (thePercentage < 1.0f)) - { - this->SetResearchProgressStatus(thePercentage); - } - } - } - } - } -} - -void AvHHud::GhostBuildingCallback(struct tempent_s* inEntity, float inFrametime, float inCurrentTime) -{ - if(this->mGhostBuilding != MESSAGE_NULL) - { - // Don't let it die - inEntity->die = gClientTimeLastUpdate + 2.0f; - - // Update position to be where mouse is - VectorCopy(this->mGhostWorldLocation, inEntity->entity.origin); - - // Visually indicate whether this is a valid position or not - if(this->mCurrentGhostIsValid) - { - inEntity->entity.curstate.renderfx = kRenderFxGlowShell; - inEntity->entity.curstate.rendercolor.r = 0; - inEntity->entity.curstate.rendercolor.g = 255; - inEntity->entity.curstate.rendercolor.b = 0; - inEntity->entity.curstate.renderamt = kShellRenderAmount; - } - else - { - inEntity->entity.curstate.renderfx = kRenderFxGlowShell; - inEntity->entity.curstate.rendercolor.r = 255; - inEntity->entity.curstate.rendercolor.g = 0; - inEntity->entity.curstate.rendercolor.b = 0; - inEntity->entity.curstate.renderamt = kShellRenderAmount; - } - } - else - { - // Kill it off immediately - inEntity->die = gClientTimeLastUpdate; - } -} - -void AvHHud::UpdateBuildingPlacement() -{ - if(this->mGhostBuilding != MESSAGE_NULL) - { - // Fetch current mouse up/down state from gScrollHandler and store - bool theMouseOneDown = gScrollHandler.GetMouseOneDown() && !gCommanderHandler.GetMouseOneDown(); - bool theMouseTwoDown = gScrollHandler.GetMouseTwoDown(); - - // If we haven't created the temp entity, create it - if(!this->mCreatedGhost) - { - // Create the temporary entity - int theModelIndex; - char* theModelName = AvHSHUGetBuildTechModelName(this->mGhostBuilding); - if(theModelName) - { - if(this->mLastGhostBuilding) - { - this->mLastGhostBuilding->die = -1; - this->mLastGhostBuilding = NULL; - } - - vec3_t theOrigin = this->GetVisualOrigin(); - struct model_s* theModel = gEngfuncs.CL_LoadModel(theModelName, &theModelIndex); - TEMPENTITY* theTempEntity = gEngfuncs.pEfxAPI->CL_TentEntAllocCustom(gPredictedPlayerOrigin, theModel, 0, CLinkGhostBuildingCallback); - if(theTempEntity) - { - theTempEntity->die += 10.0f; - theTempEntity->flags |= FTENT_PERSIST; - // Temp entities interpret baseline origin as velocity. - VectorCopy(vec3_origin, theTempEntity->entity.baseline.origin); - - // Set special properties for some models - if(this->mGhostBuilding == BUILD_SCAN) - { - theTempEntity->entity.curstate.rendermode = kRenderTransAdd; - theTempEntity->entity.curstate.renderamt = 255; - - theTempEntity->entity.baseline.rendermode = kRenderTransAdd; - theTempEntity->entity.baseline.renderamt = 255; - } - - this->mCreatedGhost = true; - } - - this->mLastGhostBuilding = theTempEntity; - } - } - - // Update location we draw ghosted entity - int theMouseX, theMouseY; - this->GetMousePos(theMouseX, theMouseY); - - Vector theNormMousePos; - CreatePickingRay(theMouseX, theMouseY, theNormMousePos); - - //char theMessage[256]; - //sprintf(theMessage, "Ghost: %f, %f, %f", this->mGhostWorldLocation[0], this->mGhostWorldLocation[1],this->mGhostWorldLocation[2]); - //CenterPrint(theMessage); - - // Was either mouse button pressed? - bool theMouseOneReleased = (theMouseOneDown && !this->mMouseOneDown); - bool theMouseTwoReleased = (theMouseTwoDown && !this->mMouseTwoDown); - if(theMouseOneReleased) - { - VectorCopy(this->mLeftMouseWorldStart, this->mNormBuildLocation); - } - else if(theMouseTwoReleased) - { - VectorCopy(this->mRightMouseWorldStart, this->mNormBuildLocation); - } - - // Test to see if we're in a valid position - this->mCurrentGhostIsValid = false; - - vec3_t theLocation; - if(AvHSHUTraceAndGetIsSiteValidForBuild(this->mGhostBuilding, this->GetVisualOrigin(), theNormMousePos, &theLocation)) - { - // Update with cost and research info - bool theIsResearchable; - int theCost; - float theTime; - if(this->mTechNodes.GetResearchInfo(this->mGhostBuilding, theIsResearchable, theCost, theTime)) - { - // Ghost is valid if message available and - // we have enough resources OR - // tech takes energy and we have enough energy - this->mCurrentGhostIsValid = false; - - if(this->mTechNodes.GetIsMessageAvailable(this->mGhostBuilding)) - { - bool theTakesEnergy = AvHSHUGetDoesTechCostEnergy(this->mGhostBuilding); - if((theCost <= this->mResources) || (theTakesEnergy)) - { - this->mCurrentGhostIsValid = true; - } - } - } - } - - // Draw at selection start range * invalid range multiplier if invalid placement, draw on at target location if valid - //VectorMA(this->GetVisualOrigin(), kSelectionStartRange*kBuildInvalidRangeMultiplier, theNormMousePos, this->mGhostWorldLocation); - VectorMA(this->GetVisualOrigin(), kSelectionStartRange*8, theNormMousePos, this->mGhostWorldLocation); - //if(this->mCurrentGhostIsValid) - //{ - VectorCopy(theLocation, this->mGhostWorldLocation); - //} - - if((theMouseOneReleased) || (theMouseTwoReleased)) - { - // If this is a valid location - if(this->mCurrentGhostIsValid) - { - // Play sound - this->PlayHUDSound(HUD_SOUND_PLACE_BUILDING); - - // Remember it for input to grab - this->mValidatedBuilding = this->mGhostBuilding; - } - } - } -} - -void AvHHud::CancelBuilding() -{ - SetGhostBuildingMode(MESSAGE_NULL); - this->mValidatedBuilding = MESSAGE_NULL; -} - -void AvHHud::UpdateSelection() -{ - // Fetch current mouse up/down state from gScrollHandler and store - bool theMouseOneDown = gScrollHandler.GetMouseOneDown() && !gCommanderHandler.GetMouseOneDown(); - bool theMouseTwoDown = gScrollHandler.GetMouseTwoDown(); - - int theCurrentX, theCurrentY; - this->GetMousePos(theCurrentX, theCurrentY); - - CreatePickingRay(theCurrentX, theCurrentY, this->mMouseWorldPosition); - //ASSERT(this->mMouseWorldPosition.z < 0.0f); - - // Left mouse button // - // If mouse just pressed, set starting point - if(theMouseOneDown && !this->mMouseOneDown) - { - this->mMouseOneStartX = theCurrentX; - this->mMouseOneStartY = theCurrentY; - this->mLeftMouseStarted = true; - - //CreatePickingRay(theCurrentX, theCurrentY, this->mLeftMouseWorldStart); - //ASSERT(this->mLeftMouseWorldStart.z < 0.0f); - VectorCopy(this->mMouseWorldPosition, this->mLeftMouseWorldStart); - } - else - { - this->mLeftMouseStarted = false; - } - - // If mouse just released, set flag to indicate selection just changed - if(!theMouseOneDown && this->mMouseOneDown) - { - //CreatePickingRay(theCurrentX, theCurrentY, this->mLeftMouseWorldEnd); - //ASSERT(this->mLeftMouseWorldEnd.z < 0.0f); - VectorCopy(this->mMouseWorldPosition, this->mLeftMouseWorldEnd); - - //this->mSelectionJustChanged = true; - this->mLeftMouseEnded = true; - } - else - { - this->mLeftMouseEnded = false; - } - - // Right mouse button // - // If mouse two just pressed, set starting point - if(theMouseTwoDown && !this->mMouseTwoDown) - { - this->mMouseTwoStartX = theCurrentX; - this->mMouseTwoStartY = theCurrentY; - this->mRightMouseStarted = true; - - //CreatePickingRay(theCurrentX, theCurrentY, this->mRightMouseWorldStart); - //ASSERT(this->mRightMouseWorldStart.z < 0.0f); - VectorCopy(this->mMouseWorldPosition, this->mRightMouseWorldStart); - } - else - { - this->mRightMouseStarted = false; - } - - // If mouse just released, set flag to indicate selection just changed - if(!theMouseTwoDown && this->mMouseTwoDown) - { - //CreatePickingRay(theCurrentX, theCurrentY, this->mRightMouseWorldEnd); - //ASSERT(this->mRightMouseWorldEnd.z < 0.0f); - VectorCopy(this->mMouseWorldPosition, this->mRightMouseWorldEnd); - - //this->mSelectionJustChanged = true; - this->mRightMouseEnded = true; - } - else - { - this->mRightMouseEnded = false; - } - - // Set extents of marquee control - this->mSelectionBox->SetStartPos(this->mMouseOneStartX, this->mMouseOneStartY); - this->mSelectionBox->SetEndPos(theCurrentX, theCurrentY); - - // Set visibility state of marquee control - //this->mSelectionBox->setVisible(theMouseOneDown); - this->mSelectionBox->setVisible(false); - - this->mSelectionBoxX1 = mMouseOneStartX; - this->mSelectionBoxY1 = mMouseOneStartY; - this->mSelectionBoxX2 = theCurrentX; - this->mSelectionBoxY2 = theCurrentY; - - this->mSelectionBoxVisible = theMouseOneDown; - - // If we're just selecting stuff, don't want to hit this button by mistake - //gCommanderHandler.SetActive(!theMouseOneDown); - - // Change context sensitive cursor depending on current position - //if(this->mSelected.size() > 0) - //{ -// if(this->mGhostBuilding == MESSAGE_NULL) -// { -// Vector theCurrentMouseRay; - // CreatePickingRay(theCurrentX, theCurrentY, theCurrentMouseRay); -// -// int theTargetIndex; -// AvHOrderTargetType theTargetType; -// Vector theTargetLocation; -// AvHUser3 theUser3 = AVH_USER3_NONE; -// AvHOrderType theOrderType = AvHGetDefaultOrderType(this->GetHUDTeam(), this->GetVisualOrigin(), theCurrentMouseRay, theTargetIndex, theTargetLocation, theUser3, theTargetType); - // Test UI blocking -// theOrderType = ORDERTYPEL_DEFAULT; -// if(!AvHSHUGetIsRegionBlockedByUI((float)theCurrentX/ScreenWidth, (float)theCurrentY/ScreenHeight)) -// { -// theOrderType = ORDERTYPET_GUARD; -// } -// this->SetCursor(theOrderType); - - // Change cursor depending on order type - //if(theOrderType != ORDERTYPEL_MOVE && this->mSelected.size() > 0 ) - //{ -// if(!this->GetIsRegionBlockedByUI(theCurrentX/ScreenWidth(), theCurrentY/ScreenHeight())) -// { - //this->SetCursor(theOrderType); -// } - //} -// } - //} - - if(this->mLeftMouseEnded) - { - // Select all units at this click or in this area (but don't select when clicking a building down) - if(!this->mPlacingBuilding) - { - gSelectionHelper.QueueSelection(this->GetVisualOrigin(), this->mLeftMouseWorldStart, this->mLeftMouseWorldEnd, this->GetHUDTeam()); - gSelectionHelper.ProcessPendingSelections(); - } - } - - if(gSelectionHelper.SelectionResultsWaiting()) - { - EntityListType theNewSelection; - gSelectionHelper.GetAndClearSelection(theNewSelection); -// this->mNumLocalSelectEvents++; - - if(theNewSelection != this->mSelected) - { - this->mSelected = theNewSelection; - this->mSelectionJustChanged = true; - } - - this->ClearTrackingEntity(); - } - - // If selection just changed, make sure we have selection effects for everyone - if(this->mSelectionJustChanged) - { - // Create effects - this->SetSelectionEffects(this->mSelected); - - this->PlayHUDSound(HUD_SOUND_SELECT); - - gCommanderHandler.SetSelectedUnits(this->mSelected); - - // Clear flag - this->mSelectionJustChanged = false; - -// // Set default order mode -// if(this->mSelected.size() > 0) -// { -// this->mOrderMode = ORDERTYPEL_DEFAULT; -// } -// else -// { -// this->mOrderMode = ORDERTYPE_UNDEFINED; -// } - } - - this->UpdateBuildingPlacement(); - - // Store current mouse state - this->mMouseOneDown = theMouseOneDown; - this->mMouseTwoDown = theMouseTwoDown; -} - -void AvHHud::UpdateBuildResearchText() -{ - // Hide unchanging ("unhelpful"?) help text after this amount of time - const float kHelpTextInterval = 2.5f; - - if(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) - { - Label* theHelpTextLabel = NULL; - if(this->GetManager().GetVGUIComponentNamed(kTechHelpText, theHelpTextLabel)) - { - gCommanderHandler.RecalculateBuildResearchText(); - - // Display build/research text - string theBuildResearchText = gCommanderHandler.GetBuildResearchText(); - theHelpTextLabel->setText(theBuildResearchText.c_str()); - - // Center it - int theWidth, theHeight; - theHelpTextLabel->getTextSize(theWidth, theHeight); - - int theX, theY; - theHelpTextLabel->getPos(theX, theY); - - int theScreenWidth = ScreenWidth(); - theHelpTextLabel->setPos(theScreenWidth/2 - theWidth/2, theY); - - // Vanish if no text (but keep build/research text visible) - theHelpTextLabel->setVisible(true); - if(theBuildResearchText.length() == 0)// || ((this->mTimeLastHelpTextChanged != -1) && (this->mTimeLastHelpTextChanged + kHelpTextInterval < this->mTimeOfLastUpdate))) - { - theHelpTextLabel->setVisible(false); - } - - // Display action button tool tip - string theTechHelpText = gCommanderHandler.GetTechHelpText(); - if(theTechHelpText != "") - { - this->SetActionButtonHelpMessage(theTechHelpText); - } - - if(theTechHelpText != this->mPreviousHelpText) - { - this->mTimeLastHelpTextChanged = this->mTimeOfLastUpdate; - } - - this->mPreviousHelpText = theTechHelpText; - } - } -} - -void AvHHud::UpdateTechNodes() -{ - this->UpdateBuildResearchText(); - - // Don't get new node until existing one has been processed - if(this->mTechEvent == MESSAGE_NULL) - { - //if(this->mGameStarted) - //{ - //AvHTechNode theTechNode; - //if(gCommanderHandler.GetAndClearTechNodePressed(theTechNode)) - AvHMessageID theMessageID; - if(gCommanderHandler.GetAndClearTechNodePressed(theMessageID)) - { - // Check for grouping and request events and handle separately - if(this->mTechNodes.GetIsMessageAvailable(theMessageID)) - { - bool theIsResearchable; - int theCost; - float theTime; - - if(this->mTechNodes.GetResearchInfo(theMessageID, theIsResearchable, theCost, theTime)) - { - if(AvHSHUGetIsBuildTech(theMessageID)) - { - // Don't check for enough points yet, they might get enough points before they put it down - - // Get ready to build it, don't send a research message - this->SetGhostBuildingMode(theMessageID); - } - else if(AvHSHUGetIsResearchTech(theMessageID) && theIsResearchable) - { - // If we hit cancel, and we're in building mode, get out of build mode and throw event away - if((theMessageID == MESSAGE_CANCEL) && (this->GetGhostBuilding() != MESSAGE_NULL)) - { - CancelBuilding(); - } - else if(theCost <= this->mResources) - { - this->mTechEvent = theMessageID; - } - else - { - // Emit error message that you don't have the resources - this->PlayHUDSound(HUD_SOUND_MARINE_MORE); - } - } - else if(theMessageID == BUILD_RECYCLE) - { - this->mTechEvent = theMessageID; - } - } - else - { - this->mTechEvent = theMessageID; - } - } - else if((theMessageID >= SAYING_1) && (theMessageID <= SAYING_9)) - { - this->mTechEvent = theMessageID; - } - } - //} - } -} - -void AvHHud::UpdateAmbientSounds() -{ - Vector theListenerPosition; - VectorCopy(gPredictedPlayerOrigin, theListenerPosition); - - // Commanders have a different PAS then themselves - if(this->mInTopDownMode) - { - VectorCopy(this->mCommanderPAS, theListenerPosition); - } - - for(AmbientSoundListType::iterator theIter = this->mAmbientSounds.begin(); theIter != this->mAmbientSounds.end(); theIter++) - { - theIter->StartPlayingIfNot(); - theIter->UpdateVolume(theListenerPosition); - } -} - -void AvHHud::UpdateFromEntities(float inCurrentTime) -{ - // Only update every so often for performance reasons - const float kEntityUpdateInterval = .4f; - if(inCurrentTime > (this->mTimeOfLastEntityUpdate + kEntityUpdateInterval)) - { - this->mHelpIcons.clear(); - this->mHelpEnts.clear(); - - bool theAutoHelpEnabled = gEngfuncs.pfnGetCvarFloat(kvAutoHelp); - - // Clear help icons - this->mHelpEnts.clear(); - - // Clear building effects - //this->mBuildingEffectsEntityList.clear(); - - this->mTimeOfLastEntityUpdate = inCurrentTime; - - // Scan for entities, adding help icons - // Only draw if enabled - const int kHelpDistance = 350; - const int kBuildDistance = 500; - const int kHealthDistance = 125; - - // Look for entities that have help icons in front of us. Don't search to far away, it could give players away. - EntityListType theHelpEntities; - AvHSHUGetEntities(-1, theHelpEntities); - - cl_entity_t* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - - for(EntityListType::iterator theIter = theHelpEntities.begin(); theIter != theHelpEntities.end(); theIter++) - { - vec3_t theLocation; - if(AvHSHUGetEntityLocation(*theIter, theLocation)) - { - float theDistance = VectorDistance((float*)&theLocation, theLocalPlayer->origin); - if(theDistance < kHelpDistance) - { - // If iuser3 isn't 0, try looking up an icon for it - physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theIter); - if(theEntity) - { - // Don't add cloaked entities (I wish this could be more general purpose) - if((theEntity->team == this->GetHUDTeam()) || (theEntity->rendermode == kRenderNormal)) - { - int theUser3 = theEntity->iuser3; - vec3_t theEntityOrigin = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs); - - // Some entities always draw - bool theAlwaysDraw = false; - switch(theUser3) - { - case AVH_USER3_WELD: - //case AVH_USER3_COMMANDER_STATION: - //case AVH_USER3_HIVE: - theAlwaysDraw = true; - break; - } - - if(theAutoHelpEnabled || theAlwaysDraw) - { - this->mHelpIcons.push_back( make_pair(theEntityOrigin, theUser3) ); - - // Push back entity for displaying helpful tooltips - this->mHelpEnts.push_back(*theIter); - } - } - } - } - } - } - } -} - -void AvHHud::UpdateViewModelEffects() -{ - cl_entity_t* theViewModel = GetViewEntity(); - if(theViewModel) - { - int theRenderMode = kRenderNormal; - int theRenderAmount = 0; - int theRenderFx = theViewModel->curstate.renderfx; - color24 theRenderColor = theViewModel->curstate.rendercolor; - short theSkin = 0; - - // Set the skin, stored in playerclass - - //cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - - // Use the visible player so that when we are spectating we can tell - // when the player is cloaked. - cl_entity_s* theLocalPlayer = GetVisiblePlayer(); - - if(theLocalPlayer) - { - - //theViewModel->curstate.skin = theLocalPlayer->curstate.skin; - theRenderMode = theLocalPlayer->curstate.rendermode; - theRenderAmount = theLocalPlayer->curstate.renderamt; - theRenderFx = theLocalPlayer->curstate.renderfx; - theRenderColor = theLocalPlayer->curstate.rendercolor; - theSkin = theLocalPlayer->curstate.skin; - // Hack to make cloaking work for viewmodels (if only view models rendered in additive properly). - // Draw view model normally unless fully cloaked. - //bool theIsCloakedViaUpgrade = GetHasUpgrade(theLocalPlayer->curstate.iuser4, MASK_ALIEN_CLOAKED); - int old=theRenderAmount; - if((theRenderMode == kRenderTransTexture) /*|| theIsCloakedViaUpgrade*/) - { - theRenderFx = kRenderFxNone; - theRenderColor.r = theRenderColor.g = theRenderColor.b = 0; - - if ( theRenderAmount == kAlienSelfCloakingBaseOpacity ) - { - theRenderMode = kAlienCloakViewModelRenderMode; - theRenderAmount = 10; - } - else if( theRenderAmount > kAlienSelfCloakingBaseOpacity && theRenderAmount < 186) - { - theRenderMode = kAlienCloakViewModelRenderMode; - theRenderAmount = 40; - } - else if ( theRenderAmount == 186 ) { - theRenderMode = kAlienCloakViewModelRenderMode; - theRenderAmount = 50; - } - else - { - theRenderMode = kRenderNormal; - theRenderAmount = 255; - } - } - - - //char theMessage[128]; - //sprintf(theMessage, "Setting view model mode: %d amount: %d\n", theRenderMode, theRenderAmount); - //CenterPrint(theMessage); - } - -// if(GetHasUpgrade(this->GetLocalUpgrades(), MASK_ALIEN_CLOAKED)) -// { -// theRenderMode = kAlienCloakViewModelRenderMode; -// int theCloakingLevel = AvHGetAlienUpgradeLevel(this->GetLocalUpgrades(), MASK_UPGRADE_7); -// theRenderAmount = kAlienCloakViewModelAmount - theCloakingLevel*kAlienCloakViewModelLevelAmount; -// -// // Put in color, because texture rendering needs it -// theRenderFx = kRenderFxNone; -// theRenderColor.r = theRenderColor.g = theRenderColor.b = 0; -// } - - if(this->mInTopDownMode) - { - theRenderMode = kRenderTransAdd; - theRenderAmount = 1; - } - - theViewModel->curstate.skin = theSkin; - theViewModel->curstate.rendermode = theRenderMode; - theViewModel->curstate.renderamt = theRenderAmount; - theViewModel->curstate.renderfx = theRenderFx; - theViewModel->curstate.rendercolor = theRenderColor; - } -} - -void AvHHud::UpdateResources(float inTimePassed) -{ - const float kResourceRate = this->GetMaxAlienResources()/50.0f; - int thePointsToMove = max(inTimePassed*kResourceRate, 1); - - // Update visual resources if different this resources - if(this->mVisualResources != this->mResources) - { - if(abs(this->mVisualResources - this->mResources) <= thePointsToMove) - { - this->mVisualResources = this->mResources; - } - else - { - if(this->mVisualResources < this->mResources) - { - this->mVisualResources += thePointsToMove; - } - else - { - this->mVisualResources -= thePointsToMove; - } - } - } - - // Smoothly adjust energy level - float theCurrentEnergyLevel = pmove->fuser3/kNormalizationNetworkFactor; - - if((g_iUser1 == OBS_IN_EYE) && (g_iUser2 != this->mUser2OfLastEnergyLevel)) - { - // This isn't working yet - this->mVisualEnergyLevel = theCurrentEnergyLevel; - this->mUser2OfLastEnergyLevel = g_iUser2; - } - else - { - float kEnergyRate = 1.0f; - float theEnergyToMove = inTimePassed*kEnergyRate; - if(this->mVisualEnergyLevel != theCurrentEnergyLevel) - { - float theDiff = fabs(this->mVisualEnergyLevel - theCurrentEnergyLevel); - if(theDiff <= theEnergyToMove) - { - this->mVisualEnergyLevel = theCurrentEnergyLevel; - } - else - { - if(this->mVisualEnergyLevel < theCurrentEnergyLevel) - { - this->mVisualEnergyLevel += theEnergyToMove; - } - else - { - this->mVisualEnergyLevel -= theEnergyToMove; - } - } - } - } - - string theResourceText; - char theResourceBuffer[64]; - - if(this->GetInTopDownMode() && this->mCommanderResourceLabel) - { - LocalizeString(kMarineResourcePrefix, theResourceText); - sprintf(theResourceBuffer, "%s %d", theResourceText.c_str(), this->mVisualResources); - this->mCommanderResourceLabel->setText(64, theResourceBuffer); - } - - // Update visual resource indicators, expiring old ones - const float kNumericalInfoEffectLifetime = 1.1f; - const float kNumericalInfoScrollSpeed = 24; - for(NumericalInfoEffectListType::iterator theIter = this->mNumericalInfoEffects.begin(); theIter != this->mNumericalInfoEffects.end(); /* no inc */) - { - if((theIter->GetTimeCreated() + kNumericalInfoEffectLifetime) < this->mTimeOfLastUpdate) - { - theIter = this->mNumericalInfoEffects.erase(theIter); - } - else - { - // Update position - float thePosition[3]; - theIter->GetPosition(thePosition); - - thePosition[2] += inTimePassed*kNumericalInfoScrollSpeed; - - theIter->SetPosition(thePosition); - - // Next - theIter++; - } - } -} - - - - -//void AvHHud::ChangeUpgradeCosts(int inOldMessageID, int inNewMessageID, const char* inText) -//{ -// this->ChangeUpgradeCostsForMenu(this->mSoldierMenu, inOldMessageID, inNewMessageID, inText); -// this->ChangeUpgradeCostsForMenu(this->mLeaderMenu, inOldMessageID, inNewMessageID, inText); -// this->ChangeUpgradeCostsForMenu(this->mCommanderMenu, inOldMessageID, inNewMessageID, inText); -// -// this->UpdateUpgradeCosts(); -//} -// -//void AvHHud::ChangeUpgradeCostsForMenu(PieMenu* inMenu, int inOldMessageID, int inNewMessageID, const char* inText) -//{ -// if(inMenu) -// { -// inMenu->ChangeNode(inOldMessageID, inNewMessageID, string(inText)); -// } -//} -// -//void AvHHud::ResetUpgradeCosts() -//{ -// this->ResetUpgradeCostsForMenu(this->mSoldierMenu); -// this->ResetUpgradeCostsForMenu(this->mLeaderMenu); -// this->ResetUpgradeCostsForMenu(this->mCommanderMenu); -// -// this->UpdateUpgradeCosts(); -//} - -//void AvHHud::ResetUpgradeCostsForMenu(PieMenu* inMenu) -//{ -// if(inMenu) -// { -// inMenu->ResetToDefaults(); -// } -//} - -bool AvHHud::GetParticlesVisible() const -{ - - if (g_iUser1 == OBS_NONE) - { - return true; - } - - if (m_Spectator.IsInOverviewMode()) - { - return m_Spectator.m_iDrawCycle == 1; - } - else - { - return true; - } - -} - -// Sprite drawing on a level change is problematic and can cause crashing or disconcerting "no such sprite" error messages -bool AvHHud::GetSafeForSpriteDrawing() const -{ - bool theSafeForDrawing = false; - - const char* theLevelName = gEngfuncs.pfnGetLevelName(); - string theCurrentMapName = this->GetMapName(true); - if(theLevelName && (theCurrentMapName != "")) - { - string theLevelNameString(theLevelName); - int thePos = (int)theLevelNameString.find(theCurrentMapName); - if(thePos != string::npos) - { - theSafeForDrawing = true; - } - } - - return theSafeForDrawing; -} - -bool AvHHud::GetShouldDisplayUser3(AvHUser3 inUser3) const -{ - bool theShouldDisplay = false; - - if((inUser3 > AVH_USER3_NONE) && (inUser3 < AVH_USER3_MAX)) - { - theShouldDisplay = true; - - switch(inUser3) - { - case AVH_USER3_BREAKABLE: - case AVH_USER3_USEABLE: - case AVH_USER3_PARTICLE_ON: - case AVH_USER3_PARTICLE_OFF: - case AVH_USER3_ALPHA: - case AVH_USER3_WAYPOINT: - case AVH_USER3_NOBUILD: - case AVH_USER3_SPAWN_TEAMA: - case AVH_USER3_SPAWN_TEAMB: - theShouldDisplay = false; - break; - } - } - - return theShouldDisplay; -} - -bool AvHHud::GetTranslatedUser3Name(AvHUser3 inUser3, string& outString) const -{ - bool theSuccess = false; - - if(this->GetShouldDisplayUser3(inUser3)) - { - char theUser3String[512]; - sprintf(theUser3String, "#%s%d", kUser3Name, inUser3); - theSuccess = LocalizeString(theUser3String, outString); - } - - return theSuccess; -} - -bool AvHHud::GetTranslatedUser3Description(AvHUser3 inUser3, bool inFriendly, string& outString) const -{ - bool theSuccess = false; - - if(this->GetShouldDisplayUser3(inUser3)) - { - char theUser3String[512]; - sprintf(theUser3String, "#%s%d", kUser3Description, inUser3); - theSuccess = LocalizeString(theUser3String, outString); - - // If we're commanding, look for that description if it exists - if(this->GetInTopDownMode()) - { - string theCommanderDescription; - sprintf(theUser3String, "#%s%d", kUser3CommanderDescription, inUser3); - if(LocalizeString(theUser3String, theCommanderDescription)) - { - outString = theCommanderDescription; - theSuccess = true; - } - } - // Else look for a message that tell us what to do with this thing (assumes we're not commanding though) - else if(inFriendly) - { - string theFriendlyDescription; - sprintf(theUser3String, "#%s%d", kUser3FriendlyDescription, inUser3); - if(LocalizeString(theUser3String, theFriendlyDescription)) - { - outString = theFriendlyDescription; - theSuccess = true; - } - } - } - - return theSuccess; -} - - -void AvHHud::UpdateUpgradeCosts() -{ - PieMenu* theCurrentMenu = NULL; - if(this->GetManager().GetVGUIComponentNamed(this->mPieMenuControl, theCurrentMenu)) - { - this->UpdateEnableState(theCurrentMenu); - } -} - -void AvHHud::AddMiniMapAlert(float x, float y) -{ - mOverviewMap.AddAlert(x, y); -} - -void AvHHud::UpdateEnableState(PieMenu* inMenu) -{ - if(inMenu) - { - int thePurchaseLevel = this->GetIsCombatMode() ? max(0, this->mExperienceLevel - this->mExperienceLevelSpent - 1) : this->mResources; - - inMenu->UpdateMenuFromTech(this->mTechNodes, thePurchaseLevel); - -// if(this->GetIsNSMode()) - //{ - // Now disable any nodes whose children are all disabled (in NS, only end nodes can be chosen) - //inMenu->DisableNodesWhoseChildrenAreDisabled(); - //} - - inMenu->RecomputeVisibleSize(); - } -} - -void AvHHud::ShowMap() -{ - if (!sShowMap && gHUD.GetIsNSMode()) - { - sShowMap = true; - gHUD.HideCrosshair(); - gHUD.GetManager().UnhideComponent(kShowMapHierarchy); - } -} - -void AvHHud::HideMap() -{ - if (sShowMap) - { - sShowMap = false; - gHUD.GetManager().HideComponent(kShowMapHierarchy); - gHUD.ShowCrosshair(); - } -} - -void AvHHud::GetSpriteForUser3(AvHUser3 inUser3, int& outSprite, int& outFrame, int& outRenderMode) -{ - - switch (inUser3) - { - - // Marines - case AVH_USER3_WAYPOINT: - outSprite = Safe_SPR_Load(kSmallOrderSprite); - outFrame = 2; - outRenderMode = kRenderTransAdd; - break; - case AVH_USER3_MARINE_PLAYER: - outSprite = Safe_SPR_Load(kMarinePlayersSprite); - outFrame = 0; - break; - case AVH_USER3_HEAVY: // This really means a marine with heavy armor, not a heavy armor object. - outSprite = Safe_SPR_Load(kMarinePlayersSprite); - outFrame = 1; - break; - case AVH_USER3_COMMANDER_STATION: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 5; - break; - case AVH_USER3_TURRET_FACTORY: - case AVH_USER3_ADVANCED_TURRET_FACTORY: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 6; - break; - case AVH_USER3_ARMORY: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 7; - break; - case AVH_USER3_ADVANCED_ARMORY: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 8; - break; - case AVH_USER3_ARMSLAB: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 9; - break; - case AVH_USER3_PROTOTYPE_LAB: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 10; - break; - case AVH_USER3_OBSERVATORY: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 11; - break; - case AVH_USER3_TURRET: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 12; - break; - case AVH_USER3_SIEGETURRET: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 13; - break; - case AVH_USER3_RESTOWER: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 14; - break; - case AVH_USER3_INFANTRYPORTAL: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 15; - break; - case AVH_USER3_PHASEGATE: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 16; - break; - - // Aliens - case AVH_USER3_DEFENSE_CHAMBER: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 17; - break; - case AVH_USER3_MOVEMENT_CHAMBER: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 18; - break; - case AVH_USER3_OFFENSE_CHAMBER: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 19; - break; - case AVH_USER3_SENSORY_CHAMBER: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 20; - break; - case AVH_USER3_ALIENRESTOWER: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 21; - break; - case AVH_USER3_HIVE: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 3; - break; - case AVH_USER3_ALIEN_PLAYER1: - outSprite = Safe_SPR_Load(kAlienPlayersSprite); - outFrame = 0; - break; - case AVH_USER3_ALIEN_PLAYER2: - outSprite = Safe_SPR_Load(kAlienPlayersSprite); - outFrame = 1; - break; - case AVH_USER3_ALIEN_PLAYER3: - outSprite = Safe_SPR_Load(kAlienPlayersSprite); - outFrame = 2; - break; - case AVH_USER3_ALIEN_PLAYER4: - outSprite = Safe_SPR_Load(kAlienPlayersSprite); - outFrame = 3; - break; - case AVH_USER3_ALIEN_PLAYER5: - outSprite = Safe_SPR_Load(kAlienPlayersSprite); - outFrame = 4; - break; - case AVH_USER3_ALIEN_EMBRYO : - outSprite = Safe_SPR_Load(kAlienPlayersSprite); - outFrame = 5; - break; - - case AVH_USER3_FUNC_RESOURCE: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 4; - break; - case AVH_USER3_WELD: - outSprite = Safe_SPR_Load(kStructuresSprite); - outFrame = 0; - break; - - default: - outSprite = 0; - outFrame = 0; - - } - -} - -int AvHHud::GetCurrentSquad() const -{ - return mCurrentSquad; -} - -AvHOverviewMap& AvHHud::GetOverviewMap() -{ - return mOverviewMap; -} - -void AvHHud::ShowCrosshair() -{ - - ++mCrosshairShowCount; - - if (mCrosshairShowCount > 0) - { - SetCrosshair(mCrosshairSprite, mCrosshairRect, mCrosshairR, mCrosshairG, mCrosshairB); - } - -} - -void AvHHud::HideCrosshair() -{ - - --mCrosshairShowCount; - - if (mCrosshairShowCount <= 0) - { - wrect_t nullrect = { 0, 0, 0, 0 }; - SetCrosshair(0, nullrect, 0, 0, 0); - } - -} - -void AvHHud::SetCurrentCrosshair(HSPRITE hspr, wrect_t rc, int r, int g, int b) -{ - mCrosshairSprite = hspr; - mCrosshairRect = rc; - mCrosshairR = r; - mCrosshairG = g; - mCrosshairB = b; - - if (mCrosshairShowCount > 0) - { - SetCrosshair(mCrosshairSprite, mCrosshairRect, mCrosshairR, mCrosshairG, mCrosshairB); - } - -} - -void AvHHud::SetViewport(const int inViewport[4]) -{ - if (!m_Spectator.IsInOverviewMode()) - { - mViewport[0] = inViewport[0]; - mViewport[1] = inViewport[1]; - mViewport[2] = inViewport[2]; - mViewport[3] = inViewport[3]; - } - else - { - mSpecialViewport[0] = inViewport[0]; - mSpecialViewport[1] = inViewport[1]; - mSpecialViewport[2] = inViewport[2]; - mSpecialViewport[3] = inViewport[3]; - - mViewport[0] = 0; - mViewport[1] = 0; - mViewport[2] = ScreenWidth(); - mViewport[3] = ScreenHeight(); - - } -} - -void AvHHud::GetViewport(int outViewport[4]) const -{ - outViewport[0] = mViewport[0]; - outViewport[1] = mViewport[1]; - outViewport[2] = mViewport[2]; - outViewport[3] = mViewport[3]; -} - -const AvHFont& AvHHud::GetSmallFont() const -{ - return mSmallFont; -} - -void AvHHud::PlayStream() -{ - if(gEngfuncs.Cmd_Argc() <= 1) - { - gEngfuncs.Con_Printf( "usage: playstream \n" ); - } - else - { - if ( gEngfuncs.Cmd_Argc() >= 2 ) - { - // Read URL - string theURL; - theURL = string("http://") + string(gEngfuncs.Cmd_Argv(1)); - - string theError; - if(!gHUD.PlayInternetStream(theURL, theError)) - { - gHUD.AddTooltip(theError.c_str()); - } - } - } -} - -void AvHHud::StopStream() -{ - gHUD.StopInternetStream(); -} - -float AvHHud::GetServerVariableFloat(const char* inName) const -{ - ServerVariableMapType::const_iterator iterator; - iterator = mServerVariableMap.find(inName); - - if ( iterator == mServerVariableMap.end() ) - { - return 0; - } - else - { - return atof( iterator->second.c_str() ); - } - -} - - -/** - * Prints the call stack when an unhandled exception occurs. - */ -LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* pExp) -{ - - /* - // E-mail the exception log to the programmers. - - std::stringstream buffer; - LogException(buffer, pExp); - - const char* serverName = "66.111.4.62"; - const char* fromAddress = "noreply@overmind.com"; - - const char* programmerAddress[] = - { - "max_mcguire@yahoo.com", - }; - - for (int i = 0; i < sizeof(programmerAddress) / sizeof(char*); ++i) - { - SendMail(serverName, fromAddress, fromAddress, programmerAddress[i], - "Exception Log", buffer.str().c_str()); - } - */ - - AvHHud::ResetGammaAtExit(); - - return EXCEPTION_EXECUTE_HANDLER; - -} - -// Added DLL entry point to try to reset gamma properly under VAC -BOOL WINAPI DllMain(HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved) -{ - if (fdwReason == DLL_PROCESS_ATTACH) - { - - // Install a crash handler. - //SetUnhandledExceptionFilter(ExceptionFilter); - - } - else if (fdwReason == DLL_PROCESS_DETACH) - { - AvHHud::ResetGammaAtExit(); - } - return TRUE; -} +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: Main NS NUD, also interface to client network messages +// +// $Workfile: AvHHud.cpp $ +// $Date: 2002/10/28 20:35:32 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHHud.cpp,v $ +// Revision 1.70 2002/10/28 20:35:32 Flayra +// - Fix for gamma reset with VAC +// - Info location fix after changelevel +// +// Revision 1.69 2002/10/25 21:49:10 Flayra +// - Updated skin to sit in pev->skin +// - Reset components every tick to fix problem with disappearing team resource label +// +// Revision 1.68 2002/10/24 21:29:49 Flayra +// - Moved help client-side +// - Fixed particle/changelevel crash +// - Reworked marine upgrade drawing +// - Added lots of utility functions for help system (mirrors functions on server) +// - Removed gamma message unless it failed or if maxplayers is 1 +// - Fixed alien hive sight crash +// - Show players under reticle while in ready room and spectating +// - Removed ugly/too-prevalent user3 icons +// +// Revision 1.67 2002/10/18 22:19:49 Flayra +// - Added alien easter egg sayings +// +// Revision 1.66 2002/10/16 20:53:41 Flayra +// - Draw scan model specially so it looks right +// +// Revision 1.65 2002/10/16 00:58:02 Flayra +// - Removed hotgroups +// - Added "need order" alert +// +// Revision 1.64 2002/10/03 20:24:39 Flayra +// - Changes for "more resources required" +// +// Revision 1.63 2002/10/03 18:54:30 Flayra +// - Allow right-click to cancel building placement +// - Fixed help icons +// - Added a couple utility functions +// - Reworked order notification +// - Reworked blip network messages to avoid hard-coded limit +// - Sent max resources down with current resources +// - Countdown sound no longer prevents other hud sounds +// - Alien trigger sounds +// - New order sounds +// - No longer disable nodes out of our cost range +// +// Revision 1.62 2002/09/25 20:47:19 Flayra +// - Don't draw elements on HUD when dead +// - UI refactoring +// - Split reticle help into help text and reticle text +// - Removed use order +// - Added separate select sound for alien +// - Multiple move sounds +// - Only draw entity build/health status when under reticle (no more scanning around you) +// - Added 3 new sayings +// +// Revision 1.61 2002/09/23 22:18:25 Flayra +// - Added alien build circles +// - Game status changes so particles aren't sent every time +// - Demo playback changes (save restore basic data that HUD already has) +// - New alert sounds +// - Skin support +// +// Revision 1.60 2002/09/09 19:55:24 Flayra +// - Added hive info indicator +// - Fixed bug where reticle tooltip help text wasn't being set until a weapon was selected +// - Fixed release mode bug where tooltips weren't expiring +// - Fixed bug where marine upgrades blinked +// - "No commander" indicator now blinks +// +// Revision 1.59 2002/08/31 18:01:01 Flayra +// - Work at VALVe +// +// Revision 1.58 2002/08/16 02:37:49 Flayra +// - HUD sounds no longer cut each other off (they won't play instead of cutting off another sound) +// - Tooltip sounds +// - Selection issues +// - Draw rings around buildings that need to be built +// - Removed old overwatch code +// +// Revision 1.57 2002/08/09 01:02:40 Flayra +// - Added hooks for demo playback, removed prediction selection +// +// Revision 1.56 2002/08/02 21:59:12 Flayra +// - Added reticle help, new tooltip system and much nicer order drawing! Refactored view model drawing a bit, hoping to make texture blending work for it. +// +// Revision 1.55 2002/07/26 23:05:01 Flayra +// - Generate numerical feedback for damage events +// - Refactoring for more info when looking at something (instead of bad-looking player names only) +// +// Revision 1.54 2002/07/24 18:45:41 Flayra +// - Linux and scripting changes +// +// Revision 1.53 2002/07/23 17:07:36 Flayra +// - Added visually-smooth energy level, added versatile location code, new hive sight info, refactored to remove extra sprites (128 HUD sprites bug), commander tech help fixes +// +// Revision 1.52 2002/07/10 14:41:55 Flayra +// - Fixed bug where non-sighted particle systems weren't being drawn for players on the ground (bug #127) +// +// Revision 1.51 2002/07/08 17:07:56 Flayra +// - Started to add display of marine upgrade sprite, fixed bug where building indicators aren't displayed after a map change, info_location drawing changes, primal scream color tweak, removed old hive drawing code +// +// Revision 1.50 2002/07/01 21:35:05 Flayra +// - Removed lots of outdated sprites and sprite code, added building ranges, fixed ghost building problem (bug #82) +// +// Revision 1.49 2002/06/25 18:03:09 Flayra +// - Added info_locations, removed old weapon help system, added smooth resource swelling, lots of alien UI usability changes, fixed problem with ghost building +// +// Revision 1.48 2002/06/10 19:55:36 Flayra +// - New commander UI (bindable via hotkeys, added REMOVE_SELECTION for when clicking menu options when no players selected) +// +// Revision 1.47 2002/06/03 16:48:45 Flayra +// - Help sprites moved into one animated sprite, select sound volume reduced (now that sound is normalized) +// +// Revision 1.46 2002/05/28 17:48:14 Flayra +// - Minimap refactoring, reinforcement refactoring, new hive sight fixes, recycling support +// +// Revision 1.45 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHConstants.h" +#include "mod/AvHHud.h" +#include "cl_dll/hud.h" +#include "cl_dll/cl_util.h" +#include "vgui_label.h" +#include "ui/PieMenu.h" +#include "mod/AvHTeamHierarchy.h" +#include "mod/AvHPieMenuHandler.h" +#include "mod/AvHParticleTemplateClient.h" +#include "mod/AvHParticleSystemManager.h" +#include "mod/AvHClientVariables.h" +#include "mod/AvHSpecials.h" +#include "ui/FadingImageLabel.h" +#include "mod/AvHScrollHandler.h" +#include "mod/AvHEvents.h" +#include "pm_shared/pm_shared.h" +#include "common/cl_entity.h" +#include "mod/AvHCommanderModeHandler.h" +#include "mod/AvHParticleEditorHandler.h" +#include "mod/AvHTechTree.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHTitles.h" +#include "mod/AvHSelectionHelper.h" +#include "mod/AvHActionButtons.h" +#include "pm_shared/pm_debug.h" +#include "util/MathUtil.h" +#include "util/STLUtil.h" +#include "mod/AvHSharedUtil.h" +#include "common/r_efx.h" +#include "cl_dll/eventscripts.h" +#include +#include "mod/AvHSprites.h" +#include "ui/UIUtil.h" +#include "mod/AvHMiniMap.h" +#include "types.h" +#include +#include "common/event_api.h" +#include "mod/AvHHulls.h" +#include "common/com_model.h" +#include "mod/AvHBasePlayerWeaponConstants.h" +#include "cl_dll/vgui_ScorePanel.h" +#include "mod/AvHAlienAbilityConstants.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHScriptManager.h" +#include "mod/AvHHudConstants.h" +#include "cl_dll/demo.h" +#include "common/demo_api.h" +#include "cl_dll/ammohistory.h" +#include "mod/AvHTechImpulsePanel.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHCommandConstants.h" +#include "mod/AvHDebugUtil.h" +#include "engine/keydefs.h" +#include "ui/ChatPanel.h" +#include "cl_dll/r_studioint.h" +#include "util/Tokenizer.h" +#include +#include "mod/AvHNetworkMessages.h" + +//#include "cl_dll/studio_util.h" +//#include "cl_dll/r_studioint.h" + +void IN_GetMousePos( int *mx, int *my ); +extern playermove_t *pmove; +void RemoveAllDecals(); +void ScorePanel_InitializeDemoRecording(); + +// Include windows for GDI and gamma functions +#include "windows.h" + +extern engine_studio_api_t IEngineStudio; + +AvHPieMenuHandler gPieMenuHandler; +AvHScrollHandler gScrollHandler; +AvHCommanderModeHandler gCommanderHandler; +AvHParticleEditorHandler gParticleEditorHandler; +extern AvHParticleTemplateListClient gParticleTemplateList; +extern DebugPointListType gTriDebugLocations; +extern extra_player_info_t g_PlayerExtraInfo[MAX_PLAYERS+1]; +extern WeaponsResource gWR; + +extern double gClientTimeLastUpdate; +extern "C" Vector gPredictedPlayerOrigin; +extern "C" Vector gPredictedPlayerVOfs; +extern void __CmdFunc_Close(void); +extern int CL_ButtonBits(int); +extern int g_iVisibleMouse; + +GammaTable AvHHud::sPregameGammaTable; +GammaTable AvHHud::sGameGammaTable; + +bool AvHHud::sShowMap = false; + +// Global because of global HUD complilation error +AvHMiniMap gMiniMap; + +#include "VGUI_RepaintSignal.h" + +//extern vec3_t v_origin; +//extern vec3_t v_angles; +//vec3_t gPlayerOrigin; +//vec3_t gPlayerAngles; + +extern AvHSelectionHelper gSelectionHelper; + +//#if defined( AVH_CLIENT ) +//extern "C" float gOverwatchTargetRange; +extern float gOverwatchTargetRange; +//#endif + +extern bool gResetViewAngles; +extern vec3_t gViewAngles; +extern char sDebugString[128]; + +float kOverwatchFlashInterval = 2.5f; +const float kReticleInfoMaxAlpha = 50; +int gVisibleMouse = 0; + +//: cvar pointers, these should always remain valid once they are set. + + +const AvHMapExtents& GetMapExtents() +{ + return gHUD.GetMapExtents(); +} + +NumericalInfoEffect::NumericalInfoEffect(float inPosition[3], float inNumber, int inEventType, float inTimeCreated) +{ + this->mPosition[0] = inPosition[0]; + this->mPosition[1] = inPosition[1]; + this->mPosition[2] = inPosition[2]; + + this->mNumber = inNumber; + this->mEventType = inEventType; + + this->mTimeCreated = inTimeCreated; +} + +void NumericalInfoEffect::GetPosition(float* outPosition) const +{ + outPosition[0] = this->mPosition[0]; + outPosition[1] = this->mPosition[1]; + outPosition[2] = this->mPosition[2]; +} + +float NumericalInfoEffect::GetNumber() const +{ + return this->mNumber; +} + +int NumericalInfoEffect::GetEventType() const +{ + return this->mEventType; +} + +float NumericalInfoEffect::GetTimeCreated() const +{ + return this->mTimeCreated; +} + +void NumericalInfoEffect::SetPosition(float inPosition[3]) +{ + this->mPosition[0] = inPosition[0]; + this->mPosition[1] = inPosition[1]; + this->mPosition[2] = inPosition[2]; +} + +void AvHHud::OnActivateSteamUI() +{ + // Set the normal gamma so the Steam UI looks correct. + sPregameGammaTable.InitializeToVideoState(); + mSteamUIActive = true; +} + +void AvHHud::OnDeactivateSteamUI() +{ + + // Set the special NS gamma. + SetGamma(mDesiredGammaSlope); + mSteamUIActive = false; + + // The Steam UI screws up the mouse cursor so reset it. + if (gViewPort != NULL) + { + gViewPort->UpdateCursorState(); + } + +} + +void AvHHud::OnLostFocus() +{ + sPregameGammaTable.InitializeToVideoState(); +} + +bool AvHHud::OnKeyEvent(int virtualKey, int scanCode, bool pressed) +{ + + if (gViewPort != NULL && !mSteamUIActive) + { + + ChatPanel* theChatPanel = gViewPort->GetChatPanel(); + + if (theChatPanel && theChatPanel->isVisible()) + { + if (pressed) + { + theChatPanel->KeyDown(virtualKey, scanCode); + return true; + } + else + { + // If the key wasn't pressed while the chat window was open, + // the key up needs to go to HL. + return theChatPanel->WasKeyPushed(virtualKey); + } + } + + if (virtualKey == VK_ESCAPE && GetInTopDownMode() && mGhostBuilding != MESSAGE_NULL) + { + if (pressed) + { + CancelBuilding(); + } + return true; + } + + } + + return false; + +} + +int AvHHud::GetGameTime() const +{ + int theGameTime = 0; + + if(this->mGameTime > 0) + { + theGameTime = (int)(this->mGameTime); + } + + return theGameTime; +} + +int AvHHud::GetGameTimeLimit() const +{ + return this->mTimeLimit; +} + +int AvHHud::GetCombatAttackingTeamNumber() const +{ + return this->mCombatAttackingTeamNumber; +} + +bool AvHHud::GetShowingMap() +{ + return sShowMap; +} + +bool AvHHud::GetGameStarted() const +{ + return (this->mGameTime >= 0) && !this->mGameEnded; +} + +bool AvHHud::GetIsAlive(bool inIncludeSpectating) const +{ + bool theIsAlive = false; + + cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); + if(inIncludeSpectating) + { + thePlayer = this->GetVisiblePlayer(); + } + + if(thePlayer) + { + int thePlayerIndex = thePlayer->index; + if((thePlayerIndex) && (thePlayerIndex <= MAX_PLAYERS)) + { + int thePlayerClass = g_PlayerExtraInfo[thePlayerIndex].playerclass; + switch(thePlayerClass) + { + case PLAYERCLASS_ALIVE_MARINE: + case PLAYERCLASS_ALIVE_HEAVY_MARINE: + case PLAYERCLASS_ALIVE_JETPACK_MARINE: + case PLAYERCLASS_ALIVE_LEVEL1: + case PLAYERCLASS_ALIVE_LEVEL2: + case PLAYERCLASS_ALIVE_LEVEL3: + case PLAYERCLASS_ALIVE_LEVEL4: + case PLAYERCLASS_ALIVE_LEVEL5: + case PLAYERCLASS_ALIVE_DIGESTING: + case PLAYERCLASS_ALIVE_GESTATING: + case PLAYERCLASS_COMMANDER: + theIsAlive = true; + } + } + } + + return theIsAlive; +} + +bool GetIsWithinRegion(float inNormX, float inNormY, float inLowX, float inLowY, float inHighX, float inHighY) +{ + bool inRegion = false; + + if((inNormX >= inLowX) && (inNormY >= inLowY) && (inNormX <= inHighX) && (inNormY < inHighY)) + { + inRegion = true; + } + + return inRegion; +} + +bool AvHHud::GetIsRegionBlockedByUI(float inNormX, float inNormY) +{ + bool theIsBlocked = true; + + if( GetIsWithinRegion(inNormX, inNormY, 0, .061, .3017, .6797) || + GetIsWithinRegion(inNormX, inNormY, .248, .0791, .7753, .6823) || + GetIsWithinRegion(inNormX, inNormY, .748, .092, 1, .6575) || + GetIsWithinRegion(inNormX, inNormY, .751, .645, .870, .678) || + GetIsWithinRegion(inNormX, inNormY, .337, .679, .729, .754) || + GetIsWithinRegion(inNormX, inNormY, .337, .717, .703, .823) ) + { + theIsBlocked = false; + + // Now check pending requests (the only HUD element not drawn as part of the outlying frame + for(PendingRequestListType::const_iterator theIterator = this->mPendingRequests.begin(); theIterator != this->mPendingRequests.end(); theIterator++) + { + AvHMessageID theMessageID = theIterator->first; + char theComponentName[256]; + sprintf(theComponentName, kPendingImpulseSpecifier, (int)theMessageID); + + AvHTechImpulsePanel* theTechImpulsePanel = NULL; + if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) + { + int thePosX, thePosY; + theTechImpulsePanel->getPos(thePosX, thePosY); + + int theWidth, theHeight; + theTechImpulsePanel->getSize(theWidth, theHeight); + + int theHighX = thePosX + theWidth; + int theHighY = thePosY + theHeight; + + float theScreenWidth = ScreenWidth(); + float theScreenHeight = ScreenHeight(); + + if(GetIsWithinRegion(inNormX, inNormY, thePosX/theScreenWidth, thePosY/theScreenHeight, theHighX/theScreenWidth, theHighY/theScreenHeight)) + { + theIsBlocked = true; + break; + } + } + } + } + + return theIsBlocked; +} + +bool AvHHud::GetIsShowingMap() const +{ + return sShowMap; +} + +void AvHHud::ClearSelection() +{ + gSelectionHelper.ClearSelection(); + this->mGroupEvent = COMMANDER_REMOVESELECTION; +} + + +void CLinkGhostBuildingCallback( struct tempent_s *ent, float frametime, float currenttime) +{ + gHUD.GhostBuildingCallback(ent, frametime, currenttime); +} + +// For easily adding message functions +#define BIND_MESSAGE(x) \ + int __MsgFunc_##x(const char *pszName, int iSize, void *pbuf) \ + { \ + return gHUD.##x(pszName, iSize, pbuf ); \ + } + +AvHHud::AvHHud(const string& inFilename, UIFactory* inFactory) : UIHud(inFilename, inFactory) +{ + this->ClearData(); + mSteamUIActive = false; +} + +void AvHHud::AddNumericalInfoMessage(float inOrigin[3], float inNumber, int inEventType) +{ + NumericalInfoEffect theEffect(inOrigin, inNumber, inEventType, this->mTimeOfLastUpdate); + this->mNumericalInfoEffects.push_back(theEffect); +} + +void AvHHud::AddTooltip(const char* inMessageText, bool inIsToolTip, float inTooltipWidth) +{ + if(!gEngfuncs.pDemoAPI->IsPlayingback() && (strlen(inMessageText) > 0)) + { + if(gEngfuncs.pfnGetCvarFloat(kvAutoHelp) || !inIsToolTip) + { + AvHTooltip theNewTooltip; + + theNewTooltip.SetText(string(inMessageText)); + theNewTooltip.SetNormalizedScreenX(1.0f - inTooltipWidth - kHelpMessageLeftEdgeInset); + theNewTooltip.SetNormalizedScreenY(0.01f); + theNewTooltip.SetCentered(false); + theNewTooltip.SetIgnoreFadeForLifetime(true); + theNewTooltip.SetNormalizedMaxWidth(inTooltipWidth); + + if(inIsToolTip) + { + this->PlayHUDSound(HUD_SOUND_TOOLTIP); + } + + this->mTooltips.push_back(theNewTooltip); + } + } +} + +bool AvHHud::AddTooltipOnce(const char* inMessageText, bool inIsToolTip) +{ + bool theAddedTooltip = false; + + string theMessage(inMessageText); + + // Check if message is in sent list + StringList::iterator theIter = std::find(this->mDisplayedToolTipList.begin(), this->mDisplayedToolTipList.end(), theMessage); + if(theIter == this->mDisplayedToolTipList.end()) + { + // If not + // Call AddTooltip + this->AddTooltip(inMessageText, inIsToolTip); + theAddedTooltip = true; + + // Add message to list + this->mDisplayedToolTipList.push_back(theMessage); + } + + return theAddedTooltip; +} + + +void AvHHud::Cancel(void) +{ + ASSERT(this->mInTopDownMode); + gCommanderHandler.CancelHit(); +} + +void AvHHud::ClearData() +{ + gParticleTemplateList.Clear(); + + this->mResources = 0; + + this->mHierarchy = NULL; + this->mShowMapHierarchy = NULL; + + this->mCommanderResourceLabel = NULL; + this->mGenericProgressBar = NULL; + this->mResearchProgressBar = NULL; + this->mAlienProgressBar = NULL; + this->mResearchLabel = NULL; + //this->mArmorLevel = ARMOR_BASE; + this->mTimeOfLastUpdate = 0.0; + this->mTimeOfNextHudSound = -1; + this->mLastHUDSoundPlayed = HUD_SOUND_INVALID; + this->mTimeOfLastEntityUpdate = -1; + this->mInTopDownMode = false; + this->mLeftMouseStarted = false; + this->mLeftMouseEnded = false; + this->mPlacingBuilding = false; + this->mRightMouseStarted = false; + this->mRightMouseEnded = false; + //this->mOrderMode = ORDERTYPE_UNDEFINED; + this->mTechEvent = MESSAGE_NULL; + this->mAlienAbility = MESSAGE_NULL; + this->mGroupEvent = MESSAGE_NULL; + this->mTrackingEntity = 0; + this->mNumLocalSelectEvents = 0; + + this->mSelected.clear(); + this->mSelectionJustChanged = false; + this->mMouseOneDown = false; + this->mMouseTwoDown = false; + this->mMouseOneStartX = 0; + this->mMouseOneStartY = 0; + this->mMouseTwoStartX = 0; + this->mMouseTwoStartY = 0; + + this->mMouseCursorX = this->mMouseCursorY = 0; + this->mPieMenuControl = ""; + + this->mPreviousHelpText = ""; + this->mTimeLastHelpTextChanged = -1; + this->mCurrentCursorFrame = 0; + + this->mMapExtents.ResetMapExtents(); + this->mMapName = ""; + + this->mGhostBuilding = MESSAGE_NULL; + this->mValidatedBuilding = MESSAGE_NULL; + this->mCreatedGhost = false; + this->mCurrentGhostIsValid = false; + + this->mAmbientSounds.clear(); + + // : 0000971 + this->mTeammateOrder.clear(); + this->mDisplayOrderIndex = 0; + this->mDisplayOrderTime = 0; + this->mDisplayOrderType = 0; + // : + + this->mProgressBarDrawframe = PROGRESS_BAR_DEFAULT; + this->mProgressBarLastDrawn = -10.0f; + + this->mHasWelder=false; + this->mHasMines=false; + this->mHasGrenades=false; + this->mNumSensory=0; + this->mNumMovement=0; + this->mNumDefense=0; +} + + +AvHHud::~AvHHud(void) +{ + //this->ResetGamma(); + //delete [] sOriginalGammaTable; + //delete [] sGammaTable; + AvHHud::ResetGammaAtExit(); +} + +void DummyFunction() +{ +} + +#ifdef DEBUG +int gGlobalDebugAuth = 0; +void TestIcon() +{ + gGlobalDebugAuth = rand() % 7; +} + +typedef struct alias_t { + alias_t* next; + char name[32]; + char* cmds; +} alias_s; + +void TestAlias() +{ + cmdalias_t* alias = gEngfuncs.pfnGetAliases();// *(alias_s**)0x02d29b7c; + while(alias) + { + gEngfuncs.Con_Printf("%s=%s\n%x - %x\n", alias->name, alias->value, alias, gEngfuncs); + alias = alias->next; + } +} +#endif + +// Used for console command +void AvHHud::PlayRandomSongHook() +{ + gHUD.PlayRandomSong(); +} + +void AvHHud::AddCommands() +{ + gEngfuncs.pfnAddCommand ("+popupmenu", AvHPieMenuHandler::OpenPieMenu); + gEngfuncs.pfnAddCommand ("-popupmenu", AvHPieMenuHandler::ClosePieMenu); + + gEngfuncs.pfnAddCommand ("+mousepopupmenu", AvHPieMenuHandler::OpenPieMenu); + gEngfuncs.pfnAddCommand ("-mousepopupmenu", AvHPieMenuHandler::ClosePieMenu); + + // Add scrolling commands + gEngfuncs.pfnAddCommand ("+scrollup", AvHScrollHandler::KeyScrollUp); + gEngfuncs.pfnAddCommand ("-scrollup", AvHScrollHandler::KeyScrollUpStop); + + gEngfuncs.pfnAddCommand ("+scrolldown", AvHScrollHandler::KeyScrollDown); + gEngfuncs.pfnAddCommand ("-scrolldown", AvHScrollHandler::KeyScrollDownStop); + + gEngfuncs.pfnAddCommand ("+scrollleft", AvHScrollHandler::KeyScrollLeft); + gEngfuncs.pfnAddCommand ("-scrollleft", AvHScrollHandler::KeyScrollLeftStop); + + gEngfuncs.pfnAddCommand ("+scrollright", AvHScrollHandler::KeyScrollRight); + gEngfuncs.pfnAddCommand ("-scrollright", AvHScrollHandler::KeyScrollRightStop); + + gEngfuncs.pfnAddCommand ("toggleeditps", AvHParticleEditorHandler::ToggleEdit); + + gEngfuncs.pfnAddCommand ("nexttrack", AvHHud::PlayRandomSongHook); + + gEngfuncs.pfnAddCommand ("+showmap", AvHHud::ShowMap); + gEngfuncs.pfnAddCommand ("-showmap", AvHHud::HideMap); + + gEngfuncs.pfnAddCommand ("+commandmenu", AvHHud::ShowCommandMenu); + gEngfuncs.pfnAddCommand ("-commandmenu", AvHHud::HideCommandMenu); + + gEngfuncs.pfnAddCommand ("playstream", AvHHud::PlayStream); + gEngfuncs.pfnAddCommand ("stopstream", AvHHud::StopStream); + + #ifdef DEBUG + gEngfuncs.pfnAddCommand("testicon", TestIcon); + gEngfuncs.pfnAddCommand("testalias", TestAlias); + #endif + + int i = 0; + char theBinding[128]; + for(i = (int)(RESOURCE_UPGRADE); i <= (int)(BUILD_RECYCLE); i++) + { + sprintf(theBinding, "%s%d", kHotKeyPrefix, i); + gEngfuncs.pfnAddCommand(theBinding, DummyFunction); + } + + for(i = (int)(MENU_BUILD); i <= (int)(MENU_EQUIP); i++) + { + sprintf(theBinding, "%s%d", kHotKeyPrefix, i); + gEngfuncs.pfnAddCommand(theBinding, DummyFunction); + } +} + +void AvHHud::ClientProcessEntity(struct entity_state_s* inEntity) +{ + // Check if we need to create or destroy particle systems + int theIndex = inEntity->number; + bool theParticleOn = inEntity->iuser3 == AVH_USER3_PARTICLE_ON; + bool theParticleOff = inEntity->iuser3 == AVH_USER3_PARTICLE_OFF; + if(theParticleOn || theParticleOff) + { + int theHandle = -1; + if(theParticleOn) + { + // Ent index and template index stored in fuser1 + int theValue = (int)(inEntity->fuser1); + int theGenEntIndex = (0xFFFF0000 & theValue) >> 16; + //int theTemplateIndex = (0x0000FFFF & theValue); + int theTemplateIndex = (((int(inEntity->fuser1)) & 0x0000FF00) >> 8); + + //int theTemplateIndex = theValue; + + // Handle stored in fuser2 + theHandle = (int)(inEntity->fuser2); + + // Don't create particle systems marked as high-detail if we don't have that option set. Note, this could cause collision + // differences between the client and server if the particle system doesn't use this flag with care + const AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(theTemplateIndex); + if(theTemplate) + { + if(!theTemplate->GetHighDetailOnly() || gEngfuncs.pfnGetCvarFloat(kvHighDetail)) + { + AvHParticleSystemManager::Instance()->CreateParticleSystemIfNotCreated(inEntity->number, theTemplateIndex, /*theEntIndex,*/ theHandle); + + // Update postion and visibility + if(theGenEntIndex > 0) + { + cl_entity_s* theGenEntity = gEngfuncs.GetEntityByIndex(theGenEntIndex); + if(theGenEntity) + { + AvHParticleSystemManager::Instance()->SetParticleSystemGenerationEntityExtents(theGenEntity->curstate.mins, theGenEntity->curstate.maxs, theHandle); + } + } + else + { + AvHParticleSystemManager::Instance()->SetParticleSystemPosition(inEntity->origin, theHandle); + } + + // Set the particle system custom data + //uint16 theCustomData = (uint16)(((int)inEntity->fuser3) >> 16); + //uint16 theCustomData = (uint16)(inEntity->fuser3); + uint16 theCustomData = (uint16)(inEntity->weaponmodel); + AvHParticleSystemManager::Instance()->SetParticleSystemCustomData(theCustomData, theHandle); + } + } + } + else if(theParticleOff) + { + theHandle = (int)(inEntity->fuser1); + //AvHParticleSystemManager::Instance()->DestroyParticleSystemIfNotDestroyed(inEntity->number, theHandle); + AvHParticleSystemManager::Instance()->MarkParticleSystemForDeletion(inEntity->number, theHandle); + } + + // Always update visibility + bool theVisibilityState = false; + if(this->GetInTopDownMode()) + { + if(GetHasUpgrade(inEntity->iuser4, MASK_VIS_SIGHTED)) + { + theVisibilityState = true; + } + else + { + theVisibilityState = false; + } + } + else + { + theVisibilityState = true; + } + + AvHParticleSystemManager::Instance()->SetParticleSystemVisibility(theVisibilityState, theHandle); + } + else if((inEntity->iuser3 == AVH_USER3_AUDIO_ON) || (inEntity->iuser3 == AVH_USER3_AUDIO_OFF)) + { + // Read values + int theEntIndex = (int)(inEntity->fuser1) >> 16; + //int theSoundIndex = (int)(inEntity->fuser1) & 0x0000FFFF; + + // memcpy so value isn't interpreted + //int theSoundIndex = 0; + //memcpy(&theSoundIndex, &inEntity->fuser1, sizeof(float)); + //theSoundIndex = theSoundIndex & 0x0000FFFF; + int theSoundIndex = (((int(inEntity->fuser1)) & 0x0000FF00) >> 8); + + // Top byte is flags, next byte is volume, bottom two bytes are fade distance + int theFlags = inEntity->iuser4 >> 24; + int theVolume = (inEntity->iuser4 >> 16) & 0x00FF; + int theFadeDistance = (inEntity->iuser4) & 0x0000FFFF; + + float theTimeOfAction = inEntity->fuser2; + + bool theSoundOn = (inEntity->iuser3 == AVH_USER3_AUDIO_ON); + + this->ModifyAmbientSoundEntryIfChanged(theSoundOn, theSoundIndex, theEntIndex, theTimeOfAction, theVolume, theFadeDistance, theFlags, inEntity->origin); + } +} + +string LookupAndTranslate(AvHMessageID inMessageID) +{ + string theKey = string(kTechNodeLabelPrefix) + MakeStringFromInt((int)inMessageID); + + string theTranslatedTechName; + LocalizeString(theKey.c_str(), theTranslatedTechName); + + return theTranslatedTechName; +} + +void AvHHud::DisplayCombatUpgradeMenu(bool inVisible) +{ + if(inVisible) + { + // Parse current tech nodes and set text + const AvHTechID kLineStart[kNumUpgradeLines] = {TECH_ONE_LEVEL_ONE, TECH_TWO_LEVEL_ONE, TECH_THREE_LEVEL_ONE, TECH_FOUR_LEVEL_ONE, TECH_FIVE_LEVEL_ONE}; + + // Add "you are now x"! + string theYouAreNow; + LocalizeString(kYouAreNowA, theYouAreNow); + + string theRankTitle = this->GetRankTitle(false); + + string theExclamation; + LocalizeString(kExclamation, theExclamation); + + string theChooseAnUpgrade; + LocalizeString(kChooseAnUpgrade, theChooseAnUpgrade); + + string theFinalText = theYouAreNow + string(" ") + theRankTitle + theExclamation + string("\n"); + theFinalText += theChooseAnUpgrade + string("\n\n"); + + // Set the lines + this->mCombatUpgradeMenu.SetText(theFinalText); + } + + // Parse text above every time, but only set position once so it doesn't keep animating + if(inVisible && !this->mDrawCombatUpgradeMenu) + { + // Start off screen, and scroll right + const float kWidth = .4f; + this->mCombatUpgradeMenu.SetNormalizedScreenX(-kWidth); + this->mCombatUpgradeMenu.SetNormalizedScreenY(.25f); + this->mCombatUpgradeMenu.SetNormalizedMaxWidth(kWidth); + } + + this->mDrawCombatUpgradeMenu = inVisible; +} + +void AvHHud::DisplayMessage(const char* inMessage) +{ + this->m_Message.MessageAdd(inMessage, this->m_flTime); + + // Remember the time -- to fix up level transitions + //this->m_Message.m_parms.time = this->m_flTime; + + // Turn on drawing + if ( !(this->m_Message.m_iFlags & HUD_ACTIVE) ) + this->m_Message.m_iFlags |= HUD_ACTIVE; +} + +//int AvHHud::GetArmorLevel(void) const +//{ +// return this->mArmorLevel; +//} + +int AvHHud::GetFrameForOrderType(AvHOrderType inOrderType) const +{ + int theFrame = 0; + + switch(inOrderType) + { + case ORDERTYPEL_DEFAULT: + theFrame = 2; + break; + + case ORDERTYPEL_MOVE: + theFrame = 2; + break; + + case ORDERTYPET_ATTACK: + theFrame = 4; + break; + + case ORDERTYPET_BUILD: + theFrame = 5; + break; + + case ORDERTYPET_GUARD: + theFrame = 6; + break; + + case ORDERTYPET_WELD: + theFrame = 7; + break; + + case ORDERTYPET_GET: + theFrame = 8; + break; + } + + return theFrame; +} + +AvHPlayMode AvHHud::GetPlayMode(void) const +{ + AvHPlayMode thePlayMode = PLAYMODE_UNDEFINED; + + cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); + if(thePlayer) + { + if(gEngfuncs.IsSpectateOnly()) + { + thePlayMode = PLAYMODE_OBSERVER; + } + else + { + thePlayMode = AvHPlayMode(thePlayer->curstate.playerclass); + } + } + + return thePlayMode; +} + +AvHPlayMode AvHHud::GetHUDPlayMode() const +{ + AvHPlayMode thePlayMode = this->GetPlayMode(); + + cl_entity_s* thePlayer = this->GetVisiblePlayer(); + if(thePlayer) + { + thePlayMode = AvHPlayMode(thePlayer->curstate.playerclass); + } + + return thePlayMode; +} + +cl_entity_s* AvHHud::GetVisiblePlayer() const +{ + cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); + if(g_iUser1 == OBS_IN_EYE) + { + cl_entity_t* theEnt = gEngfuncs.GetEntityByIndex(g_iUser2); + if(theEnt) + { + thePlayer = theEnt; + } + } + + return thePlayer; +} + +int AvHHud::GetLocalUpgrades() const +{ + static int theUpgrades = 0; + + cl_entity_s* thePlayer = this->GetVisiblePlayer(); + if(thePlayer) + { + theUpgrades = thePlayer->curstate.iuser4; + } + + return theUpgrades; +} + +// Players could hack their client dll and see all the orders on their team. Minor cheat but definitely possible. +EntityListType AvHHud::GetDrawPlayerOrders() const +{ + EntityListType theList; + + cl_entity_s* theVisiblePlayer = this->GetVisiblePlayer(); + if(theVisiblePlayer) + { + int theVisiblePlayerIndex = theVisiblePlayer->index; + + if(this->GetHUDUser3() == AVH_USER3_MARINE_PLAYER) + { + // Only draw orders for us + theList.push_back(theVisiblePlayerIndex); + } + else if(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) + { + // Add everyone that he has selected! + return this->mSelected; + } + } + + return theList; +} + +bool AvHHud::GetInTopDownMode() const +{ + return this->mInTopDownMode; +} + +bool AvHHud::GetIsSelecting() const +{ + return mSelectionBoxVisible; +} + +OrderListType AvHHud::GetOrderList() const +{ + return this->mOrders; +} + +//AvHOrderType AvHHud::GetOrderMode() const +//{ +// return this->mOrderMode; +//} + +bool AvHHud::GetCenterPositionForGroup(int inGroupNumber, vec3_t& outCenterPosition) const +{ + bool theSuccess = false; + + if((inGroupNumber >= 1) && (inGroupNumber <= kNumHotkeyGroups)) + { + vec3_t theCenterPosition; + VectorClear(theCenterPosition); + + int theNumFound = 0; + + const EntityListType& theGroup = this->mGroups[inGroupNumber - 1]; + if(theGroup.size() > 0) + { + for(EntityListType::const_iterator theIter = theGroup.begin(); theIter != theGroup.end(); theIter++) + { + int theEntIndex = *theIter; + + Vector thePosition; + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(theEntIndex); + if(theEntity) + { + thePosition = theEntity->curstate.origin; + } + + if(AvHSHUGetEntityLocation(theEntIndex, thePosition)) + { + theCenterPosition.x += thePosition.x; + theCenterPosition.y += thePosition.y; + theNumFound++; + } + } + + if(theNumFound > 0) + { + theCenterPosition.x /= theNumFound; + theCenterPosition.y /= theNumFound; + + outCenterPosition = theCenterPosition; + + theSuccess = true; + } + } + } + + return theSuccess; +} + +void AvHHud::GetMousePos(int& outX, int& outY) const +{ + gEngfuncs.GetMousePosition(&outX, &outY); + + // Clip mouse to window (weird) + outX = min(max(0, outX), ScreenWidth()); + outY = min(max(0, outY), ScreenHeight()); + + //char theMouseMessage[256]; + //sprintf(theMouseMessage, "Mouse coords: %d, %d", outX, outY); + //CenterPrint(theMouseMessage); +} + +bool AvHHud::GetAndClearTopDownScrollAmount(int& outX, int& outY, int& outZ) +{ + bool theSuccess = false; + outX = 0; + outY = 0; + outZ = 0; + + // Don't scroll if the the commander is dragging a selection box. + + if(this->GetInTopDownMode() && !GetIsSelecting()) + { + const int kScreenWidth = ScreenWidth(); + const int kScreenHeight = ScreenHeight(); + const kScrollHorizontal = .0152f*kScreenWidth; + const kScrollVertical = .015f*kScreenHeight; + + // Left side + if(this->GetIsMouseInRegion(0, 0, kScrollHorizontal, kScreenHeight) || (gScrollHandler.GetXScroll() < 0)) + { + outX = -1; + } + // Right side + else if(this->GetIsMouseInRegion(kScreenWidth - kScrollHorizontal, 0, kScrollHorizontal, kScreenHeight) || (gScrollHandler.GetXScroll() > 0)) + { + outX = 1; + } + + // Top edge + if(this->GetIsMouseInRegion(0, 0, kScreenWidth, kScrollVertical) || (gScrollHandler.GetYScroll() > 0)) + { + outY = 1; + } + // Bottom edge + else if(this->GetIsMouseInRegion(0, kScreenHeight - kScrollVertical, kScreenWidth, kScrollVertical) || (gScrollHandler.GetYScroll() < 0)) + { + outY = -1; + } + + // Only clear z scroll because of the way events work (invnext/invprev vs. holding a key down) + //gScrollHandler.ClearScrollHeight(); + + theSuccess = true; + } + + return theSuccess; +} + +bool AvHHud::GetAndClearSelectionEvent(vec3_t& outSelection, AvHMessageID& outMessageID) +{ + bool theSuccess = false; + + // Return a build event if there is one, else return a COMMANDER_MOUSECOORD event + if(this->mLeftMouseStarted) + { + if(this->mValidatedBuilding == MESSAGE_NULL) + { + VectorCopy(this->mLeftMouseWorldStart, outSelection); + outMessageID = COMMANDER_MOUSECOORD; + } + else + { + VectorCopy(this->mNormBuildLocation, outSelection); + outMessageID = this->mValidatedBuilding; + this->mValidatedBuilding = this->mGhostBuilding = MESSAGE_NULL; + this->mPlacingBuilding = true; + } + theSuccess = true; + } + else if(this->mLeftMouseEnded) + { + if(!this->mPlacingBuilding) + { + outSelection = this->mLeftMouseWorldEnd; + outMessageID = COMMANDER_MOUSECOORD; + theSuccess = true; + } + this->mPlacingBuilding = false; + } + else if(this->mRightMouseStarted) + { + // Cancel building placement + if(this->mGhostBuilding != MESSAGE_NULL) + { + CancelBuilding(); + } + else + { + outSelection = this->mRightMouseWorldStart; + outMessageID = COMMANDER_MOUSECOORD; + theSuccess = true; + } + } + else if(this->mRightMouseEnded) + { + outSelection = this->mRightMouseWorldEnd; + outMessageID = COMMANDER_MOUSECOORD; + theSuccess = true; + } + else + { + outSelection = this->mMouseWorldPosition; + outMessageID = COMMANDER_MOUSECOORD; + theSuccess = true; + } + + return theSuccess; +} + +EntityListType AvHHud::GetSelected() const +{ + return this->mSelected; +} + +const AvHTechSlotManager& AvHHud::GetTechSlotManager() const +{ + return this->mTechSlotManager; +} + +bool AvHHud::GetAndClearAlienAbility(AvHMessageID& outMessageID) +{ + bool theAlienAbilityWaiting = false; + + if(this->mAlienAbility != MESSAGE_NULL) + { + outMessageID = this->mAlienAbility; + theAlienAbilityWaiting = true; + this->mAlienAbility = MESSAGE_NULL; + } + + return theAlienAbilityWaiting; +} + +bool AvHHud::GetAndClearGroupEvent(AvHMessageID& outMessageID) +{ + bool theGroupEventWaiting = false; + + if(this->mGroupEvent != MESSAGE_NULL) + { + outMessageID = this->mGroupEvent; + theGroupEventWaiting = true; + +// if(!this->mIsTracking) +// { + this->mGroupEvent = MESSAGE_NULL; +// } + } + + return theGroupEventWaiting; +} + +int AvHHud::GetTrackingEntity() const +{ + return this->mTrackingEntity; +} + +void AvHHud::ClearTrackingEntity() +{ + this->mTrackingEntity = 0; +} + +void AvHHud::SetSelectionEffects(EntityListType& inUnitList) +{ + // Make sure we have an effect created for each unit in this list. If there are units that + // have selection effects that aren't in this list, delete them. This is called locally when the + // selection is predicted, then it's called again when the selection is confirmed. + this->mSelectionEffects.clear(); + + for(EntityListType::iterator theIter = inUnitList.begin(); theIter != inUnitList.end(); theIter++) + { + SelectionEffect theNewEffect; + theNewEffect.mEntIndex = *theIter; + theNewEffect.mAngleOffset = 0; + this->mSelectionEffects.push_back(theNewEffect); + } +} + +UIMode AvHHud::GetUIMode() const +{ + return this->mCurrentUIMode; +} + +bool AvHHud::SwitchUIMode(UIMode inNewMode) +{ + bool theSuccess = false; + + // Only allow switching to a non-main mode when we're in main, always allow switching back to main mode + if((inNewMode == MAIN_MODE) || (this->mCurrentUIMode == MAIN_MODE)) + { + if(inNewMode != this->mCurrentUIMode) + { + // Move pop-up menu components away or back so they don't block other compoments...ugh + if(inNewMode != MAIN_MODE) + { + gHUD.GetManager().TranslateComponent(kSoldierMenu, true); + gHUD.GetManager().TranslateComponent(kSoldierCombatMenu, true); + gHUD.GetManager().TranslateComponent(kAlienMenu, true); + gHUD.GetManager().TranslateComponent(kAlienCombatMenu, true); + gHUD.GetManager().TranslateComponent(kAlienMembrane, true); + gHUD.GetManager().TranslateComponent(kScroller, true); + gHUD.GetManager().TranslateComponent(kSelectionText, true); + //CenterPrint("Pop-up controls moved off screen"); + } + else + { + gHUD.GetManager().TranslateComponent(kSoldierMenu, false); + gHUD.GetManager().TranslateComponent(kSoldierCombatMenu, false); + gHUD.GetManager().TranslateComponent(kAlienMenu, false); + gHUD.GetManager().TranslateComponent(kAlienCombatMenu, false); + gHUD.GetManager().TranslateComponent(kAlienMembrane, false); + gHUD.GetManager().TranslateComponent(kScroller, false); + gHUD.GetManager().TranslateComponent(kSelectionText, false); + //CenterPrint("Pop-up controls moved on screen"); + } + + this->mCurrentUIMode = inNewMode; + } + + theSuccess = true; + } + + return theSuccess; +} + +bool AvHHud::Update(float inCurrentTime, string& outErrorString) +{ + bool theSuccess = false; + + if(inCurrentTime > this->mTimeOfLastUpdate) + { + + this->mTimeOfCurrentUpdate = inCurrentTime; + + // Predict game time + if(this->GetGameStarted()) + { + this->mGameTime += (inCurrentTime - this->mTimeOfLastUpdate); + } + + AvHParticleSystemManager::Instance()->Start(); + + // This component must always be visible to allow us to hook mouse cursor sprite drawing + this->ResetComponentsForUser3(); + + this->UpdateDataFromVuser4(inCurrentTime); + + this->UpdateSpectating(); + + this->GetManager().UnhideComponent(kLastComponent); + + this->UpdateDemoRecordPlayback(); + + theSuccess = UIHud::Update(inCurrentTime, outErrorString); + if(!theSuccess) + { + this->AddTooltip(outErrorString.c_str()); + } + + this->UpdateProgressBar(); + + this->UpdateCommonUI(); + + this->UpdateAlienUI(inCurrentTime); + + this->UpdateMarineUI(inCurrentTime); + + this->UpdateCountdown(inCurrentTime); + + this->UpdateExploitPrevention(); + this->UpdateFromEntities(inCurrentTime); + + this->UpdateEntityID(inCurrentTime); + + this->UpdateHelpText(); + + this->UpdateTooltips(inCurrentTime); + + this->UpdateStructureNotification(inCurrentTime); + + this->UpdateMusic(inCurrentTime); + + this->UpdatePieMenuControl(); + + // Reset cursor every tick, update selection may change it + // This cursor is used when we're on the ground for pie menus as well + this->SetCursor(ORDERTYPE_UNDEFINED); + + this->UpdateHierarchy(); + + this->UpdateInfoLocation(); + + if(this->GetInTopDownMode()) + { + this->UpdateSelection(); + gCommanderHandler.Update(this->mTechNodes, this->mResources); + +// char theDebugString[128]; +// sprintf(theDebugString, "norm X/Y: %f, %f", (float)this->mMouseCursorX/ScreenWidth, (float)this->mMouseCursorY/ScreenHeight); +// CenterPrint(theDebugString); + } + else + { + this->ResetTopDownUI(); + } + + // Update orders + //for(OrderListType::iterator theIter = this->mOrders.begin(); theIter != this->mOrders.end(); theIter++) + //{ + // theIter->Update(); + //} + + this->UpdateTechNodes(); + + this->UpdateAmbientSounds(); + + this->UpdateViewModelEffects(); + + mOverviewMap.UpdateOrders(mOrders, GetDrawPlayerOrders()); + mOverviewMap.Update(inCurrentTime); + + float theTimePassed = inCurrentTime - this->mTimeOfLastUpdate; + + AvHParticleSystemManager::Instance()->Update(theTimePassed); + + AvHScriptManager::Instance()->ClientUpdate(theTimePassed); + + this->UpdateResources(theTimePassed); + + if((this->GetHUDPlayMode() == PLAYMODE_PLAYING) && !this->GetIsAlive()) + { + this->mCommanderResourceLabel->setVisible(false); + this->mHierarchy->setVisible(false); + this->mShowMapHierarchy->setVisible(false); + } + + if(cl_particleinfo->value) + { + char theDebugText[128]; + int theNumVisible = AvHParticleSystemManager::Instance()->GetNumVisibleParticleSystems(); + int theNum = AvHParticleSystemManager::Instance()->GetNumberParticleSystems(); + int theNumTemplates = gParticleTemplateList.GetNumberTemplates(); + sprintf(theDebugText, "(vis, total, list): %d, %d, %d", theNumVisible, theNum, theNumTemplates); + + //sprintf(theDebugText, "step interval: %d", pmove->flTimeStepSound); + + /* + if(this->mMarineResourceLabel) + { + this->mMarineResourceLabel->setText(theDebugText); + this->mMarineResourceLabel->setVisible(true); + } + */ + + } + + if(!gEngfuncs.pDemoAPI->IsPlayingback()) + { + IN_GetMousePos(&this->mMouseCursorX, &this->mMouseCursorY); + } + + // Update user3 and team + this->mLastUser3 = this->GetHUDUser3(); + this->mLastTeamNumber = this->GetHUDTeam(); + this->mLastPlayMode = this->GetPlayMode(); + + this->mTimeOfLastUpdate = inCurrentTime; + + // Save view origin and angles before we do crazy viewport stuff + // gPlayerOrigin = v_origin; + // gPlayerAngles = v_angles; + } + + return theSuccess; +} + +//void AvHHud::UpdateSelectionEffects(float inTimePassed) +//{ +// // Radians/sec +// const float kSpinRate = 1.5f; +// for(SelectionListType::iterator theIter = this->mSelectionEffects.begin(); theIter != this->mSelectionEffects.end(); theIter++) +// { +// theIter->mAngleOffset = (theIter->mAngleOffset += inTimePassed*kSpinRate) % 360; +// } +//} + +bool AvHHud::GetAndClearTechEvent(AvHMessageID& outMessageID) +{ + bool theTechEventWaiting = false; + + if(this->mTechEvent != MESSAGE_NULL) + { + outMessageID = this->mTechEvent; + theTechEventWaiting = true; + this->mTechEvent = MESSAGE_NULL; + } + + return theTechEventWaiting; +} + +bool AvHHud::GetLastHotkeySelectionEvent(AvHMessageID& outMessageID) +{ + bool theSuccess = false; + + switch(this->mLastHotkeySelectionEvent) + { + case GROUP_SELECT_1: + case GROUP_SELECT_2: + case GROUP_SELECT_3: + case GROUP_SELECT_4: + case GROUP_SELECT_5: + outMessageID = this->mLastHotkeySelectionEvent; + theSuccess = true; + break; + } + + return theSuccess; +} + +void AvHHud::SetLastHotkeySelectionEvent(AvHMessageID inMessageID) +{ + switch(inMessageID) + { + case MESSAGE_NULL: + case GROUP_CREATE_1: + case GROUP_CREATE_2: + case GROUP_CREATE_3: + case GROUP_CREATE_4: + case GROUP_CREATE_5: + case GROUP_SELECT_1: + case GROUP_SELECT_2: + case GROUP_SELECT_3: + case GROUP_SELECT_4: + case GROUP_SELECT_5: + case COMMANDER_REMOVESELECTION: + this->mLastHotkeySelectionEvent = inMessageID; + break; + + default: + ASSERT(false); + break; + } +} + +bool AvHHud::GetIsAlien() const +{ + bool theIsAlien = false; + + AvHUser3 theUser3 = this->GetHUDUser3(); + + switch(theUser3) + { + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: + case AVH_USER3_ALIEN_EMBRYO: + theIsAlien = true; + break; + } + + return theIsAlien; +} + +bool AvHHud::GetIsBeingDigested() const +{ + bool theIsBeingDigested = false; + + int theUpgrades = this->GetHUDUpgrades(); + + if(GetHasUpgrade(theUpgrades, MASK_DIGESTING)) + { + cl_entity_t* theVisiblePlayer = this->GetVisiblePlayer(); + if(theVisiblePlayer && (theVisiblePlayer->curstate.effects & EF_NODRAW)) + { + theIsBeingDigested = true; + } + } + + return theIsBeingDigested; +} + +bool AvHHud::GetIsEnsnared() const +{ + int theUpgrades = this->GetHUDUpgrades(); + return GetHasUpgrade(theUpgrades, MASK_ENSNARED); +} + +bool AvHHud::GetIsStunned() const +{ + int theUpgrades = this->GetHUDUpgrades(); + return GetHasUpgrade(theUpgrades, MASK_PLAYER_STUNNED); +} + +bool AvHHud::GetIsDigesting() const +{ + bool theIsDigesting = false; + + int theUpgrades = this->GetHUDUpgrades(); + + if(GetHasUpgrade(theUpgrades, MASK_DIGESTING)) + { + cl_entity_t* theVisiblePlayer = this->GetVisiblePlayer(); + if(theVisiblePlayer && !(theVisiblePlayer->curstate.effects & EF_NODRAW)) + { + theIsDigesting = true; + } + } + + return theIsDigesting; +} + +bool AvHHud::GetIsNotInControl() const +{ + return GetIsBeingDigested() || !IEngineStudio.IsHardware(); +} + +bool AvHHud::GetIsInTopDownMode() const +{ + bool theIsInTopDownMode = false; + + if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_TOPDOWN)) + { + theIsInTopDownMode = true; + } + + return theIsInTopDownMode; +} + +int AvHHud::GetCommanderIndex() const +{ + int theCommanderIndex = -1; + + for(int i = 1; i <= MAX_PLAYERS; i++) + { + extra_player_info_t* theExtraPlayerInfo = &g_PlayerExtraInfo[i]; + ASSERT(theExtraPlayerInfo); + int thePlayerClass = theExtraPlayerInfo->playerclass; + if(thePlayerClass == PLAYERCLASS_COMMANDER) + { + theCommanderIndex = i; + break; + } + } + + return theCommanderIndex; +} + + +bool AvHHud::GetHasJetpack() const +{ + int theLocalUpgrades = this->GetHUDUpgrades(); + bool theHasJetpackUpgrade = GetHasUpgrade(theLocalUpgrades, MASK_UPGRADE_7) && this->GetIsMarine(); + + return theHasJetpackUpgrade; +} + +bool AvHHud::GetHasAlienUpgradesAvailable() const +{ + bool theHasUpgradesAvailable = false; + + if(this->GetIsAlien() && this->GetIsRelevant() && !this->GetIsBeingDigested()) + { + int theUpgradeVar = this->GetLocalUpgrades(); + bool theHasDefensiveUpgradesAvailable = AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_DEFENSE, this->mUpgrades, theUpgradeVar); + bool theHasMovementUpgradesAvailable = AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_MOVEMENT, this->mUpgrades, theUpgradeVar); + bool theHasSensoryUpgradesAvailable = AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_SENSORY, this->mUpgrades, theUpgradeVar); + + theHasUpgradesAvailable = theHasDefensiveUpgradesAvailable || theHasMovementUpgradesAvailable || theHasSensoryUpgradesAvailable; + } + + return theHasUpgradesAvailable; +} + +bool AvHHud::GetIsMarine() const +{ + bool theIsMarine = false; + + AvHUser3 theUser3 = this->GetHUDUser3(); + + switch(theUser3) + { + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_COMMANDER_PLAYER: + theIsMarine = true; + break; + } + + return theIsMarine; +} + +bool AvHHud::GetIsRelevant() const +{ + bool theIsRelevant = false; + + if(this->GetIsAlive() && (this->GetPlayMode() == PLAYMODE_PLAYING) /*&& !this->GetIsSpectator()*/) + { + theIsRelevant = true; + } + + return theIsRelevant; +} + +vec3_t AvHHud::GetVisualOrigin() const +{ + vec3_t theVisualOrigin = gPredictedPlayerOrigin; + + theVisualOrigin.z += gPredictedPlayerVOfs[2]; + + return theVisualOrigin; +} + +AvHMessageID AvHHud::HotKeyHit(char inChar) +{ + return gCommanderHandler.HotKeyHit(inChar); +} + +float AvHHud::GetGammaSlope() const +{ + return sGameGammaTable.GetGammaSlope(); +} + +string AvHHud::GetMapName(bool inLocalOnly) const +{ + string theMapName = this->mMapName; + + if((theMapName == "") && !inLocalOnly ) + { + const char* theLevelName = gEngfuncs.pfnGetLevelName(); + if(theLevelName) + { + theMapName = string(theLevelName); + + // Remove maps/ from the beginning and .bsp from the end. + StringVector theVector; + Tokenizer::split(theMapName, "/.", theVector); + if(theVector.size() >= 2) + { + theMapName = theVector[1]; + } + } + } + + return theMapName; +} + +int AvHHud::GetNumActiveHives() const +{ + int theNumActiveHives = 0; + + if(this->GetIsAlien()) + { + for(HiveInfoListType::const_iterator theIter = this->mHiveInfoList.begin(); theIter != this->mHiveInfoList.end(); theIter++) + { + if(theIter->mStatus == kHiveInfoStatusBuilt) + { + theNumActiveHives++; + } + } + } + + return theNumActiveHives; +} + +int AvHHud::GetMaxAlienResources() const +{ + int theMaxAlienResources = kMaxAlienResources; + + if(this->mMaxResources >= 0) + { + theMaxAlienResources = this->mMaxResources; + } + + return theMaxAlienResources; +} + +bool AvHHud::SetGamma(float inSlope) +{ + bool theSuccess = false; + + // Disable gamma stuff in debug for sanity +// #ifndef DEBUG + + HDC theDC = GetDC(NULL); + if(theDC != 0) + { + const float kGammaIncrement = 0.05f; + float theGammaToTry = inSlope + kGammaIncrement; + while(!theSuccess && (theGammaToTry > 1.0f)) + { + theGammaToTry -= kGammaIncrement; + + sGameGammaTable.ProcessSlope(theGammaToTry); + // : fakes a successful gamma ramp change if cl_gammaramp is set to 0 + if((CVAR_GET_FLOAT(kvGammaRamp) == 0) || sGameGammaTable.InitializeToVideoState()) + { + // Tell UI components so they can change shading to look the same + this->GetManager().NotifyGammaChange(theGammaToTry); + + // aww yeah + theSuccess = true; + } + } + + char theMessage[256]; + if(theSuccess) + { + sprintf(theMessage, "Gamma set to %f.", theGammaToTry); + } + else + { + sprintf(theMessage, "Display doesn't support downloadable gamma ramps."); + } + + if(!theSuccess || (gEngfuncs.GetMaxClients() == 1)) + { + CenterPrint(theMessage); + } + + if(!ReleaseDC(NULL, theDC)) + { + // emit error about leak + } + } + + //#endif + + return theSuccess; +} + +bool AvHHud::SlotInput(int inSlot) +{ + bool theHandled = false; + + if(this->mInTopDownMode) + { + if((inSlot >= 0) && (inSlot < kNumHotkeyGroups)) + { + // TODO: Read state of control/duck here + bool theCreateGroup = false; + + int theButtonBits = CL_ButtonBits(0); + if(theButtonBits & IN_DUCK) + { + theCreateGroup = true; + } + + int theBaseOffset = theCreateGroup ? GROUP_CREATE_1 : GROUP_SELECT_1; + + this->mGroupEvent = (AvHMessageID)(theBaseOffset + inSlot); + + theHandled = true; + } + } + + return theHandled; +} + +int AvHHud::Redraw( float flTime, int intermission ) +{ + + if (!gViewPort->IsOptionsMenuVisible() && + !gParticleEditorHandler.GetInEditMode()) + { + Render(); + } + + int theRC = UIHud::Redraw(flTime, intermission); + + return theRC; +} + +void AvHHud::ResetGammaAtExit() +{ + sPregameGammaTable.InitializeToVideoState(); +} + +int AvHHud::ResetGammaAtExitForOnExit() +{ + sPregameGammaTable.InitializeToVideoState(); + return TRUE; +} + +void AvHHud::ResetGammaAtExit(int inSig) +{ + AvHHud::ResetGammaAtExit(); +} + +void AvHHud::ResetTopDownUI() +{ + this->mGhostBuilding = MESSAGE_NULL; + this->mSelected.clear(); + this->mSelectionEffects.clear(); + + gCommanderHandler.Reset(); + + for(int i = 0; i < kNumHotkeyGroups; i++) + { + this->mGroups[i].clear(); + this->mGroupTypes[i] = AVH_USER3_NONE; + this->mGroupAlerts[i] = ALERT_NONE; + } + + this->mSelectAllGroup.clear(); +} + +void AvHHud::SetSelectingWeaponID(int inWeaponID, int inR, int inG, int inB) +{ + if(gEngfuncs.pfnGetCvarFloat(kvAutoHelp)) + { + if(inR != -1) + { + this->mHelpMessage.SetR(inR); + } + if(inG != -1) + { + this->mHelpMessage.SetG(inG); + } + if(inB != -1) + { + this->mHelpMessage.SetB(inB); + } + + this->mSelectingWeaponID = inWeaponID; + } +} + +void AvHHud::SetTechHelpText(const string& inTechHelpText) +{ + this->mTechHelpText = inTechHelpText; +} + +BIND_MESSAGE(Countdown); +int AvHHud::Countdown(const char* pszName, int iSize, void* pbuf) +{ + NetMsg_UpdateCountdown( pbuf, iSize, this->mNumTicksToPlay ); + this->mLastTickPlayed = 1; + this->mCountDownClock = this->m_flTime; + + return 1; +} + +bool AvHHud::GetAmbientSoundNameFromIndex(string& outSoundName, int inSoundIndex) const +{ + bool theFoundName = false; + + if(inSoundIndex < (int)(this->mSoundNameList.size())) + { + outSoundName = this->mSoundNameList[inSoundIndex]; + theFoundName = true; + } + + return theFoundName; +} + +void AvHHud::ModifyAmbientSoundEntryIfChanged(bool inSoundOn, int inSoundIndex, int inEntIndex, float inTimeStarted, int inVolume, int inFadeDistance, int inFlags, Vector inOrigin) +{ + bool theFoundSound = false; + + // Look up sound using inSoundIndex + string theSoundName; + if(this->GetAmbientSoundNameFromIndex(theSoundName, inSoundIndex)) + { + // Loop through current sounds + for(AmbientSoundListType::iterator theIter = this->mAmbientSounds.begin(); theIter != this->mAmbientSounds.end(); ) + { + bool theErasedSound = false; + if(theIter->GetEntityIndex() == inEntIndex) + { + // If found, remember that we found it + theFoundSound = true; + + // Set position + theIter->SetPosition(inOrigin); + + // If we're turning off sound, kill the sound + if(!inSoundOn) + { + theIter->ClearData(); + theIter = this->mAmbientSounds.erase(theIter); + theErasedSound = true; + } + } + if(!theErasedSound) + { + theIter++; + } + } + + // If we're turning a sound on, and we didn't find one + if(inSoundOn && !theFoundSound) + { + bool theLooping = inFlags & 2; + float theTimeElapsed = this->mTimeOfLastUpdate - inTimeStarted; + + // Add new entry with these values + this->mAmbientSounds.push_back(AvHAmbientSound(theSoundName, inVolume, inFadeDistance, theLooping, inOrigin, inEntIndex, theTimeElapsed)); + } + } + else + { + // We may not have the sound list yet, it's OK + //ASSERT(false); + } +} + +// : +void AvHHud::SetCenterText(const char* inText) +{ + LocalizeString(inText, this->mCenterText); + this->mCenterTextTime = this->mTimeOfLastUpdate; +} + +void AvHHud::ClearCenterText() +{ + this->mCenterText.clear(); + this->mCenterTextTime = -1; +} + +// : + +// Look at incoming order. If we are one of the receivers, play a HUD sound +// indicating our new order +void AvHHud::OrderNotification(const AvHOrder& inOrder) +{ + //if(!inOrder.GetOrderCompleted()) + //{ + // If we are commander, or we are in receiver list + int theLocalPlayer = gEngfuncs.GetLocalPlayer()->index; + if((this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) || (inOrder.GetHasReceiver(theLocalPlayer))) + { + // Do a switch on the order type + AvHOrderType theOrderType = inOrder.GetOrderType(); + AvHHUDSound theSound = HUD_SOUND_INVALID; + + // : 0000992 + // popup indicator for order + bool thePopup = false; + + // Play HUD sound depending on order + switch(theOrderType) + { + case ORDERTYPEL_MOVE: + theSound = HUD_SOUND_ORDER_MOVE; + thePopup = true; + break; + + case ORDERTYPET_ATTACK: + theSound = HUD_SOUND_ORDER_ATTACK; + thePopup = true; + break; + + case ORDERTYPET_BUILD: + theSound = HUD_SOUND_ORDER_BUILD; + thePopup = true; + break; + + case ORDERTYPET_GUARD: + theSound = HUD_SOUND_ORDER_GUARD; + thePopup = true; + break; + + case ORDERTYPET_WELD: + theSound = HUD_SOUND_ORDER_WELD; + thePopup = true; + break; + + case ORDERTYPET_GET: + theSound = HUD_SOUND_ORDER_GET; + thePopup = true; + break; + } + + if((this->GetHUDUser3() == AVH_USER3_MARINE_PLAYER) && (inOrder.GetOrderCompleted())) + { + theSound = HUD_SOUND_ORDER_COMPLETE; + } + + this->PlayHUDSound(theSound); + + // : 0000992 | 0001052 + if (thePopup && (this->GetInTopDownMode() == false) && (inOrder.GetOrderActive())) + { + this->SetDisplayOrder(2, this->GetFrameForOrderType(theOrderType), "", "", ""); + } + // : + } + //} +} + +void AvHHud::ResetComponentsForUser3() +{ + + this->mPieMenuControl = ""; + + this->GetManager().HideComponents(); + + if(gParticleEditorHandler.GetInEditMode()) + { + gHUD.GetManager().UnhideComponent(kPSESizeSlider); + gHUD.GetManager().UnhideComponent(kPSESizeLabel); + + gHUD.GetManager().UnhideComponent(kPSEScaleSlider); + gHUD.GetManager().UnhideComponent(kPSEScaleLabel); + + gHUD.GetManager().UnhideComponent(kPSEGenerationRateSlider); + gHUD.GetManager().UnhideComponent(kPSEGenerationRateLabel); + + gHUD.GetManager().UnhideComponent(kPSEParticleLifetimeSlider); + gHUD.GetManager().UnhideComponent(kPSEParticleLifetimeLabel); + + gHUD.GetManager().UnhideComponent(kPSEParticleSystemLifetimeSlider); + gHUD.GetManager().UnhideComponent(kPSEParticleSystemLifetimeLabel); + + gHUD.GetManager().UnhideComponent(kPSEMaxParticlesSlider); + gHUD.GetManager().UnhideComponent(kPSEMaxParticlesLabel); + + gHUD.GetManager().UnhideComponent(kPSEDrawModeSlider); + gHUD.GetManager().UnhideComponent(kPSEDrawModeLabel); + + gHUD.GetManager().UnhideComponent(PSEGenVelToggleSlider); + gHUD.GetManager().UnhideComponent(kPSEGenVelToggleLabel); + + gHUD.GetManager().UnhideComponent(kPSEGenVelShapeSlider); + gHUD.GetManager().UnhideComponent(kPSEGenVelShapeLabel); + + gHUD.GetManager().UnhideComponent(kPSEGenVelParamNumSlider); + gHUD.GetManager().UnhideComponent(kPSEGenVelParamNumLabel); + + gHUD.GetManager().UnhideComponent(kPSEGenVelParamValueSlider); + gHUD.GetManager().UnhideComponent(kPSEGenVelParamValueLabel); + } + else + { + bool theIsCombatMode = (this->mMapMode == MAP_MODE_CO); + bool theIsNSMode = (this->mMapMode == MAP_MODE_NS); + + if((this->GetHUDPlayMode() == PLAYMODE_PLAYING) && !this->GetIsNotInControl() && !gViewPort->IsOptionsMenuVisible()) + { + switch(this->GetHUDUser3()) + { + case AVH_USER3_MARINE_PLAYER: + if(theIsCombatMode) + { + this->mPieMenuControl = kSoldierCombatMenu; + } + else if(theIsNSMode) + { + this->mPieMenuControl = kSoldierMenu; + } + + if (g_iUser1 == OBS_NONE) + { + this->GetManager().UnhideComponent(mPieMenuControl.c_str()); + } + + // Removed these for recording footage until they look better + //this->GetManager().UnhideComponent(kReinforcementsLabel); + //this->GetManager().UnhideComponent(kResourceLabel); + //this->GetManager().UnhideComponent(kMouseCursorLabel); + //this->GetManager().UnhideComponent(kDebugCSPServerLabel); + //this->GetManager().UnhideComponent(kDebugCSPClientLabel); + break; + + case AVH_USER3_COMMANDER_PLAYER: + if(this->mInTopDownMode) + { + this->GetManager().UnhideComponent(kSelectionBox); + this->GetManager().UnhideComponent(kCommanderResourceLabel); + //this->GetManager().UnhideComponent(kMouseCursorLabel); + this->GetManager().UnhideComponent(kLeaveCommanderButton); + this->GetManager().UnhideComponent(kScroller); + this->GetManager().UnhideComponent(kTechHelpText); + this->GetManager().UnhideComponent(kHierarchy); + this->GetManager().UnhideComponent(kResearchBackgroundPanel); + this->GetManager().UnhideComponent(kActionButtonsComponents); + this->GetManager().UnhideComponent(kSelectAllImpulsePanel); + //this->GetManager().UnhideComponent(kTopDownHUDTopSpritePanel); + //this->GetManager().UnhideComponent(kTopDownHUDBottomSpritePanel); + } + //this->GetManager().UnhideComponent(kReinforcementsLabel); + break; + + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: + if(theIsCombatMode) + { + this->mPieMenuControl = kAlienCombatMenu; + } + else if(theIsNSMode) + { + this->mPieMenuControl = kAlienMenu; + } + + if (g_iUser1 == OBS_NONE) + { + this->GetManager().UnhideComponent(mPieMenuControl.c_str()); + } + + //this->GetManager().UnhideComponent(kMouseCursorLabel); + //this->GetManager().UnhideComponent(kDebugCSPServerLabel); + //this->GetManager().UnhideComponent(kDebugCSPClientLabel); + break; + + case AVH_USER3_ALIEN_EMBRYO: + //this->GetManager().UnhideComponent(kAlienMembrane); + break; + } + + if(sShowMap) + { + this->GetManager().UnhideComponent(kShowMapHierarchy); + } + + mOverviewMap.SetUser3(this->GetHUDUser3()); + + // Update command hierarchy so it can potentially display differently + if(this->mHierarchy) + { + this->mHierarchy->setPos(.0105f*ScreenWidth(), .728*ScreenHeight()); + this->mHierarchy->setSize(.265*ScreenWidth(), .247*ScreenHeight()); + } + + } + } +} + +BIND_MESSAGE(BalanceVar); +int AvHHud::BalanceVar(const char* pszName, int iSize, void* pbuf) +{ + string name; + BalanceMessageAction action; + int ivalue; + float fvalue; + string svalue; + NetMsg_BalanceVar( pbuf, iSize, name, action, ivalue, fvalue, svalue ); + BalanceValueContainer* container = BalanceValueContainerFactory::get(); + + switch( action ) + { + case BALANCE_ACTION_INSERT_INT: + container->insert(name,ivalue); + break; + case BALANCE_ACTION_INSERT_FLOAT: + container->insert(name,fvalue); + break; + case BALANCE_ACTION_INSERT_STRING: + container->insert(name,svalue); + break; + case BALANCE_ACTION_REMOVE: + container->remove(name); + break; + case BALANCE_ACTION_CLEAR: + container->clear(); + break; + } + return 1; +} + +BIND_MESSAGE(GameStatus); +int AvHHud::GameStatus(const char* pszName, int iSize, void* pbuf) +{ + int status, game_time, timelimit, misc_data; + AvHMapMode map_mode; + NetMsg_GameStatus( pbuf, iSize, status, map_mode, game_time, timelimit, misc_data ); + + this->mMapMode = map_mode; + + switch( status ) + { + case kGameStatusReset: + case kGameStatusResetNewMap: + if(this->mInTopDownMode) + { + this->ToggleMouse(); + } + + this->ResetGame( status == kGameStatusResetNewMap ? true : false ); + break; + case kGameStatusEnded: // Victor determined, but we are still in the cooldown time + this->mGameEnded = true; // Stop research + break; + case kGameStatusGameTime: + this->mGameTime = game_time; + this->mTimeLimit = timelimit; + this->mCombatAttackingTeamNumber = misc_data; + break; + case kGameStatusUnspentLevels: + this->mExperienceLevelSpent = misc_data; + break; + } + + return 1; +} + +BIND_MESSAGE(MiniMap); +int AvHHud::MiniMap(const char* pszName, int iSize, void* pbuf) +{ + gMiniMap.ReceiveFromNetworkStream( pbuf, iSize ); + return 1; +} + +// : 0000971 +BIND_MESSAGE(IssueOrder); +int AvHHud::IssueOrder(const char* pszName, int iSize, void* pbuf) +{ + int ordertype, ordersource, ordertarget; + NetMsg_IssueOrder( pbuf, iSize, ordertype, ordersource, ordertarget); + + float now = this->GetTimeOfLastUpdate(); + TeammateOrderListType::iterator theIter = this->mTeammateOrder.find(ordersource); + if (theIter == this->mTeammateOrder.end()) + { + this->mTeammateOrder.insert(theIter, pair(ordersource, TeammateOrderType(ordertype, now))); + } + else + { + TeammateOrderType *theOrder = &((*theIter).second); + (*theOrder).first = ordertype; + (*theOrder).second = now; + } + + if (this->GetInTopDownMode() == false) + { + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + if (theLocalPlayer->index == ordertarget) + { + hud_player_info_t info; + memset(&info, 0, sizeof(info)); + GetPlayerInfo(ordersource, &info); + + string temp; + string nameFormat; + // fetch from titles.txt + sprintf(temp, "TeammateOrder%d", ordertype); + LocalizeString(temp.c_str(), nameFormat); + sprintf(temp, nameFormat.c_str(), info.name); + + this->SetDisplayOrder(1, ordertype, temp, "", ""); + } + if (theLocalPlayer->index == ordersource) + { + this->mCurrentOrderTarget = ordertarget; + this->mCurrentOrderType = ordertype; + this->mCurrentOrderTime = now; + } + } + + return 1; +} +// : + +BIND_MESSAGE(ServerVar); +int AvHHud::ServerVar(const char* pszName, int iSize, void* pbuf) +{ + string name; + int value; + NetMsg_ServerVar( pbuf, iSize, name, value ); + mServerVariableMap[name] = value; + return 1; +} + +BIND_MESSAGE(Progress); +int AvHHud::Progress(const char* pszName, int iSize, void* pbuf) +{ + NetMsg_ProgressBar( pbuf, iSize, this->mProgressBarEntityIndex, this->mProgressBarParam, this->mProgressBarCompleted ); + return 1; +} + +// Start the game over. Called after the game ends and also after a map change +void AvHHud::ResetGame(bool inMapChanged) +{ + UIHud::ResetGame(); + + this->mResources = 0; + this->mMaxResources = -1; + this->mVisualResources = 0; + this->mUser2OfLastResourceMessage = 0; + this->mTimeOfLastEntityUpdate = -1; + this->mVisualEnergyLevel = 0; + this->mUser2OfLastEnergyLevel = 0; + + // Don't use a menu yet + //this->ResetUpgradeCosts(); + + // Reset armor as well. + //this->mArmorLevel = ARMOR_BASE; + + // Clear out all particle systems and templates + if(inMapChanged || gEngfuncs.IsSpectateOnly() ) + { + gParticleTemplateList.Clear(); + this->mTimeOfLastUpdate = 0.0f; + this->mInfoLocationList.clear(); + this->mMapName=""; + } + + // : 1066 reset overview map on game restart + gHUD.GetOverviewMap().Clear(); + + AvHParticleSystemManager::Instance()->Reset(); + + this->mTechSlotManager.Clear(); + + this->mTechNodes.Clear(); + + this->mTimeOfCurrentUpdate = 0.0f; + + // On game reset, clear blips (happens on server as well) + + this->mEntityHierarchy.Clear(); + + // Clear selection effects + this->mSelectionEffects.clear(); + + // End any jetpack effects + //EndJetpackEffects(); + + // Clear client scripts + AvHScriptManager::Instance()->Reset(); + + // Selection and commander variables + this->mNumLocalSelectEvents = 0; + // Removed to allow map to be shown before gamestart. + // The map-mode will be re-set by the Gamestate messages anyway. + this->mMapMode = MAP_MODE_UNDEFINED; + this->mInTopDownMode = false; + this->mLeftMouseStarted = false; + this->mLeftMouseEnded = false; + this->mPlacingBuilding = false; + + sShowMap = false; + + this->StopMusic(); + + for(AmbientSoundListType::iterator theIter = this->mAmbientSounds.begin(); theIter != this->mAmbientSounds.end(); theIter++) + { + theIter->ClearData(); + } + this->mAmbientSounds.clear(); + + this->SetReinforcements(0); + + this->mOrders.clear(); + + this->mCurrentCursorFrame = 0; + this->mProgressBarEntityIndex = -1; + this->mProgressBarParam = -1; + this->mProgressBarCompleted = -1; + + this->mEnemyBlips.Clear(); + this->mFriendlyBlips.Clear(); + + // Reset view angles (in case player was in commander mode) + gViewAngles.z = 0.0f; + gResetViewAngles = true; + + // Clear location + this->mLocationText = ""; + + this->mUpgrades.clear(); + + this->mNumUpgradesAvailable = 0; + int i; + for(i = 0; i < ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE; i++) + { + this->mCurrentUpgradeCategory[i] = ALIEN_UPGRADE_CATEGORY_INVALID; + } + + // Remove all decals (no idea how to get the total decals) + //for(i = 0; i < 1024; i++) + //{ + // gEngfuncs.pEfxAPI->R_DecalRemoveAll(i); + //} + RemoveAllDecals(); + + // Remove temp ghost building + if(this->mLastGhostBuilding) + { + this->mLastGhostBuilding->die = -1; + } + + this->mNumericalInfoEffects.clear(); + this->mTimeOfNextHudSound = -1; + this->mLastHUDSoundPlayed = HUD_SOUND_INVALID; + + //this->mTooltips.clear(); + + this->mHiveInfoList.clear(); + + this->mDesiredGammaSlope = kDefaultMapGamma; + this->mRecordingLastFrame = false; + this->mTimeOfLastHelpText = -1; + this->mDisplayedToolTipList.clear(); + this->mCurrentWeaponID = -1; + this->mCurrentWeaponEnabled = false; + + // Is this needed? + //this->mCurrentUIMode = MAIN_MODE; + + this->mMenuTechSlots = 0; + this->mPendingRequests.clear(); + + for(i = 0; i < kNumHotkeyGroups; i++) + { + this->mGroups[i].clear(); + this->mGroupTypes[i] = AVH_USER3_NONE; + this->mGroupAlerts[i] = ALERT_NONE; + } + this->mSelectAllGroup.clear(); + + this->mCurrentSquad = 0; + this->mBlinkingAlertType = 0; + + this->mLastTeamSpectated = TEAM_IND; + + this->mStructureNotificationList.clear(); + + this->mGameTime = -1; + this->mTimeLimit = -1; + this->mCombatAttackingTeamNumber = 0; + this->mGameEnded = false; + + this->mExperience = 0; + this->mExperienceLevel = 1; + this->mExperienceLevelLastDrawn = 1; + this->mExperienceLevelSpent = 0; + this->mTimeOfLastLevelUp = -1; + + memset(this->mMenuImpulses, MESSAGE_NULL, sizeof(AvHMessageID)*kNumUpgradeLines); + + // : 0000992 & 0000971 + this->mTeammateOrder.clear(); + this->mCurrentOrderTarget = 0; + this->mCurrentOrderType = 0; + this->mCurrentOrderTime = 0.0f; + + this->mDisplayOrderTime = 0.0f; + this->mDisplayOrderType = 0; + this->mDisplayOrderIndex = 0; + this->mDisplayOrderText1 = ""; + this->mDisplayOrderText2 = ""; + this->mDisplayOrderText3 = ""; + + this->mCenterText.clear(); + this->mCenterTextTime = -1; + // : + + this->mProgressBarLastDrawn = -10.0f; + + this->mHasGrenades=false; + this->mHasMines=false; + this->mHasWelder=false; + this->mNumSensory=0; + this->mNumMovement=0; + this->mNumDefense=0; + if ( this->mCrosshairShowCount != 1 ) { + this->mCrosshairShowCount = 1; + } +} + +BIND_MESSAGE(SetGmma); +int AvHHud::SetGmma(const char* pszName, int iSize, void* pbuf) +{ + NetMsg_SetGammaRamp( pbuf, iSize, this->mDesiredGammaSlope ); + if (!mSteamUIActive) + { + this->SetGamma(this->mDesiredGammaSlope); + } + + return 1; +} + +BIND_MESSAGE(BlipList); +int AvHHud::BlipList(const char* pszName, int iSize, void* pbuf) +{ + bool friendly_blips; + AvHVisibleBlipList list; + NetMsg_BlipList( pbuf, iSize, friendly_blips, list ); + + float theCurrentTime = gEngfuncs.GetClientTime(); + + if( friendly_blips ) + { + this->mFriendlyBlips.Clear(); + this->mFriendlyBlips.AddBlipList(list); + this->mFriendlyBlips.SetTimeBlipsReceived(theCurrentTime); + } + else + { + this->mEnemyBlips.Clear(); + this->mEnemyBlips.AddBlipList(list); + this->mEnemyBlips.SetTimeBlipsReceived(theCurrentTime); + } + return 1; +} + +BIND_MESSAGE(ClScript); +int AvHHud::ClScript(const char *pszName, int iSize, void *pbuf) +{ + StringList script_names; + NetMsg_ClientScripts( pbuf, iSize, script_names ); + StringList::iterator current, end = script_names.end(); + for( current = script_names.begin(); current != end; ++current ) + { + AvHScriptManager::Instance()->RunScript(current->c_str()); + } + + return 1; +} + +BIND_MESSAGE(DelParts); +int AvHHud::DelParts(const char *pszName, int iSize, void *pbuf) +{ + NetMsg_DelParts( pbuf, iSize ); + gParticleTemplateList.Clear(); + + return 1; +} + +BIND_MESSAGE(Particles); +int AvHHud::Particles(const char *pszName, int iSize, void *pbuf) +{ + int index=-1; + AvHParticleTemplate particle_template; + NetMsg_SetParticleTemplate( pbuf, iSize, index, particle_template ); + gParticleTemplateList.Insert( particle_template, index ); + + return 1; +} + +BIND_MESSAGE(SoundNames); +int AvHHud::SoundNames(const char *pszName, int iSize, void *pbuf) +{ + bool theClearSoundList; + string sound_name; + NetMsg_SetSoundNames( pbuf, iSize, theClearSoundList, sound_name ); + + if(theClearSoundList) + { + this->mSoundNameList.clear(); + } + else + { + this->mSoundNameList.push_back(sound_name); + } + + return 1; +} + +BIND_MESSAGE(SetSelect); +int AvHHud::SetSelect(const char* pszName, int iSize, void* pbuf) +{ + Selection selection; + NetMsg_SetSelect( pbuf, iSize, selection ); + + EntityListType theGroup; + + switch( selection.group_number ) + { + case 0: + this->mTrackingEntity = selection.tracking_entity; + if( selection.selected_entities != this->mSelected ) + { + this->mSelectionJustChanged = true; + this->mSelected = selection.selected_entities; + } + break; + case kSelectAllHotGroup: + this->mSelectAllGroup = selection.selected_entities; + break; + default: + this->mGroups[selection.group_number-1] = selection.selected_entities; + this->mGroupTypes[selection.group_number-1] = selection.group_type; + this->mGroupAlerts[selection.group_number-1] = selection.group_alert; + } + + return 1; +} + +BIND_MESSAGE(SetRequest); +int AvHHud::SetRequest(const char* pszName, int iSize, void* pbuf) +{ + int request_type, request_count; + NetMsg_SetRequest( pbuf, iSize, request_type, request_count ); + this->mPendingRequests[(AvHMessageID)request_type] = request_count; + + return 1; +} + +BIND_MESSAGE(SetOrder); +int AvHHud::SetOrder(const char* pszName, int iSize, void* pbuf) +{ + AvHOrder theNewOrder; + NetMsg_SetOrder( pbuf, iSize, theNewOrder ); + + AvHChangeOrder(this->mOrders, theNewOrder); + + // Give feedback on order + this->OrderNotification(theNewOrder); + + // Run through orders, deleting any that are complete + for(OrderListType::iterator theIter = this->mOrders.begin(); theIter != this->mOrders.end(); /* no inc */) + { + if(theIter->GetOrderCompleted()) + { + this->mOrders.erase(theIter); + } + else + { + theIter++; + } + } + + return 1; +} + +BIND_MESSAGE(SetupMap); +int AvHHud::SetupMap(const char* pszName, int iSize, void* pbuf) +{ + bool is_location, draw_background; + float min_extents[3], max_extents[3]; + string name; + NetMsg_SetupMap( pbuf, iSize, is_location, name, min_extents, max_extents, draw_background ); + + if( is_location ) + { + vec3_t theMaxExtent; + theMaxExtent.x = max_extents[0]; + theMaxExtent.y = max_extents[1]; + theMaxExtent.z = max_extents[2]; + vec3_t theMinExtent; + theMinExtent.x = min_extents[0]; + theMinExtent.y = min_extents[1]; + theMinExtent.z = min_extents[2]; + + this->mInfoLocationList.push_back( AvHBaseInfoLocation( name, theMaxExtent, theMinExtent ) ); + } + else + { + this->mMapName = name; + this->mMapExtents.SetMaxMapX( max_extents[0] ); + this->mMapExtents.SetMaxMapY( max_extents[1] ); + this->mMapExtents.SetMaxViewHeight( max_extents[2] ); + this->mMapExtents.SetMinMapX( min_extents[0] ); + this->mMapExtents.SetMinMapY( min_extents[1] ); + this->mMapExtents.SetMinViewHeight( min_extents[2] ); + this->mMapExtents.SetDrawMapBG( draw_background ); + if ( gEngfuncs.IsSpectateOnly() ) + this->mOverviewMap.SetMapExtents(this->GetMapName(), this->mMapExtents); + } + return 1; +} + + +BIND_MESSAGE(SetTopDown); +int AvHHud::SetTopDown(const char* pszName, int iSize, void* pbuf) +{ + bool is_menu_tech, is_top_down; + float position[3]; + int tech_slots; + NetMsg_SetTopDown( pbuf, iSize, is_menu_tech, is_top_down, position, tech_slots ); + + if(is_menu_tech) + { + this->mMenuTechSlots = tech_slots; + } + else + { + if(this->mInTopDownMode && !is_top_down) + { + // Switch away from top down mode + this->mInTopDownMode = false; + this->ToggleMouse(); + + // Reset angles + gViewAngles[0] = position[0]; + gViewAngles[1] = position[1]; + gViewAngles[2] = position[2]; + + gResetViewAngles = true; + + this->mSelectionEffects.clear(); + } + else if(!this->mInTopDownMode && is_top_down) + { + // Switch to top down mode! + this->mInTopDownMode = true; + this->ShowMouse(); + } + + if(is_top_down) + { + // Read new PAS + this->mCommanderPAS.x = position[0]; + this->mCommanderPAS.y = position[1]; + this->mCommanderPAS.z = position[2]; + } + } + + return 1; +} + +BIND_MESSAGE(DelEntHier); +int AvHHud::DelEntHier(const char *pszName, int iSize, void *pbuf) { + NetMsg_DelEntityHierarchy(pbuf, iSize); + this->mEntityHierarchy.Clear(); + return 0; +} + +BIND_MESSAGE(EntHier); +int AvHHud::EntHier(const char *pszName, int iSize, void *pbuf) +{ + MapEntityMap new_items; + EntityListType old_items; + NetMsg_UpdateEntityHierarchy( pbuf, iSize, new_items, old_items ); + + MapEntityMap::iterator current, end = new_items.end(); + for( current = new_items.begin(); current != end; ++current ) + { + this->mEntityHierarchy.InsertEntity( current->first, current->second ); + } + + EntityListType::iterator d_current, d_end = old_items.end(); + for( d_current = old_items.begin(); d_current != d_end; ++d_current ) + { + this->mEntityHierarchy.DeleteEntity( *d_current ); + } + + return 0; +} + +BIND_MESSAGE(EditPS); +int AvHHud::EditPS(const char* pszName, int iSize, void* pbuf) +{ + int particle_index; + NetMsg_EditPS( pbuf, iSize, particle_index ); + AvHParticleEditorHandler::SetEditIndex((uint32)particle_index); + + return 1; +} + +BIND_MESSAGE(Fog); +int AvHHud::Fog(const char* pszName, int iSize, void* pbuf) +{ + bool enabled; + int R, G, B; + float start, end; + NetMsg_Fog( pbuf, iSize, enabled, R, G, B, start, end ); + + this->mFogActive = enabled; + if(enabled) + { + this->mFogColor.x = R; + this->mFogColor.y = G; + this->mFogColor.z = B; + this->mFogStart = start; + this->mFogEnd = end; + } + + return 1; +} + + +BIND_MESSAGE(SetUpgrades); +int AvHHud::SetUpgrades(const char* pszName, int iSize, void* pbuf) +{ + int mask; + NetMsg_HUDSetUpgrades( pbuf, iSize, mask ); + // Aliens + if ( mask & 0x80 ) { + this->mNumMovement=mask & 0x3; + mask >>=2; + this->mNumDefense=mask & 0x3; + mask >>=2; + this->mNumSensory=mask & 0x3; + } + // Marines + else { + this->mHasWelder=mask & 0x1; + this->mHasMines=mask & 0x2; + this->mHasGrenades=mask & 0x4; + } + return 1; +} + + +BIND_MESSAGE(ListPS); +int AvHHud::ListPS(const char* pszName, int iSize, void* pbuf) +{ + string name; + NetMsg_ListPS( pbuf, iSize, name ); + this->m_SayText.SayTextPrint(name.c_str(), 256, 0); + + return 1; +} + +BIND_MESSAGE(PlayHUDNot); +int AvHHud::PlayHUDNot(const char* pszName, int iSize, void* pbuf) +{ + int message_id, sound; + float location_x, location_y; + NetMsg_PlayHUDNotification( pbuf, iSize, message_id, sound, location_x, location_y ); + + if(message_id == 0) + { + // Hack to avoid adding another network message (at max) + if(!this->GetInTopDownMode()) + { + switch(sound) + { + case HUD_SOUND_SQUAD1: + this->mCurrentSquad = 1; + break; + case HUD_SOUND_SQUAD2: + this->mCurrentSquad = 2; + break; + case HUD_SOUND_SQUAD3: + this->mCurrentSquad = 3; + break; + case HUD_SOUND_SQUAD4: + this->mCurrentSquad = 4; + break; + case HUD_SOUND_SQUAD5: + this->mCurrentSquad = 5; + break; + } + } + else + { + switch((AvHHUDSound)sound) + { + // Danger + case HUD_SOUND_MARINE_SOLDIER_UNDER_ATTACK: + case HUD_SOUND_MARINE_BASE_UNDER_ATTACK: + case HUD_SOUND_MARINE_SENTRYDAMAGED: + case HUD_SOUND_MARINE_SOLDIERLOST: + case HUD_SOUND_MARINE_CCUNDERATTACK: + this->mBlinkingAlertType = 2; + AddMiniMapAlert(location_x, location_y); + break; + + // Just research or something like that + case HUD_SOUND_MARINE_UPGRADE_COMPLETE: + case HUD_SOUND_MARINE_RESEARCHCOMPLETE: + this->mBlinkingAlertType = 1; + AddMiniMapAlert(location_x, location_y); + break; + } + } + + this->PlayHUDSound((AvHHUDSound)sound); + } + else + { + // Push back icon + HUDNotificationType theNotification; + theNotification.mStructureID = (AvHMessageID)sound; //message_id; + theNotification.mTime = this->mTimeOfCurrentUpdate; + theNotification.mLocation = Vector(location_x, location_y, 0.0f); + + if(CVAR_GET_FLOAT(kvBuildMessages)) + { + this->mStructureNotificationList.push_back(theNotification); + } + } + + return 1; +} + +BIND_MESSAGE(AlienInfo); +int AvHHud::AlienInfo(const char* pszName, int iSize, void* pbuf) +{ + bool was_hive_info; + AvHAlienUpgradeListType upgrades; + HiveInfoListType hives; + NetMsg_AlienInfo( pbuf, iSize, was_hive_info, this->mUpgrades, this->mHiveInfoList ); + return 1; +} + +void AvHHud::PlayHUDSound(const char *szSound, float vol, float inSoundLength) +{ + if((this->mTimeOfNextHudSound == -1) || (this->mTimeOfCurrentUpdate > this->mTimeOfNextHudSound)) + { + if(szSound) + { + char theSoundName[512]; + strcpy(theSoundName, szSound); + gEngfuncs.pfnPlaySoundByName(theSoundName, vol); + this->mTimeOfNextHudSound = this->mTimeOfCurrentUpdate + inSoundLength; + } + } +} + +void AvHHud::PlayHUDSound(int iSound, float vol, float inSoundLength) +{ + if((this->mTimeOfNextHudSound == -1) || (this->mTimeOfCurrentUpdate > this->mTimeOfNextHudSound)) + { + gEngfuncs.pfnPlaySoundByIndex(iSound, vol); + this->mTimeOfNextHudSound = this->mTimeOfCurrentUpdate + inSoundLength; + } +} + +void AvHHud::PlayHUDSound(AvHHUDSound inSound) +{ + char* theSoundPtr = NULL; + float theVolume = 1.0f; + + // Some sounds are forced, but don't allow them to be spammed or cut themselves off + bool theForceSound = AvHSHUGetForceHUDSound(inSound) && (inSound != this->mLastHUDSoundPlayed); + + // : 0000407 + bool theAutoHelpEnabled = gEngfuncs.pfnGetCvarFloat(kvAutoHelp); + // : + + if((this->mTimeOfNextHudSound == -1) || (this->mTimeOfCurrentUpdate >= this->mTimeOfNextHudSound) || theForceSound) + { + float theSoundLength = 2.0f; + + switch(inSound) + { + case HUD_SOUND_POINTS_SPENT: + theSoundPtr = kPointsSpentSound; + theSoundLength = .2f; + break; + case HUD_SOUND_COUNTDOWN: + theSoundPtr = kCountdownSound; + theSoundLength = 0.0f; + break; + case HUD_SOUND_SELECT: + if(gHUD.GetIsAlien()) + theSoundPtr = kSelectAlienSound; + else + theSoundPtr = kSelectSound; + + // Set to 0 so it never blocks other sounds + theVolume = .2f; + theSoundLength = 0.0f; + break; + case HUD_SOUND_SQUAD1: + if(this->GetInTopDownMode()) + { + theSoundPtr = kSquad1Sound; + theSoundLength = 1.2f; + } + else + { + theSoundPtr = kMarineSquad1Sound; + theSoundLength = 2.0f; + } + break; + case HUD_SOUND_SQUAD2: + if(this->GetInTopDownMode()) + { + theSoundPtr = kSquad2Sound; + theSoundLength = 1.2f; + } + else + { + theSoundPtr = kMarineSquad2Sound; + theSoundLength = 2.0f; + } + break; + case HUD_SOUND_SQUAD3: + if(this->GetInTopDownMode()) + { + theSoundPtr = kSquad3Sound; + theSoundLength = 1.2f; + } + else + { + theSoundPtr = kMarineSquad3Sound; + theSoundLength = 2.0f; + } + break; + case HUD_SOUND_SQUAD4: + if(this->GetInTopDownMode()) + { + theSoundPtr = kSquad4Sound; + theSoundLength = 1.2f; + } + else + { + theSoundPtr = kMarineSquad4Sound; + theSoundLength = 2.0f; + } + break; + case HUD_SOUND_SQUAD5: + if(this->GetInTopDownMode()) + { + theSoundPtr = kSquad5Sound; + theSoundLength = 1.2f; + } + else + { + theSoundPtr = kMarineSquad5Sound; + theSoundLength = 2.0f; + } + break; + case HUD_SOUND_PLACE_BUILDING: + theSoundPtr = kPlaceBuildingSound; + theSoundLength = .2f; + break; + + case HUD_SOUND_MARINE_POINTS_RECEIVED: + theSoundPtr = kMarinePointsReceivedSound; + theSoundLength = 1.42f; + break; + + case HUD_SOUND_MARINE_RESEARCHCOMPLETE: + theSoundPtr = kMarineResearchComplete; + theSoundLength = 2.0f; + break; + case HUD_SOUND_MARINE_SOLDIER_UNDER_ATTACK: + theSoundPtr = kMarineSoldierUnderAttack; + theSoundLength = 3.0f; + break; + case HUD_SOUND_MARINE_CCONLINE: + if(rand() % 2) + { + theSoundPtr = kMarineCCOnline1; + } + else + { + theSoundPtr = kMarineCCOnline2; + } + theSoundLength = 2.3f; + break; + case HUD_SOUND_MARINE_CCUNDERATTACK: + if(rand() % 2) + { + theSoundPtr = kMarineCCUnderAttack1; + } + else + { + theSoundPtr = kMarineCCUnderAttack2; + } + theSoundLength = 3.0f; + break; + case HUD_SOUND_MARINE_COMMANDER_EJECTED: + theSoundPtr = kMarineCommanderEjected; + theSoundLength = 3.0f; + break; + case HUD_SOUND_MARINE_BASE_UNDER_ATTACK: + if(rand() % 2) + { + theSoundPtr = kMarineBaseUnderAttack1; + } + else + { + theSoundPtr = kMarineBaseUnderAttack2; + } + theSoundLength = 3.0f; + break; + case HUD_SOUND_MARINE_UPGRADE_COMPLETE: + theSoundPtr = kMarineUpgradeComplete; + theSoundLength = 1.0f; + break; + case HUD_SOUND_MARINE_MORE: + theSoundPtr = kMarineMoreResources; + theSoundLength = 1.8f; + break; + case HUD_SOUND_MARINE_RESOURCES_LOW: + theSoundPtr = kMarineLowResources; + theSoundLength = 2.0f; + break; + case HUD_SOUND_MARINE_NEEDS_AMMO: + if(rand() % 2) + theSoundPtr = kMarineNeedsAmmo1; + else + theSoundPtr = kMarineNeedsAmmo2; + theSoundLength = 1.5f; + break; + case HUD_SOUND_MARINE_NEEDS_HEALTH: + if(rand() % 2) + theSoundPtr = kMarineNeedsHealth1; + else + theSoundPtr = kMarineNeedsHealth2; + theSoundLength = 1.3f; + break; + + case HUD_SOUND_MARINE_NEEDS_ORDER: + if(rand() % 2) + theSoundPtr = kMarineNeedsOrder1; + else + theSoundPtr = kMarineNeedsOrder2; + theSoundLength = 1.5f; + break; + + case HUD_SOUND_MARINE_SOLDIER_LOST: + if(rand() % 2) + theSoundPtr = kMarineSoldierLost1; + else + theSoundPtr = kMarineSoldierLost2; + theSoundLength = 1.3f; + break; + case HUD_SOUND_MARINE_SENTRYFIRING: + if(rand() % 2) + theSoundPtr = kMarineSentryFiring1; + else + theSoundPtr = kMarineSentryFiring2; + theSoundLength = 1.3f; + break; + case HUD_SOUND_MARINE_SENTRYDAMAGED: + if(rand() % 2) + theSoundPtr = kMarineSentryTakingDamage1; + else + theSoundPtr = kMarineSentryTakingDamage2; + theSoundLength = 1.5f; + break; + + case HUD_SOUND_MARINE_GIVEORDERS: + // : 0000407 + if (theAutoHelpEnabled) + { + theSoundPtr = kMarineGiveOrders; + theSoundLength = 2.2f; + } + // : + break; + + case HUD_SOUND_MARINE_NEEDPORTAL: + // : 0000407 + if (theAutoHelpEnabled) + { + if(rand() % 2) + theSoundPtr = kMarineNeedPortal1; + else + theSoundPtr = kMarineNeedPortal2; + theSoundLength = 1.8f; + } + // : + break; + + case HUD_SOUND_MARINE_GOTOALERT: + // : 0000407 + if (theAutoHelpEnabled) + { + theSoundPtr = kMarineGotoAlert; + theSoundLength = 2.2f; + } + // : + break; + + case HUD_SOUND_MARINE_COMMANDERIDLE: + // : 0000407 + if (theAutoHelpEnabled) + { + if(rand() % 2) + theSoundPtr = kMarineCommanderIdle1; + else + theSoundPtr = kMarineCommanderIdle2; + theSoundLength = 1.5f; + } + // : + break; + + case HUD_SOUND_MARINE_ARMORYUPGRADING: + theSoundPtr = kMarineArmoryUpgrading; + theSoundLength = 3.4; + break; + + case HUD_SOUND_ALIEN_ENEMY_APPROACHES: + if(rand() %2) + theSoundPtr = kAlienEnemyApproaches1; + else + theSoundPtr = kAlienEnemyApproaches2; + theSoundLength = 1.6; + break; + + case HUD_SOUND_ALIEN_GAMEOVERMAN: + theSoundPtr = kAlienGameOverMan; + theSoundLength = 2.2f; + break; + + case HUD_SOUND_ALIEN_HIVE_ATTACK: + theSoundPtr = kAlienHiveAttack; + theSoundLength = 1.6f; + break; + case HUD_SOUND_ALIEN_HIVE_COMPLETE: + if(rand() % 2) + theSoundPtr = kAlienHiveComplete1; + else + theSoundPtr = kAlienHiveComplete2; + theSoundLength = 2.1f; + break; + case HUD_SOUND_ALIEN_HIVE_DYING: + if(rand() % 2) + { + theSoundPtr = kAlienHiveDying1; + theSoundLength = 1.7f; + } + else + { + theSoundPtr = kAlienHiveDying2; + theSoundLength = 2.4f; + } + break; + case HUD_SOUND_ALIEN_LIFEFORM_ATTACK: + if(rand() % 2) + theSoundPtr = kAlienLifeformAttack1; + else + theSoundPtr = kAlienLifeformAttack2; + theSoundLength = 1.8f; + break; + case HUD_SOUND_ALIEN_RESOURCES_LOW: + theSoundPtr = kAlienLowResources; + theSoundLength = 1.7f; + break; + case HUD_SOUND_ALIEN_MESS: + theSoundPtr = kAlienMess; + theSoundLength = 2.0f; + break; + case HUD_SOUND_ALIEN_MORE: + if(rand() % 2) + theSoundPtr = kAlienMoreResources1; + else + theSoundPtr = kAlienMoreResources2; + theSoundLength = 1.8f; + break; + case HUD_SOUND_ALIEN_NEED_BUILDERS: + if(rand() % 2) + theSoundPtr = kAlienNeedBuilders1; + else + theSoundPtr = kAlienNeedBuilders2; + theSoundLength = 1.4f; + break; + + case HUD_SOUND_ALIEN_NEED_BETTER: + theSoundPtr = kAlienNeedBetter; + theSoundLength = 2.5f; + break; + + case HUD_SOUND_ALIEN_NOW_DONCE: + theSoundPtr = kAlienNowDonce; + theSoundLength = 2.1f; + break; + + case HUD_SOUND_ALIEN_NEW_TRAIT: + if(rand() % 2) + theSoundPtr = kAlienNewTrait1; + else + theSoundPtr = kAlienNewTrait2; + theSoundLength = 1.5f; + break; + + case HUD_SOUND_ALIEN_POINTS_RECEIVED: + theSoundPtr = kAlienPointsReceivedSound; + theSoundLength = 1.57f; + break; + + case HUD_SOUND_ALIEN_RESOURCES_ATTACK: + if(rand() % 2) + theSoundPtr = kAlienResourceAttack1; + else + theSoundPtr = kAlienResourceAttack2; + theSoundLength = 2.1f; + break; + case HUD_SOUND_ALIEN_STRUCTURE_ATTACK: + if(rand() % 2) + theSoundPtr = kAlienStructureAttack1; + else + theSoundPtr = kAlienStructureAttack2; + theSoundLength = 1.9f; + break; + case HUD_SOUND_ALIEN_UPGRADELOST: + theSoundPtr = kAlienUpgradeLost; + theSoundLength = 1.5f; + break; + + case HUD_SOUND_ORDER_MOVE: + switch(rand() % 4) + { + case 0: + theSoundPtr = kSoundOrderMove1; + theSoundLength = 1.8f; + break; + case 1: + theSoundPtr = kSoundOrderMove2; + theSoundLength = 2.3f; + break; + case 2: + theSoundPtr = kSoundOrderMove3; + theSoundLength = 1.9f; + break; + case 3: + theSoundPtr = kSoundOrderMove4; + theSoundLength = 2.3f; + break; + } + break; + case HUD_SOUND_ORDER_ATTACK: + theSoundPtr = kSoundOrderAttack; + break; + case HUD_SOUND_ORDER_BUILD: + theSoundPtr = kSoundOrderBuild; + break; + case HUD_SOUND_ORDER_WELD: + theSoundPtr = kSoundOrderWeld; + break; + case HUD_SOUND_ORDER_GUARD: + theSoundPtr = kSoundOrderGuard; + break; + case HUD_SOUND_ORDER_GET: + theSoundPtr = kSoundOrderGet; + break; + + case HUD_SOUND_ORDER_COMPLETE: + switch(rand() % 6) + { + case 0: + theSoundPtr = kSoundOrderComplete1; + theSoundLength = 1.6f; + break; + case 1: + theSoundPtr = kSoundOrderComplete2; + theSoundLength = 1.9f; + break; + case 2: + theSoundPtr = kSoundOrderComplete3; + theSoundLength = 1.9f; + break; + case 3: + theSoundPtr = kSoundOrderComplete4; + theSoundLength = 1.4f; + break; + case 4: + theSoundPtr = kSoundOrderComplete5; + theSoundLength = 1.6f; + break; + case 5: + theSoundPtr = kSoundOrderComplete6; + theSoundLength = 1.4f; + break; + } + break; + + case HUD_SOUND_GAMESTART: + if(this->GetIsMarine()) + { + if(rand() % 2) + theSoundPtr = kMarineGameStart1; + else + theSoundPtr = kMarineGameStart2; + theSoundLength = 1.9f; + } + else if(this->GetIsAlien()) + { + if(rand() % 2) + theSoundPtr = kAlienGameStart1; + else + theSoundPtr = kAlienGameStart2; + theSoundLength = 2.2f; + } + break; + + case HUD_SOUND_YOU_WIN: + theSoundPtr = kYouWinSound; + theSoundLength = 6.0f; + break; + case HUD_SOUND_YOU_LOSE: + theSoundPtr = kYouLoseSound; + theSoundLength = 6.0f; + break; + case HUD_SOUND_TOOLTIP: + theSoundPtr = kTooltipSound; + // Tooltip sounds should never stop other sounds + theSoundLength = -1.0f; + theVolume = .6f; + break; + // : bug 0000767 + case HUD_SOUND_PLAYERJOIN: + theSoundPtr = kPlayerJoinedSound; + theSoundLength = 3.0f; + theVolume = 1.1; + break; + // : + } + + if(theSoundPtr) + { + //gEngfuncs.pfnPlaySoundByNameAtLocation( sound, volume, (float *)&g_finalstate->playerstate.origin ); + cl_entity_s* thePlayer = this->GetVisiblePlayer(); + if ( thePlayer ) + gEngfuncs.pfnPlaySoundByNameAtLocation(theSoundPtr, theVolume, thePlayer->origin); + else + gEngfuncs.pfnPlaySoundByName(theSoundPtr, theVolume); + if(theSoundLength >= 0.0f) + { + this->mTimeOfNextHudSound = this->mTimeOfCurrentUpdate + theSoundLength; + } + this->mLastHUDSoundPlayed = inSound; + } + } +} + +BIND_MESSAGE(SetTech); +int AvHHud::SetTech(const char* pszName, int iSize, void* pbuf) +{ + AvHTechNode* theTechNode = NULL; + NetMsg_SetTechNode( pbuf, iSize, theTechNode ); + this->mTechNodes.InsertNode(theTechNode); + delete theTechNode; + + return 1; +} + +BIND_MESSAGE(TechSlots); +int AvHHud::TechSlots(const char* pszName, int iSize, void* pbuf) +{ + AvHTechSlots theNewTechSlots; + NetMsg_SetTechSlots( pbuf, iSize, theNewTechSlots ); + + this->mTechSlotManager.AddTechSlots(theNewTechSlots); + + return 1; +} + +BIND_MESSAGE(DebugCSP); +int AvHHud::DebugCSP(const char* pszName, int iSize, void* pbuf) +{ + weapon_data_t weapon_data; + float next_attack; + NetMsg_DebugCSP( pbuf, iSize, weapon_data, next_attack ); + + char theServerInfoString[512]; + sprintf(theServerInfoString, "Server: id: %d, clip: %d, prim attack: %f, idle: %f, next attack: %f", + weapon_data.m_iId, + weapon_data.m_iClip, + weapon_data.m_flNextPrimaryAttack, + weapon_data.m_flTimeWeaponIdle, + weapon_data.m_flNextSecondaryAttack, + next_attack + ); + + vgui::Label* theLabel = NULL; + if(this->GetManager().GetVGUIComponentNamed(kDebugCSPServerLabel, theLabel)) + { + theLabel->setText(theServerInfoString); + } + + return 1; +} + +AvHVisibleBlipList& AvHHud::GetEnemyBlipList() +{ + return this->mEnemyBlips; +} + +AvHEntityHierarchy& AvHHud::GetEntityHierarchy() +{ + return this->mEntityHierarchy; +} + +AvHVisibleBlipList& AvHHud::GetFriendlyBlipList() +{ + return this->mFriendlyBlips; +} + +bool AvHHud::GetMouseOneDown() const +{ + return this->mMouseOneDown; +} + +bool AvHHud::GetMouseTwoDown() const +{ + return this->mMouseTwoDown; +} + +void AvHHud::HideProgressStatus() +{ + this->mProgressBarLastDrawn = -1.0f; + +// if(this->mGenericProgressBar) +// { +// this->mGenericProgressBar->setVisible(false); +// } +// +// if(this->mAlienProgressBar) +// { +// this->mAlienProgressBar->setVisible(false); +// } +} + +void AvHHud::HideResearchProgressStatus() +{ + if(this->mResearchProgressBar) + { + this->mResearchProgressBar->setVisible(false); + this->mResearchLabel->setVisible(false); + } +} + +void AvHHud::Init(void) +{ + UIHud::Init(); + + HOOK_MESSAGE(DelEntHier); + HOOK_MESSAGE(EntHier); + HOOK_MESSAGE(DelParts); + HOOK_MESSAGE(Particles); + HOOK_MESSAGE(SoundNames); + HOOK_MESSAGE(PlayHUDNot); + HOOK_MESSAGE(BalanceVar); + HOOK_MESSAGE(GameStatus); + HOOK_MESSAGE(Progress); + HOOK_MESSAGE(Countdown); + HOOK_MESSAGE(MiniMap); + HOOK_MESSAGE(SetOrder); + HOOK_MESSAGE(SetSelect); + HOOK_MESSAGE(SetRequest); + HOOK_MESSAGE(SetupMap); + HOOK_MESSAGE(SetTopDown); + HOOK_MESSAGE(SetTech); + HOOK_MESSAGE(EditPS); + HOOK_MESSAGE(Fog); + HOOK_MESSAGE(ListPS); + HOOK_MESSAGE(SetGmma); + HOOK_MESSAGE(BlipList); + HOOK_MESSAGE(ClScript); + HOOK_MESSAGE(AlienInfo); + HOOK_MESSAGE(DebugCSP); + HOOK_MESSAGE(SetUpgrades); + HOOK_MESSAGE(TechSlots); + // : 0000971 + HOOK_MESSAGE(IssueOrder); + // : + + HOOK_MESSAGE(ServerVar); + + this->AddCommands(); + + this->mCountDownClock = -1; + + mOverviewMap.Clear(); + + this->mEntityHierarchy.Clear(); + //this->mUpgradeCosts.clear(); + + sPregameGammaTable.InitializeFromVideoState(); + sGameGammaTable.InitializeFromVideoState(); + + int theRC = atexit(AvHHud::ResetGammaAtExit); + _onexit_t theExit = _onexit(AvHHud::ResetGammaAtExitForOnExit); + + signal(SIGILL, AvHHud::ResetGammaAtExit); + signal(SIGFPE, AvHHud::ResetGammaAtExit); + signal(SIGSEGV, AvHHud::ResetGammaAtExit); + signal(SIGTERM, AvHHud::ResetGammaAtExit); + signal(SIGBREAK, AvHHud::ResetGammaAtExit); + signal(SIGABRT, AvHHud::ResetGammaAtExit); + + //memset(this->mAlienUILifeforms, 0, sizeof(HSPRITE)*kNumAlienLifeforms); + this->mAlienUIUpgrades = 0; + this->mAlienUIUpgradeCategories = 0; + this->mOrderSprite = 0; + this->mCursorSprite = 0; + this->mMarineCursor = 0; + this->mAlienCursor = 0; + + this->mMarineUIJetpackSprite = 0; + this->mMembraneSprite = 0; + this->mBackgroundSprite = 0; + + this->mProgressBarEntityIndex = -1; + this->mProgressBarParam = -1; + this->mProgressBarCompleted = -1; + this->mSelectedNodeResourceCost = -1; + this->mCurrentUseableEnergyLevel = 0; + this->mVisualEnergyLevel = 0.0f; + + this->mNumSensory=0; + this->mNumMovement=0; + this->mNumDefense=0; + + this->mHasGrenades=false; + this->mHasWelder=false; + this->mHasMines=false; + + this->mFogActive = false; + this->mFogColor.x = this->mFogColor.y = this->mFogColor.z = 0; + this->mFogStart = 0; + this->mFogEnd = 0; + + this->mLocationText = ""; + this->mInfoLocationList.clear(); + + this->mLastHotkeySelectionEvent = MESSAGE_NULL; + this->mUpgrades.clear(); + + this->mLastGhostBuilding = NULL; + this->mReticleInfoText = ""; + this->mSelectingWeaponID = -1; + this->mSelectingNodeID = MESSAGE_NULL; + this->mDesiredGammaSlope = 1; + this->mTimeOfLastHelpText = -1; + this->mCurrentWeaponID = -1; + this->mCurrentWeaponEnabled = false; + this->mCurrentUIMode = MAIN_MODE; + this->mMenuTechSlots = 0; + this->mBlinkingAlertType = 0; + this->mLastUser3 = AVH_USER3_NONE; + this->mLastTeamNumber = TEAM_IND; + this->mLastPlayMode = PLAYMODE_UNDEFINED; + + this->mCrosshairShowCount = 1; + this->mCrosshairSprite = 0; + this->mCrosshairR = 0; + this->mCrosshairG = 0; + this->mCrosshairB = 0; + + this->mDrawCombatUpgradeMenu = false; + + // Initialize viewport + this->mViewport[0] = this->mViewport[1] = this->mViewport[2] = this->mViewport[3] = 0; +} + +// This gives the HUD a chance to draw after the VGUI. A component must allow itself to be hooked by calling this function +// at the end of it's paint() routine. This is done so the mouse cursor can draw on top of the other components. +void AvHHud::ComponentJustPainted(Panel* inPanel) +{ +// if(this->GetInTopDownMode()) +// { +// AvHTeamHierarchy* theComponent = dynamic_cast(inPanel); +// if(theComponent) +// { +// int theBasePosX; +// int theBasePosY; +// theComponent->getPos(theBasePosX, theBasePosY); +// this->DrawMouseCursor(theBasePosX, theBasePosY); +// } +// } +// else +// { +// PieMenu* theComponent = dynamic_cast(inPanel); +// if(theComponent) +// { +// int theBasePosX; +// int theBasePosY; +// theComponent->getPos(theBasePosX, theBasePosY); +// this->DrawMouseCursor(theBasePosX, theBasePosY); +// } +// } + + + DummyPanel* theComponent = dynamic_cast(inPanel); + if(theComponent) + { + int theBasePosX; + int theBasePosY; + theComponent->getPos(theBasePosX, theBasePosY); + this->DrawMouseCursor(theBasePosX, theBasePosY); + } + +} + +bool AvHHud::SetCursor(AvHOrderType inOrderType) +{ + bool theSuccess = false; + + if(!this->GetIsAlien()) + { + this->mCursorSprite = this->mMarineCursor; + + if((inOrderType >= 0) && (inOrderType < ORDERTYPE_MAX)) + { + this->mCurrentCursorFrame = (int)(inOrderType); + theSuccess = true; + } + // Change cursor when over a valid choice + if(this->mSelectingNodeID > 0) + { + this->mCurrentCursorFrame = 1; + theSuccess = true; + } + } + else + { + this->mCursorSprite = this->mAlienCursor; + this->mCurrentCursorFrame = 0; + + if(this->mSelectingNodeID > 0) + { + // Set cursor to lifeform to evolve to + switch(this->mSelectingNodeID) + { + case ALIEN_LIFEFORM_ONE: + case ALIEN_LIFEFORM_TWO: + case ALIEN_LIFEFORM_THREE: + case ALIEN_LIFEFORM_FOUR: + case ALIEN_LIFEFORM_FIVE: + this->mCurrentCursorFrame = 2 + (int)this->mSelectingNodeID - (int)ALIEN_LIFEFORM_ONE; + break; + + default: + this->mCurrentCursorFrame = 1; + break; + } + } + + theSuccess = true; + } + + // Scheme::SchemeCursor theSchemeCursor = Scheme::SchemeCursor::scu_arrow; + // //Scheme::SchemeCursor theSchemeCursor = Scheme::SchemeCursor::scu_no; + // + // switch(inOrderType) + // { + //// case ORDERTYPE_UNDEFINED: + //// theSchemeCursor = Scheme::SchemeCursor::scu_no; + //// theSuccess = true; + //// break; + // + // //case ORDERTYPEL_MOVE: + // + // case ORDERTYPET_ATTACK: + // case ORDERTYPET_GUARD: + // case ORDERTYPET_WELD: + // case ORDERTYPET_BUILD: + // case ORDERTYPEL_USE: + // case ORDERTYPET_GET: + // theSchemeCursor = Scheme::SchemeCursor::scu_hand; + // theSuccess = true; + // break; + // } + // + // App::getInstance()->setCursorOveride( App::getInstance()->getScheme()->getCursor(theSchemeCursor)); + + return theSuccess; +} + +void AvHHud::GetCursor(HSPRITE& outSprite, int& outFrame) +{ + + if (g_iUser1 == 0) + { + outSprite = mCursorSprite; + outFrame = mCurrentCursorFrame; + } + else + { + // Always use the marine cursor in spectator mode. + outSprite = mMarineCursor; + outFrame = 0; + } + +} + +void AvHHud::SetHelpMessage(const string& inHelpText, bool inForce, float inNormX, float inNormY) +{ + if(inForce || gEngfuncs.pfnGetCvarFloat(kvAutoHelp)) + { + float theReticleX = kHelpMessageLeftEdgeInset; + float theReticleY = kHelpMessageTopEdgeInset - 0.03f; // + 0.15f; + bool theCentered = false; + + if(this->GetInTopDownMode()) + { + int theMouseX, theMouseY; + this->GetMousePos(theMouseX, theMouseY); + theReticleX = theMouseX/(float)ScreenWidth(); + theReticleY = theMouseY/(float)ScreenHeight(); + + // Move text up a bit so it doesn't obscure + theReticleY -= .1f; + + theCentered = true; + } + // Alien HUD forces this to be inset a bit + else + { + if(this->GetIsAlien()) + { + theReticleX = kHelpMessageAlienLeftedgeInset; + theReticleY = kHelpMessageAlienTopEdgeInset - 0.15f; + } + + if(inNormX != -1) + { + theReticleX = inNormX; + } + if(inNormY != -1) + { + theReticleY = inNormY; + } + } + + this->mHelpMessage.SetText(inHelpText); + + this->mHelpMessage.SetNormalizedScreenX(theReticleX); + this->mHelpMessage.SetNormalizedScreenY(theReticleY); + this->mHelpMessage.SetCentered(theCentered); + this->mHelpMessage.SetNormalizedMaxWidth(kReticleMaxWidth); + //this->mHelpMessage.SetIgnoreFadeForLifetime(true); + + float theTimePassed = (this->mTimeOfCurrentUpdate - this->mTimeOfLastUpdate); + this->mHelpMessage.FadeText(theTimePassed, false); + + // Set color + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, true, false); + this->mHelpMessage.SetRGB(theR, theG, theB); + } +} + +void AvHHud::SetActionButtonHelpMessage(const string& inHelpText) +{ + this->mTopDownActionButtonHelp.SetText(inHelpText); + + const float kNormX = .73f; + this->mTopDownActionButtonHelp.SetNormalizedScreenX(kNormX); + + // Treat this as the bottom of the tooltip. Scale the tooltip so it's bottom is here. + const float kNormY = .68f; + int theHeight = this->mTopDownActionButtonHelp.GetScreenHeight(); + float theNormalizedHeight = (float)theHeight/ScreenHeight(); + this->mTopDownActionButtonHelp.SetNormalizedScreenY(kNormY - theNormalizedHeight); + + this->mTopDownActionButtonHelp.SetBackgroundA(128); + + this->mTopDownActionButtonHelp.SetCentered(false); + this->mTopDownActionButtonHelp.SetNormalizedMaxWidth(1.0f - kNormX - .01f); + //this->mTopDownActionButtonHelp.SetIgnoreFadeForLifetime(true); + + float theTimePassed = (this->mTimeOfCurrentUpdate - this->mTimeOfLastUpdate); + this->mTopDownActionButtonHelp.FadeText(theTimePassed, false); + + // Set color + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, true, false); + this->mTopDownActionButtonHelp.SetRGB(theR, theG, theB); +} + +void AvHHud::SetReticleMessage(const string& inHelpText) +{ + float theReticleX; + float theReticleY; + bool theIsCentered; + this->GetReticleTextDrawingInfo(theReticleX, theReticleY, theIsCentered); + + this->mReticleMessage.SetText(inHelpText); + + this->mReticleMessage.SetNormalizedScreenX(theReticleX); + this->mReticleMessage.SetNormalizedScreenY(theReticleY); + this->mReticleMessage.SetCentered(theIsCentered); + this->mReticleMessage.SetNormalizedMaxWidth(kReticleMaxWidth); + + // Need instant fade-in and slow fade down for player names and info + this->mReticleMessage.SetFadeDownSpeed(-100); + this->mReticleMessage.SetFadeUpSpeed(10000); + //this->mReticleMessage.SetIgnoreFadeForLifetime(true); + + float theTimePassed = (this->mTimeOfCurrentUpdate - this->mTimeOfLastUpdate); + this->mReticleMessage.FadeText(theTimePassed, false); +} + +void AvHHud::SetCurrentUseableEnergyLevel(float inEnergyLevel) +{ + this->mCurrentUseableEnergyLevel = inEnergyLevel; +} + +const AvHMapExtents& AvHHud::GetMapExtents() +{ + return this->mMapExtents; +} + +void AvHHud::InitCommanderMode() +{ + Panel* thePanel = NULL; + + if(this->GetManager().GetVGUIComponentNamed(kLeaveCommanderButton, thePanel)) + { + thePanel->addInputSignal(&gCommanderHandler); + } + + if(this->GetManager().GetVGUIComponentNamed(kHierarchy, thePanel)) + { + thePanel->addInputSignal(&gCommanderHandler); + } + + if(this->GetManager().GetVGUIComponentNamed(kActionButtonsComponents, thePanel)) + { + thePanel->addInputSignal(&gCommanderHandler); + } + + if(this->GetManager().GetVGUIComponentNamed(kSelectAllImpulsePanel, thePanel)) + { + thePanel->addInputSignal(&gCommanderHandler); + } + + // Add handler for all pending request buttons + for(int i = 0; i < MESSAGE_LAST; i++) + { + AvHMessageID theMessageID = AvHMessageID(i); + + char theComponentName[256]; + sprintf(theComponentName, kPendingImpulseSpecifier, (int)theMessageID); + + AvHTechImpulsePanel* theTechImpulsePanel = NULL; + if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) + { + theTechImpulsePanel->addInputSignal(&gCommanderHandler); + } + } + + //this->GetManager().GetVGUIComponentNamed(kScroller, thePanel); + //if(thePanel) + //{ + // thePanel->addInputSignal(&gScrollHandler); + //} + + // Get input from every control + this->GetManager().AddInputSignal(&gScrollHandler); + + // TODO: Add others here +} + +// Read in base state from stream (called by Demo_ReadBuffer) +int AvHHud::InitializeDemoPlayback(int inSize, unsigned char* inBuffer) +{ + // Read in base state + int theBytesRead = 0; + + // Read in num upgrades + int theNumUpgrades = 0; + //this->mUpgradeCosts.clear(); + LoadData(&theNumUpgrades, inBuffer, sizeof(int), theBytesRead); + + for(int i = 0; i < theNumUpgrades; i++) + { + // Read in upgrades (for backwards-compatibility) + int theFirst = 0; + LoadData(&theFirst, inBuffer, sizeof(int), theBytesRead); + + int theSecond = 0; + LoadData(&theSecond, inBuffer, sizeof(int), theBytesRead); + } + + // Read in gamma + LoadData(&this->mDesiredGammaSlope, inBuffer, sizeof(this->mDesiredGammaSlope), theBytesRead); + + if (!mSteamUIActive) + { + this->SetGamma(this->mDesiredGammaSlope); + } + + // Read in resources + LoadData(&this->mResources, inBuffer, sizeof(this->mResources), theBytesRead); + + // Read in commander (TODO: REMOVE) + int theCommander; + LoadData(&theCommander, inBuffer, sizeof(theCommander), theBytesRead); + + // Read in number of hive infos + this->mHiveInfoList.clear(); + + int theNumHiveInfos = 0; + LoadData(&theNumHiveInfos, inBuffer, sizeof(int), theBytesRead); + + // For each one, add a new hive info + for(i = 0; i < theNumHiveInfos; i++) + { + AvHHiveInfo theHiveInfo; + LoadData(&theHiveInfo, inBuffer, sizeof(AvHHiveInfo), theBytesRead); + this->mHiveInfoList.push_back(theHiveInfo); + } + + // Load and set current pie menu control + int thePieMenuControlLength = 0; + LoadData(&thePieMenuControlLength, inBuffer, sizeof(int), theBytesRead); + + char thePieMenuControl[256]; + memset(thePieMenuControl, 0, 256); + LoadData(thePieMenuControl, inBuffer, thePieMenuControlLength, theBytesRead); + this->mPieMenuControl = string(thePieMenuControl); + + // Read in selected units size + this->mSelected.clear(); + int theNumSelected = 0; + LoadData(&theNumSelected, inBuffer, sizeof(theNumSelected), theBytesRead); + + for(i = 0; i < theNumSelected; i++) + { + // Read in selected units + EntityInfo theSelectedEntity = 0; + LoadData(&theSelectedEntity, inBuffer, sizeof(theSelectedEntity), theBytesRead); + } + + ASSERT((theBytesRead + (int)sizeof(int)) == inSize); + + // Clear existing particle system templates + gParticleTemplateList.Clear(); + AvHParticleSystemManager::Instance()->Reset(); + + // Clear weapon info + gWR.Reset(); + + return theBytesRead; +} + +int AvHHud::InitializeDemoPlayback2(int inSize, unsigned char* inBuffer) +{ + // Read in base state 2 + int theBytesRead = 0; + + LOAD_DATA(this->mTimeOfLastUpdate); + LOAD_DATA(this->mTimeOfNextHudSound); + LOAD_DATA(this->mTimeOfCurrentUpdate); + LOAD_DATA(this->mCountDownClock); + LOAD_DATA(this->mLastTickPlayed); + LOAD_DATA(this->mNumTicksToPlay); + LoadStringData(this->mMapName, inBuffer, theBytesRead); + + float theMinViewHeight; + LOAD_DATA(theMinViewHeight); + this->mMapExtents.SetMinViewHeight(theMinViewHeight); + + float theMaxViewHeight; + LOAD_DATA(theMaxViewHeight); + this->mMapExtents.SetMaxViewHeight(theMaxViewHeight); + + float theMinMapX; + LOAD_DATA(theMinMapX); + this->mMapExtents.SetMinMapX(theMinMapX); + + float theMaxMapX; + LOAD_DATA(theMaxMapX); + this->mMapExtents.SetMaxMapX(theMaxMapX); + + float theMinMapY; + LOAD_DATA(theMinMapY); + this->mMapExtents.SetMinMapY(theMinMapY); + + float theMaxMapY; + LOAD_DATA(theMaxMapY); + this->mMapExtents.SetMaxMapY(theMaxMapY); + + bool theDrawMapBG; + LOAD_DATA(theDrawMapBG); + this->mMapExtents.SetDrawMapBG(theDrawMapBG); + + // Clear then load sound names + int theSoundNameListSize; + LOAD_DATA(theSoundNameListSize); + this->mSoundNameList.clear(); + + for(int i = 0; i < theSoundNameListSize; i++) + { + string theCurrentSoundName; + LoadStringData(theCurrentSoundName, inBuffer, theBytesRead); + this->mSoundNameList.push_back(theCurrentSoundName); + } + + ASSERT((theBytesRead + (int)sizeof(int)) == inSize); + + return theBytesRead; +} + +// Write out base HUD data to stream +void AvHHud::InitializeDemoRecording() +{ + // Figure out total size of buffer needed + + // Write number of upgrades, then each upgrade + // No longer done, but need to add in upgrades for backwards compatibility + int theUpgrades = 0; + int theUpgradesSize = sizeof(theUpgrades); + + // Gamma, resources + int theGammaSize = sizeof(this->mDesiredGammaSlope); + int theResourcesSizes = sizeof(this->mResources); + + // Save commander index (TODO: REMOVE) + int theCommanderIndex = this->GetCommanderIndex(); + int theCommanderSize = sizeof(theCommanderIndex); + + int theNumHiveInfoRecords = (int)this->mHiveInfoList.size(); + int theHiveInfoSize = sizeof(int) + theNumHiveInfoRecords*sizeof(AvHHiveInfo); + + string thePieMenuControl = gPieMenuHandler.GetPieMenuControl(); + int theCurrentPieMenuControlSize = sizeof(int) + (int)thePieMenuControl.size(); + + int theSelectedSize = sizeof(int) + (int)this->mSelected.size()*sizeof(EntityInfo); + + int theTotalSize = theUpgradesSize + theGammaSize + theResourcesSizes + theCommanderSize + theHiveInfoSize + theCurrentPieMenuControlSize + theSelectedSize; + + // New a char array of this size + int theCounter = 0; + + unsigned char* theCharArray = new unsigned char[theTotalSize]; + if(theCharArray) + { + // Write out number of upgrades (for backwards-compatibility) + int theNumUpgradeCosts = 0; + SaveData(theCharArray, &theNumUpgradeCosts, sizeof(theNumUpgradeCosts), theCounter); + + // Write out gamma + SaveData(theCharArray, &this->mDesiredGammaSlope, theGammaSize, theCounter); + SaveData(theCharArray, &this->mResources, theResourcesSizes, theCounter); + SaveData(theCharArray, &theCommanderIndex, theCommanderSize, theCounter); + + // Write out num hive info records + SaveData(theCharArray, &theNumHiveInfoRecords, sizeof(int), theCounter); + for(HiveInfoListType::iterator theHiveInfoIter = this->mHiveInfoList.begin(); theHiveInfoIter != this->mHiveInfoList.end(); theHiveInfoIter++) + { + SaveData(theCharArray, &(*theHiveInfoIter), sizeof(AvHHiveInfo), theCounter); + } + + // Save length of pie menu control name + int thePieMenuControlNameLength = (int)thePieMenuControl.size(); + SaveData(theCharArray, &thePieMenuControlNameLength, sizeof(int), theCounter); + SaveData(theCharArray, thePieMenuControl.c_str(), thePieMenuControlNameLength, theCounter); + + // Save out size of selected + int theNumSelected = (int)this->mSelected.size(); + SaveData(theCharArray, &theNumSelected, sizeof(theNumSelected), theCounter); + + for(EntityListType::const_iterator theSelectedIter = this->mSelected.begin(); theSelectedIter != this->mSelected.end(); theSelectedIter++) + { + EntityInfo theCurrentInfo = *theSelectedIter; + SaveData(theCharArray, &theCurrentInfo, sizeof(EntityInfo), theCounter); + } + + ASSERT(theCounter == theTotalSize); + + // Write it out + Demo_WriteBuffer(TYPE_BASESTATE, theTotalSize, theCharArray); + + // Delete char array + delete [] theCharArray; + theCharArray = NULL; + + // Save out particle templates + gParticleTemplateList.InitializeDemoRecording(); + } + + theTotalSize = theCounter = 0; + theTotalSize += sizeof(this->mTimeOfLastUpdate); + theTotalSize += sizeof(this->mTimeOfNextHudSound); + theTotalSize += sizeof(this->mTimeOfCurrentUpdate); + theTotalSize += sizeof(this->mCountDownClock); + theTotalSize += sizeof(this->mLastTickPlayed); + theTotalSize += sizeof(this->mNumTicksToPlay); + theTotalSize += GetDataSize(this->mMapName); + theTotalSize += sizeof(this->mMapExtents.GetMinViewHeight()); + theTotalSize += sizeof(this->mMapExtents.GetMaxViewHeight()); + theTotalSize += sizeof(this->mMapExtents.GetMinMapX()); + theTotalSize += sizeof(this->mMapExtents.GetMaxMapX()); + theTotalSize += sizeof(this->mMapExtents.GetMinMapY()); + theTotalSize += sizeof(this->mMapExtents.GetMaxMapY()); + theTotalSize += sizeof(this->mMapExtents.GetDrawMapBG()); + + // Save sound names + int theSoundNameListSize = (int)this->mSoundNameList.size(); + theTotalSize += sizeof(theSoundNameListSize); + + for(int i = 0; i < theSoundNameListSize; i++) + { + string theCurrentSoundName = this->mSoundNameList[i]; + theTotalSize += GetDataSize(theCurrentSoundName); + } + + theCharArray = new unsigned char[theTotalSize]; + if(theCharArray) + { + SAVE_DATA(this->mTimeOfLastUpdate); + SAVE_DATA(this->mTimeOfNextHudSound); + SAVE_DATA(this->mTimeOfCurrentUpdate); + SAVE_DATA(this->mCountDownClock); + SAVE_DATA(this->mLastTickPlayed); + SAVE_DATA(this->mNumTicksToPlay); + SaveStringData(theCharArray, this->mMapName, theCounter); + + float theMinViewHeight = this->mMapExtents.GetMinViewHeight(); + SAVE_DATA(theMinViewHeight); + float theMaxViewHeight = this->mMapExtents.GetMaxViewHeight(); + SAVE_DATA(theMaxViewHeight); + float theMinMapX = this->mMapExtents.GetMinMapX(); + SAVE_DATA(theMinMapX); + float theMaxMapX = this->mMapExtents.GetMaxMapX(); + SAVE_DATA(theMaxMapX); + float theMinMapY = this->mMapExtents.GetMinMapY(); + SAVE_DATA(theMinMapY); + float theMaxMapY = this->mMapExtents.GetMaxMapY(); + SAVE_DATA(theMaxMapY); + bool theDrawMapBG = this->mMapExtents.GetDrawMapBG(); + SAVE_DATA(theDrawMapBG); + + SAVE_DATA(theSoundNameListSize); + for(int i = 0; i < theSoundNameListSize; i++) + { + string theCurrentSoundName = this->mSoundNameList[i]; + SaveStringData(theCharArray, theCurrentSoundName, theCounter); + } + + // TODO: Save out locations? + + ASSERT(theCounter == theTotalSize); + + // Write out TYPE_BASESTATE2 chunk separately for backwards-compatibility + Demo_WriteBuffer(TYPE_BASESTATE2, theTotalSize, theCharArray); + + delete [] theCharArray; + theCharArray = NULL; + } + + ScorePanel_InitializeDemoRecording(); + + // Write out weapon info + for(i = 0; i < MAX_WEAPONS; i++) + { + WEAPON* theWeapon = gWR.GetWeapon(i); + if( theWeapon ) + { + theTotalSize = sizeof(theWeapon->szName) + sizeof(theWeapon->iAmmoType) + sizeof(theWeapon->iAmmo2Type) + sizeof(theWeapon->iMax1) + sizeof(theWeapon->iMax2) + sizeof(theWeapon->iSlot) + sizeof(theWeapon->iSlotPos) + sizeof(theWeapon->iFlags) + sizeof(theWeapon->iId) + sizeof(theWeapon->iClip) + sizeof(theWeapon->iCount);// + sizeof(int); // last one is for ammo + theCharArray = new unsigned char[theTotalSize]; + + int theWeaponCoreDataLength = theTotalSize;// - sizeof(int); + memcpy(theCharArray, theWeapon, theWeaponCoreDataLength); // Everything but ammo + //int theAmmo = gWR.GetAmmo(theWeapon->iId); + //memcpy(theCharArray + theWeaponCoreDataLength, &theAmmo, sizeof(int)); + + Demo_WriteBuffer(TYPE_WEAPONINFO, theTotalSize, (unsigned char*)theWeapon); + + delete [] theCharArray; + theCharArray = NULL; + } + } +} + +int AvHHud::InitializeWeaponInfoPlayback(int inSize, unsigned char* inBuffer) +{ + // Make sure weapons are cleared out first + WEAPON theWeapon; + memset(&theWeapon, 0, sizeof(theWeapon)); + + int theWeaponCoreDataLength = inSize;// - sizeof(int); + memcpy(&theWeapon, inBuffer, theWeaponCoreDataLength); + + if(theWeapon.iId) + { + gWR.AddWeapon( &theWeapon ); + //int theAmmo = 0; + //memcpy(&theAmmo, inBuffer + theWeaponCoreDataLength, sizeof(int)); + //gWR.SetAmmo(theWeapon.iId, theAmmo); + } + + return inSize; +} + +void AvHHud::InitMenu(const string& inMenuName) +{ + PieMenu* theMenu = NULL; + if(this->GetManager().GetVGUIComponentNamed(inMenuName, theMenu)) + { + theMenu->AddInputSignalForNodes(&gPieMenuHandler); + //outMenu->DisableNodesGreaterThanCost(this->mResources); + this->GetManager().HideComponent(inMenuName); + } +} + +void AvHHud::PostUIInit(void) +{ + this->InitMenu(kSoldierMenu); + this->InitMenu(kSoldierCombatMenu); + this->InitMenu(kAlienMenu); + this->InitMenu(kAlienCombatMenu); + + this->InitCommanderMode(); + + this->GetManager().GetVGUIComponentNamed(kCommanderResourceLabel, this->mCommanderResourceLabel); + this->GetManager().GetVGUIComponentNamed(kGenericProgress, this->mGenericProgressBar); + this->GetManager().GetVGUIComponentNamed(kResearchProgress, this->mResearchProgressBar); + this->GetManager().GetVGUIComponentNamed(kAlienProgress, this->mAlienProgressBar); + this->GetManager().GetVGUIComponentNamed(kSelectionBox, this->mSelectionBox); + this->GetManager().GetVGUIComponentNamed(kResearchingLabel, this->mResearchLabel); + + // Init particle editor + gParticleEditorHandler.Setup(); + + if(this->GetManager().GetVGUIComponentNamed(kHierarchy, this->mHierarchy)) + { + this->mHierarchy->setEnabled(false); + } + if(this->GetManager().GetVGUIComponentNamed(kShowMapHierarchy, this->mShowMapHierarchy)) + { + this->mShowMapHierarchy->setEnabled(false); + } + + // Don't turn on gamma by default, it is annoying for testing + //this->SetGamma(); +} + +AvHMessageID AvHHud::GetGhostBuilding() const +{ + return this->mGhostBuilding; +} + +void AvHHud::SetGhostBuildingMode(AvHMessageID inGhostBuilding) +{ + this->mGhostBuilding = inGhostBuilding; + this->mCreatedGhost = false; +} + +void AvHHud::SetClientDebugCSP(weapon_data_t* inWeaponData, float inNextPlayerAttack) +{ + char theClientInfoString[512]; + sprintf(theClientInfoString, "Client: id: %d, clip: %d, prim attack: %f, idle: %f, next attack: %f", inWeaponData->m_iId, inWeaponData->m_iClip, inWeaponData->m_flNextPrimaryAttack, inWeaponData->m_flTimeWeaponIdle, inNextPlayerAttack); + + vgui::Label* theLabel = NULL; + if(this->GetManager().GetVGUIComponentNamed(kDebugCSPClientLabel, theLabel)) + { + theLabel->setText(theClientInfoString); + } +} + +void AvHHud::SetCurrentWeaponData(int inCurrentWeaponID, bool inEnabled) +{ + this->mCurrentWeaponID = inCurrentWeaponID; + this->mCurrentWeaponEnabled = inEnabled; +} + +int AvHHud::GetCurrentWeaponID(void) +{ + return this->mCurrentWeaponID; +} + +void AvHHud::SetAlienAbility(AvHMessageID inAlienAbility) +{ + this->mAlienAbility = inAlienAbility; +} + +void AvHHud::SetProgressStatus(float inPercentage, int inProgressbarType) +{ + this->mProgressBarStatus = inPercentage; + this->mProgressBarLastDrawn = this->GetTimeOfLastUpdate(); + this->mProgressBarDrawframe = inProgressbarType; + +// if(this->mGenericProgressBar) +// { +// this->mGenericProgressBar->setVisible(false); +// } +// +// if(this->mAlienProgressBar) +// { +// this->mAlienProgressBar->setVisible(false); +// } +// +// ProgressBar* theProgressBar = this->mGenericProgressBar; +// if(this->GetIsAlien()) +// { +// theProgressBar = this->mAlienProgressBar; +// } +// +// if(theProgressBar) +// { +// theProgressBar->setVisible(true); +// +// int theNumSegments = theProgressBar->getSegmentCount(); +// int theSegment = inPercentage*theNumSegments; +// theProgressBar->setProgress(theSegment); +// } + +} + +void AvHHud::SetReinforcements(int inReinforcements) +{ + Label* theLabel = NULL; + if(this->GetManager().GetVGUIComponentNamed(kReinforcementsLabel, theLabel)) + { + string thePrefix; + if(LocalizeString(kReinforcementsText, thePrefix)) + { + string theText = thePrefix + string(" ") + MakeStringFromInt(inReinforcements); + theLabel->setText(theText.c_str()); + } + } +} + +void AvHHud::SetResearchProgressStatus(float inPercentage) +{ + if(this->mResearchProgressBar) + { + this->mResearchProgressBar->setVisible(true); + + int theNumSegments = this->mResearchProgressBar->getSegmentCount(); + int theSegment = inPercentage*theNumSegments; + this->mResearchProgressBar->setProgress(theSegment); + + this->mResearchLabel->setVisible(true); + + // Center research label + int theX, theY; + this->mResearchLabel->getPos(theX, theY); + int theTextWidth, theTextHeight; + this->mResearchLabel->getTextSize(theTextWidth, theTextHeight); + this->mResearchLabel->setPos(ScreenWidth()/2 - theTextWidth/2, theY); + } +} + +void AvHHud::UpdateDemoRecordPlayback() +{ + // If the mouse is visible, allow it to work with demos + if(gEngfuncs.pDemoAPI->IsRecording()) + { + // Write out first frame if needed + if(!this->mRecordingLastFrame) + { + this->InitializeDemoRecording(); + this->mRecordingLastFrame = true; + } + + // Write view origin (and angles?) + //Demo_WriteVector(TYPE_VIEWANGLES, v_angles); + //Demo_WriteVector(TYPE_VIEWORIGIN, v_origin); +// Demo_WriteByte(TYPE_MOUSEVIS, g_iVisibleMouse); +// +// if(g_iVisibleMouse) +// { +// int theMouseScreenX, theMouseScreenY; +// +// IN_GetMousePos(&theMouseScreenX, &theMouseScreenY); +// +// //theMouseScreenX += ScreenWidth/2; +// //theMouseScreenY += ScreenHeight/2; +// +// // Write mouse position +// float theNormX = (float)theMouseScreenX/ScreenWidth; +// float theNormY = (float)theMouseScreenY/ScreenHeight; +// +// Demo_WriteFloat(TYPE_MOUSEX, theNormX); +// Demo_WriteFloat(TYPE_MOUSEY, theNormY); +// +// //char theBuffer[256]; +// //sprintf(theBuffer, "Saving mouse coords %f %f\n", theNormX, theNormY); +// //CenterPrint(theBuffer); +// } + } + else if(gEngfuncs.pDemoAPI->IsPlayingback()) + { + //char theBuffer[256]; + //sprintf(theBuffer, "Restoring mouse coords %f %f\n", gNormMouseX, gNormMouseY); + //CenterPrint(theBuffer); + + //int theCurrentMouseX = gNormMouseX*ScreenWidth; + //int theCurrentMouseY = gNormMouseY*ScreenHeight; + // + ////App::getInstance()->setCursorPos(theCurrentMouseX, theCurrentMouseY); + // + //SetCursorPos(theCurrentMouseX, theCurrentMouseY); + // + //this->mMouseCursorX = theCurrentMouseX; + //this->mMouseCursorY = theCurrentMouseY; + + this->mRecordingLastFrame = false; + } + else + { + this->mRecordingLastFrame = false; + } +} + +void AvHHud::UpdateDataFromVuser4(float inCurrentTime) +{ + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + if(theLocalPlayer) + { + // Fetch data from vuser4 + vec3_t theVUser = theLocalPlayer->curstate.vuser4; + + //int theVUserVar0 = 0; + //int theVUserVar1 = 0; + //int theVUserVar2 = 0; + // + //memcpy(&theVUserVar0, (int*)(&theVUser.x), 4); + //memcpy(&theVUserVar1, (int*)(&theVUser.y), 4); + //memcpy(&theVUserVar2, (int*)(&theVUser.z), 4); + + // Fetch new resource level + //theVUserVar2;// & 0x0000FFFF; + + if(this->GetIsCombatMode()) + { + this->mExperience = (int)ceil(theVUser.z/kNumericNetworkConstant); + this->mExperienceLevel = AvHPlayerUpgrade::GetPlayerLevel(this->mExperience); + + const float kShowMenuInterval = 5.0f; + + // If we are at a level greater then we've drawn, set visible again to reparse + if(this->mExperienceLevel > this->mExperienceLevelLastDrawn) + { + this->DisplayCombatUpgradeMenu(true); + this->mTimeOfLastLevelUp = inCurrentTime; + this->mExperienceLevelLastDrawn = this->mExperienceLevel; + } + // else if we have no more levels to spend or if we've been displaying it longer then the time out (+1 because we start at level 1) + else if((this->mExperienceLevel == (this->mExperienceLevelSpent + 1)) || (inCurrentTime > (this->mTimeOfLastLevelUp + kShowMenuInterval))) + { + // stop displaying it + this->DisplayCombatUpgradeMenu(false); + } + } + else + { + int theNewValue = (int)ceil(theVUser.z/kNumericNetworkConstant); + + if(theNewValue != this->mResources) + { + this->mResources = theNewValue; + } + + // Don't smooth resources nicely when switching targets + if((g_iUser1 == OBS_IN_EYE) && (g_iUser2 != this->mUser2OfLastResourceMessage)) + { + this->mVisualResources = this->mResources; + } + } + this->mUser2OfLastResourceMessage = g_iUser2; + } +} + +void AvHHud::UpdateSpectating() +{ + // If we're spectating and the team of our target switched, delete all blips + if((this->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) || (this->GetPlayMode() == PLAYMODE_OBSERVER)) + { + AvHTeamNumber theCurrentTargetTeam = TEAM_IND; + + int theCurrentTarget = g_iUser2;//thePlayer->curstate.iuser2; + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(theCurrentTarget); + if(theEntity) + { + theCurrentTargetTeam = AvHTeamNumber(theEntity->curstate.team); + } + + if((theCurrentTargetTeam != this->mLastTeamSpectated) && (theCurrentTargetTeam != TEAM_IND)) + { + this->mEnemyBlips.Clear(); + this->mFriendlyBlips.Clear(); + this->mLastTeamSpectated = theCurrentTargetTeam; + //CenterPrint("Clearing blips."); + } + } +} + +void AvHHud::UpdateCommonUI() +{ + // Find currently selected node and draw help text if it's been open for a little bit + PieMenu* thePieMenu = NULL; + AvHMessageID theCurrentNodeID = MESSAGE_NULL; + + this->mSelectingNodeID = MESSAGE_NULL; + + if(this->GetManager().GetVGUIComponentNamed(this->mPieMenuControl, thePieMenu)) + { + // : Added check to ensure that it is only updated when a menu is visible + if (thePieMenu->getChildCount() > 0) + { + vgui::Panel *rootMenu = thePieMenu->getChild(0); + if (rootMenu && rootMenu->isVisible()) + { + this->UpdateUpgradeCosts(); + } + } + + PieNode* theCurrentNode = NULL; + if(thePieMenu && thePieMenu->GetSelectedNode(theCurrentNode)) + { + theCurrentNodeID = (AvHMessageID)theCurrentNode->GetMessageID(); + + if(theCurrentNodeID > 0) + { + this->mSelectingNodeID = theCurrentNodeID; + } + } + } + + // Clear squad on team change + if(!this->GetIsMarine()) + { + this->mCurrentSquad = 0; + } +} + +void RemoveAlias(char *name) +{ + cmdalias_t* alias = gEngfuncs.pfnGetAliases();// *(alias_s**)0x02d29b7c; + while(alias) + { + if ( name && (strlen(name) > 1) && alias->name && (strcmp(alias->name, name) == 0) ) { + strcat(alias->name, " "); + break; + } + alias = alias->next; + } +} + +void ForceCvar(char *name, cvar_t *cvar, float value) +{ + cmdalias_t* alias = gEngfuncs.pfnGetAliases();// *(alias_s**)0x02d29b7c; + while(alias) + { + if ( name && (strlen(name) > 1) && alias->name && (strcmp(alias->name, name) == 0) ) { + strcat(alias->name, " "); + break; + } + alias = alias->next; + } + if ( (cvar) && (cvar)->value != (value) ) (gEngfuncs.Cvar_SetValue)( (name) , (value) ); +} + +cvar_t *gl_monolights = NULL; +cvar_t *gl_overbright = NULL; +cvar_t *gl_clear = NULL; +cvar_t *cl_rate = NULL; +cvar_t *hud_draw = NULL; +cvar_t *r_drawviewmodel = NULL; +extern cvar_t *cl_movespeedkey; +cvar_t *gl_d3dflip = NULL; +cvar_t *s_show = NULL; +cvar_t *lightgamma = NULL; +cvar_t *texgamma = NULL; +cvar_t *r_detailtextures = NULL; +cvar_t *gl_max_size = NULL; + +void AvHHud::InitExploitPrevention() { + gl_monolights = gEngfuncs.pfnGetCvarPointer("gl_monolights"); + cl_rate = gEngfuncs.pfnGetCvarPointer("cl_rate"); + gl_overbright = gEngfuncs.pfnGetCvarPointer("gl_overbright"); + gl_clear = gEngfuncs.pfnGetCvarPointer("gl_clear"); + hud_draw = gEngfuncs.pfnGetCvarPointer("hud_draw"); + r_drawviewmodel = gEngfuncs.pfnGetCvarPointer("r_drawviewmodel"); + gl_d3dflip = gEngfuncs.pfnGetCvarPointer("gl_d3dflip"); + s_show = gEngfuncs.pfnGetCvarPointer("s_show"); + lightgamma = gEngfuncs.pfnGetCvarPointer("lightgamma"); + texgamma = gEngfuncs.pfnGetCvarPointer("texgamma"); + r_detailtextures = gEngfuncs.pfnGetCvarPointer("r_detailtextures"); + gl_max_size = gEngfuncs.pfnGetCvarPointer("gl_max_size"); + + ForceCvar("gl_monolights", gl_monolights, 0.0f); + ForceCvar("gl_overbright", gl_overbright, 0.0f); + ForceCvar("gl_clear", gl_clear, 0.0f); + ForceCvar("hud_draw", hud_draw, 1.0f); + ForceCvar("r_drawviewmodel", r_drawviewmodel, 1.0f); + 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); + + RemoveAlias("lightgamma"); + if(lightgamma && lightgamma->value < 2.0) { + ForceCvar("lightgamma", lightgamma, 2.0f); + } + if(lightgamma && lightgamma->value > 5.0) { + ForceCvar("lightgamma", lightgamma, 5.0f); + } + RemoveAlias("texgamma"); + if(texgamma && texgamma->value < 1.0) { + ForceCvar("texgamma", texgamma, 1.0f); + } + if(texgamma && texgamma->value > 5.0) { + ForceCvar("texgamma", texgamma, 5.0f); + } +} + + +void AvHHud::UpdateExploitPrevention() +{ + //Note: Sometimes some clients will not have these cvars, so be sure to check that they are not null. + ForceCvar("gl_monolights", gl_monolights, 0.0f); + ForceCvar("gl_overbright", gl_overbright, 0.0f); + ForceCvar("gl_clear", gl_clear, 0.0f); + ForceCvar("hud_draw", hud_draw, 1.0f); + ForceCvar("r_drawviewmodel", r_drawviewmodel, 1.0f); + float movespeedkey=AvHMUGetWalkSpeedFactor(this->GetHUDUser3()); + ForceCvar("cl_movespeedkey", cl_movespeedkey, movespeedkey); + 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); + + if(lightgamma && lightgamma->value < 2.0) { + ForceCvar("lightgamma", lightgamma, 2.0f); + } + if(lightgamma && lightgamma->value > 5.0) { + ForceCvar("lightgamma", lightgamma, 5.0f); + } + if(texgamma && texgamma->value < 1.0) { + ForceCvar("texgamma", texgamma, 1.0f); + } + if(texgamma && texgamma->value > 5.0) { + ForceCvar("texgamma", texgamma, 5.0f); + } +} + +void AvHHud::UpdateAlienUI(float inCurrentTime) +{ + // Always hide it by default + this->GetManager().HideComponent(kPieHelpText); + + if(this->GetIsAlien() && this->GetIsAlive()) + { + float theTimePassed = inCurrentTime - this->mTimeOfLastUpdate; +// int theNumFrames = SPR_Frames(this->mAlienUIHiveSprite); +// +// for(AnimatedSpriteListType::iterator theIter = this->mAlienUIHiveList.begin(); theIter != this->mAlienUIHiveList.end(); theIter++) +// { +// const float kAnimationSpeed = 16.0f; +// +// float theCurrentFrame = theIter->mCurrentFrame + theTimePassed*kAnimationSpeed; +// if(theCurrentFrame >= theNumFrames) +// { +// theCurrentFrame -= theNumFrames; +// } +// theIter->mCurrentFrame = theCurrentFrame; +// } + + // Check to see if we have any unspent upgrades. If so, change the pie menu and HUD state to reflect this + this->mNumUpgradesAvailable = 0; + for(int i = 0; i < ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE; i++) + { + this->mCurrentUpgradeCategory[i] = ALIEN_UPGRADE_CATEGORY_INVALID; + } + + int theUpgradeVar = this->GetLocalUpgrades(); + + if(AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_SENSORY, this->mUpgrades, theUpgradeVar)) + { + this->mCurrentUpgradeCategory[this->mNumUpgradesAvailable] = ALIEN_UPGRADE_CATEGORY_SENSORY; + this->mNumUpgradesAvailable += 1; + } + if(AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_MOVEMENT, this->mUpgrades, theUpgradeVar)) + { + this->mCurrentUpgradeCategory[this->mNumUpgradesAvailable] = ALIEN_UPGRADE_CATEGORY_MOVEMENT; + this->mNumUpgradesAvailable += 1; + } + if(AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_OFFENSE, this->mUpgrades, theUpgradeVar)) + { + this->mCurrentUpgradeCategory[this->mNumUpgradesAvailable] = ALIEN_UPGRADE_CATEGORY_OFFENSE; + this->mNumUpgradesAvailable += 1; + } + if(AvHGetHasUpgradeChoiceInCategory(ALIEN_UPGRADE_CATEGORY_DEFENSE, this->mUpgrades, theUpgradeVar)) + { + this->mCurrentUpgradeCategory[this->mNumUpgradesAvailable] = ALIEN_UPGRADE_CATEGORY_DEFENSE; + this->mNumUpgradesAvailable += 1; + } + } +} + +bool AvHHud::GetCommanderLabelText(std::string& outCommanderName) const +{ + + int theCommander = this->GetCommanderIndex(); + bool theHasCommander = (theCommander > 0) && (theCommander <= gEngfuncs.GetMaxClients()); + + if(!theHasCommander) + { + LocalizeString(kNoCommander, outCommanderName); + return false; // No commander + } + else + { + + std::stringstream theStream; + char buff[512+sizeof(hud_player_info_t)]; + + string theCommanderText; + LocalizeString(kCommander, theCommanderText); + + theStream << theCommanderText; + theStream << ": "; + + hud_player_info_t *thePlayerInfo=(hud_player_info_t *)&buff[0]; + memset(thePlayerInfo, 0, 512+sizeof(hud_player_info_t)); + memset(thePlayerInfo->padding, 0xe, sizeof(thePlayerInfo->padding)); + gEngfuncs.pfnGetPlayerInfo(theCommander, thePlayerInfo); + + if(thePlayerInfo->name) + { + const int kMaxCommNameLen = 8; + theStream << string(thePlayerInfo->name).substr(0, kMaxCommNameLen); + } + + outCommanderName = theStream.str(); + + return true; + + } + +} + +void AvHHud::UpdateMarineUI(float inCurrentTime) +{ + // Find commander label + Label* theCommanderStatusLabel = NULL; + + + if(this->mMapMode == MAP_MODE_NS) + { + + // Add handler for all pending request buttons (this does hotgroups too) + for(int i = 0; i < MESSAGE_LAST; i++) + { + AvHMessageID theMessageID = AvHMessageID(i); + + char theComponentName[256]; + sprintf(theComponentName, kPendingImpulseSpecifier, (int)theMessageID); + + AvHTechImpulsePanel* theTechImpulsePanel = NULL; + if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) + { + bool theVisibility = false; + + // Do we have any requests pending? + if(this->GetIsInTopDownMode()) + { + PendingRequestListType::iterator theIterator; + for(theIterator = this->mPendingRequests.begin(); theIterator != this->mPendingRequests.end(); theIterator++) + { + if(theIterator->first == theMessageID) + { + if(theIterator->second > 0) + { + theVisibility = true; + } + } + } + + switch(theMessageID) + { + case GROUP_SELECT_1: + case GROUP_SELECT_2: + case GROUP_SELECT_3: + case GROUP_SELECT_4: + case GROUP_SELECT_5: + case COMMANDER_SELECTALL: + theVisibility = true; + break; + } + } + + theTechImpulsePanel->setVisible(theVisibility); + } + } + } +} + + +void AvHHud::UpdateCountdown(float inCurrentTime) +{ + if(this->mCountDownClock != -1) + { + if(inCurrentTime - this->mCountDownClock > this->mLastTickPlayed) + { + // Play tick + this->PlayHUDSound(HUD_SOUND_COUNTDOWN); + this->mLastTickPlayed++; + } + + if(this->mLastTickPlayed > this->mNumTicksToPlay) + { + this->AddTooltip(kGameBegun, false); + this->mCountDownClock = -1; + } + } +} + +void AvHHud::UpdateHierarchy() +{ + + // Start the starting point on the map to our local position + this->mOverviewMap.SetMapExtents(this->GetMapName(), this->mMapExtents); + this->mOverviewMap.SetWorldPosition(gPredictedPlayerOrigin[0], gPredictedPlayerOrigin[1]); + + if(this->mHierarchy) + { + this->mHierarchy->SetFullScreen(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER); + } + if(this->mShowMapHierarchy) + { + this->mShowMapHierarchy->SetFullScreen(true); + } + +} + +string AvHHud::GetNameOfLocation(vec3_t inLocation) const +{ + string theLocationName; + + AvHSHUGetNameOfLocation(this->mInfoLocationList, inLocation, theLocationName); + + return theLocationName; +} + + +void AvHHud::UpdateInfoLocation() +{ + if(this->GetIsAlive() && this->GetHUDUser3() != AVH_USER3_ALIEN_EMBRYO) + { + // Don't clear our location, disconcerting to see our location disappear + //this->mLocationText = ""; + + this->mLocationText = this->GetNameOfLocation(gPredictedPlayerOrigin); + } + else + { + this->mLocationText = ""; + } +} + +bool AvHHud::GetIsCombatMode() const +{ + return (this->mMapMode == MAP_MODE_CO); +} + +bool AvHHud::GetIsNSMode() const +{ + return (this->mMapMode == MAP_MODE_NS); +} + +bool AvHHud::GetIsMouseInRegion(int inX, int inY, int inWidth, int inHeight) +{ + bool theMouseIsInRegion = false; + + if(this->GetInTopDownMode()) + { + int theMouseX, theMouseY; + this->GetMousePos(theMouseX, theMouseY); + + if((theMouseX >= inX) && (theMouseX <= (inX + inWidth))) + { + if((theMouseY >= inY) && (theMouseY <= (inY + inHeight))) + { + theMouseIsInRegion = true; + } + } + } + + return theMouseIsInRegion; +} + +void AvHHud::TraceEntityID(int& outEntityID) +{ + pmtrace_t tr; + vec3_t up, right, forward; + + // Trace forward to see if we see a player + gEngfuncs.pEventAPI->EV_PushPMStates(); + + // Now add in all of the players. + gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); + + gEngfuncs.pEventAPI->EV_SetTraceHull(2); + + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + int theLocalPlayerIndex = theLocalPlayer->index; + //AngleVectors(theLocalPlayer->curstate.angles, forward, right, up); + + pVector theRealView; + gEngfuncs.pfnAngleVectors(pmove->angles, forward, right, up); + + Vector theStartTrace; + //VectorMA(gPredictedPlayerOrigin, kMaxPlayerHullWidth, forward, theStartTrace); + VectorCopy(gPredictedPlayerOrigin, theStartTrace); + + Vector theEndTrace; + VectorMA(gPredictedPlayerOrigin, 8192, forward, theEndTrace); + bool theDone = false; + + do + { + gEngfuncs.pEventAPI->EV_PlayerTrace(theStartTrace, theEndTrace, PM_NORMAL, -1, &tr); + + // Ignore local player, and ignore the player we're spectating + int theHit = gEngfuncs.pEventAPI->EV_IndexFromTrace(&tr); + if(theHit == theLocalPlayerIndex) + { + VectorMA(tr.endpos, kHitOffsetAmount, forward, theStartTrace); + } + // We hit something + else if(tr.fraction < 1.0) + { + physent_t* pe = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); + if(pe) + { + int thePotentialEntity = pe->info; + //if((thePotentialEntity >= 1) && (thePotentialEntity < gEngfuncs.GetMaxClients())) + //{ + outEntityID = pe->info; + //} + } + theDone = true; + } + } while(!theDone); + + gEngfuncs.pEventAPI->EV_PopPMStates(); +} + +bool AvHHud::GetAlienHelpForMessage(int inMessageID, string& outHelpText, int& outPointCost) const +{ + bool theSuccess = false; + + char theNumber[8]; + sprintf(theNumber, "%d", inMessageID); + string theNumberString(theNumber); + + string theTechHelpText; + string theKey = string(kTechNodeHelpPrefix) + theNumberString; + if(LocalizeString(theKey.c_str(), theTechHelpText)) + { + string thePointString; + if(LocalizeString(kPointsSuffix, thePointString)) + { +/* + // Lookup point cost + for(UpgradeCostListType::const_iterator theIter = this->mPersonalUpgradeCosts.begin(); theIter != this->mPersonalUpgradeCosts.end(); theIter++) + { + if(theIter->first == inMessageID) + { + char theCostSuffix[128]; + sprintf(theCostSuffix, " (%d %s)", theIter->second, thePointString.c_str()); + outPointCost = theIter->second; + theTechHelpText += string(theCostSuffix); + outHelpText = theTechHelpText; + theSuccess = true; + break; + } + } +*/ + } + } + return theSuccess; +} + +bool AvHHud::GetDoesPlayerHaveOrder() const +{ + bool thePlayerHasOrder = false; + + for(OrderListType::const_iterator theIter = this->mOrders.begin(); theIter != this->mOrders.end(); theIter++) + { + // Draw the order if the order is for any plays that are in our draw player list + EntityInfo theReceiverPlayer = theIter->GetReceiver(); + + cl_entity_s* thePlayer = gEngfuncs.GetLocalPlayer(); + if(thePlayer) + { + int thePlayerIndex = thePlayer->index; + if (thePlayerIndex == theReceiverPlayer ) + { + thePlayerHasOrder = true; + break; + } + } + } + + return thePlayerHasOrder; +} + + +bool AvHHud::GetHelpForMessage(int inMessageID, string& outHelpText) const +{ + bool theSuccess = false; + + char theNumber[8]; + sprintf(theNumber, "%d", (int)inMessageID); + string theNumberString(theNumber); + + string theKey = string(kTechNodeLabelPrefix) + theNumberString; + string theTechNodeLabel; + if(LocalizeString(theKey.c_str(), theTechNodeLabel)) + { + theKey = string(kTechNodeHelpPrefix) + theNumberString; + string theTechNodeHelp; + if(LocalizeString(theKey.c_str(), theTechNodeHelp)) + { + outHelpText = /*theTechNodeLabel + " " +*/ theTechNodeLabel; + theSuccess = true; + + int theCost; + bool theResearchable; + float theTime; + this->mTechNodes.GetResearchInfo((AvHMessageID)(inMessageID), theResearchable, theCost, theTime); + + // Add cost + if(theCost > 0) + { + string theCostString; + if(AvHSHUGetDoesTechCostEnergy((AvHMessageID)inMessageID)) + { + LocalizeString(kEnergyPrefix, theCostString); + } + else + { + LocalizeString(kMessageButtonCost, theCostString); + } + + outHelpText += " "; + outHelpText += theCostString; + outHelpText += " "; + outHelpText += MakeStringFromInt(theCost); + + // Draw description below + //outHelpText += "\n"; + //outHelpText += theTechNodeHelp; + } + } + } + + return theSuccess; +} + +void AvHHud::UpdateMusic(float inCurrentTime) +{ + bool theMusicEnabled = false; + + if(this->GetGameStarted()) + { + // If we're entering play mode and music is enabled, allow playing of music + if(this->GetHUDPlayMode() != PLAYMODE_READYROOM && (cl_musicenabled->value == 1.0f) && (cl_musicvolume->value > 0)) + { + theMusicEnabled = true; + } + } + + this->SetMusicAllowed(theMusicEnabled); + + UIHud::UpdateMusic(inCurrentTime); +} + +void AvHHud::UpdatePieMenuControl() +{ + // Set which pie menu to use ("" for none) + bool theScoreBoardIsOpen = false; + + ScorePanel* theScoreBoard = gViewPort->GetScoreBoard(); + if(theScoreBoard && theScoreBoard->isVisible()) + { + theScoreBoardIsOpen = true; + } + + if(theScoreBoardIsOpen) + { + AvHPieMenuHandler::SetPieMenuControl(""); + } + else + { + AvHPieMenuHandler::SetPieMenuControl(this->mPieMenuControl); + } + + // Clear all nodes in case VGUI didn't catch events (seems to happen with lag) + if(!gPieMenuHandler.GetIsPieMenuOpen()) + { + PieMenu* theCurrentPieMenu = gPieMenuHandler.GetActivePieMenu(); + if(theCurrentPieMenu) + { + theCurrentPieMenu->SetFadeState(false); + } + } + + // If we're dead, make sure the popup menu is closed + if(!this->GetIsAlive(false)) + { + gPieMenuHandler.ClosePieMenu(); + } +} + +bool AvHHud::GetEntityInfoString(int inEntityID, string& outEntityInfoString, bool& outIsEnemy) +{ + bool theSuccess = false; + bool theIsEnemy = false; + + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(inEntityID); + if(theEntity) + { + AvHTeamNumber theTeam = this->GetHUDTeam(); + + if((inEntityID >= 1) && (inEntityID <= gEngfuncs.GetMaxClients())) + { + hud_player_info_t thePlayerInfo; + gEngfuncs.pfnGetPlayerInfo(inEntityID, &thePlayerInfo); + + string thePrePendString; + + // Don't show cloaked enemies + if((theTeam != TEAM_IND) && !this->GetInTopDownMode() && ((theEntity->curstate.team == theTeam) || (theEntity->curstate.rendermode == kRenderNormal))) + { + //string thePostPendString; + if((theEntity->curstate.team == theTeam)) + { + //LocalizeString(kFriendText, thePrePendString); + //sprintf(thePostPendString, " (health: %d)", theEntity->curstate.health); + } + else + { + LocalizeString(kEnemyText, thePrePendString); + theIsEnemy = true; + } + + if(thePrePendString != "") + { + outEntityInfoString = thePrePendString + " - "; + } + } + + if(thePlayerInfo.name) + { + outEntityInfoString += thePlayerInfo.name;// + thePostPendString; + + // Get string from status bar and append it + const char* theStatusCStr = this->m_StatusBar.GetStatusString(); + if(strlen(theStatusCStr) > 0) + { + outEntityInfoString += string(theStatusCStr); + } + + outIsEnemy = theIsEnemy; + theSuccess = true; + } + + // sprintf(thePlayerName, "%s (health: %d)", thePlayerInfo.name, thePlayer->curstate.health); + //} + } + else + { + bool theAutoHelpEnabled = gEngfuncs.pfnGetCvarFloat(kvAutoHelp); + + // Return the type of thing it is + AvHUser3 theUser3 = (AvHUser3)(theEntity->curstate.iuser3); + + if(this->GetTranslatedUser3Name(theUser3, outEntityInfoString)) + { + if((theEntity->curstate.team != TEAM_IND) && (theEntity->curstate.team != theTeam)) + { + outIsEnemy = true; + } + + // Upper case first character + if(outEntityInfoString.length() > 0) + { + int theFirstChar = outEntityInfoString[0]; + int theUpperFirstChar = toupper(theFirstChar); + outEntityInfoString[0] = theUpperFirstChar; + } + + // Assume status bar set to health/armor/info data + const char* theStatusCStr = this->m_StatusBar.GetStatusString(); + bool theHasStatusString = (strlen(theStatusCStr) > 0); + if(theHasStatusString) + { + outEntityInfoString += string(theStatusCStr); + } + + if(theAutoHelpEnabled) + { + string theDescription; + bool theIsFriendly = (this->GetHUDTeam() == theEntity->curstate.team); + if(this->GetTranslatedUser3Description(theUser3, theIsFriendly, theDescription)) + { + outEntityInfoString += " - " + theDescription; + } + } + + // Only display help when asked for or important + if(theAutoHelpEnabled || theHasStatusString) + { + theSuccess = true; + } + } + } + } + + return theSuccess; +} + +void AvHHud::UpdateEntityID(float inCurrentTime) +{ + this->mBuildingEffectsEntityList.clear(); + + bool theSetHelpMessage = false; + bool theSetReticleMessage = false; + + //char* theNewPlayerName = NULL; + int theEntityIndex = -1; + + if(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) + { + int theCurrentX, theCurrentY; + this->GetMousePos(theCurrentX, theCurrentY); + + // Don't show help when mouse is over the UI + float theNormX = ((float)theCurrentX)/(ScreenWidth()); + float theNormY = ((float)theCurrentY)/(ScreenHeight()); + if(!this->GetIsRegionBlockedByUI(theNormX, theNormY)) + { + Vector theNormRay; + + CreatePickingRay(theCurrentX, theCurrentY, theNormRay); + + // Look for player and entities under/near the mouse + AvHSHUGetEntityAtRay(this->GetVisualOrigin(), theNormRay, theEntityIndex); + } + } + else if(g_iUser1 != OBS_IN_EYE) + { + // Trace entity id in front of us + this->TraceEntityID(theEntityIndex); + } + + if(this->mSelectingWeaponID != -1) + { + // Look up help, set our current help to it + string theWeaponHelpKey; + sprintf(theWeaponHelpKey, kWeaponHelpText, this->mSelectingWeaponID); + + string theHelpText; + if(LocalizeString(theWeaponHelpKey.c_str(), theHelpText)) + { + // Get damage amount from weapon + ASSERT(this->mSelectingWeaponID >= 0); + ASSERT(this->mSelectingWeaponID < 32); + WEAPON* theWeapon = gWR.GetWeapon(this->mSelectingWeaponID); + if( theWeapon ) + { + int theDamage = theWeapon->iMax2; + + char theHelpTextWithDamage[1024]; + sprintf(theHelpTextWithDamage, theHelpText.c_str(), theDamage); + + this->SetHelpMessage(theHelpTextWithDamage); + theSetHelpMessage = true; + } + } + } + else if(this->mTechHelpText != "") + { + this->SetHelpMessage(this->mTechHelpText, false, .8f, .6f); + theSetHelpMessage = true; + } + else if(this->mSelectingNodeID > 0) + { + char theNumber[8]; + sprintf(theNumber, "%d", this->mSelectingNodeID); + string theNumberString(theNumber); + + string theKey = string(kTechNodeHelpPrefix) + theNumberString; + if(LocalizeString(theKey.c_str(), this->mReticleInfoText)) + { + string theCostString; + if(LocalizeString(kCostMarker, theCostString)) + { + string thePointsString; + if(LocalizeString(kPointsSuffix, thePointsString)) + { + } + + // Don't draw marine sayings, as they are printed on the controls and the aliens are using the titles.txt entries + if(this->GetIsMarine()) + { + switch(this->mSelectingNodeID) + { + case SAYING_1: + case SAYING_2: + case SAYING_3: + case SAYING_4: + case SAYING_5: + case SAYING_6: + case SAYING_7: + case SAYING_8: + case SAYING_9: + this->mReticleInfoText = ""; + break; + } + } + + this->SetHelpMessage(this->mReticleInfoText); + theSetHelpMessage = true; + } + } + } + + if(theEntityIndex > 0) + { + // Don't draw info for cloaked structures + cl_entity_s* theProgressEntity = gEngfuncs.GetEntityByIndex(theEntityIndex); + if(theProgressEntity) + { + if((theProgressEntity->curstate.rendermode != kRenderTransTexture) || (theProgressEntity->curstate.renderamt > kAlienStructureCloakAmount)) + { + if(std::find(this->mBuildingEffectsEntityList.begin(), this->mBuildingEffectsEntityList.end(), theEntityIndex) == this->mBuildingEffectsEntityList.end()) + { + this->mBuildingEffectsEntityList.push_back(theEntityIndex); + } + + bool theEntityIsPlayer = ((theEntityIndex > 0) && (theEntityIndex <= gEngfuncs.GetMaxClients())); + + string theHelpText; + bool theIsEnemy = false; + + if(this->GetEntityInfoString(theEntityIndex, theHelpText, theIsEnemy)) + { + // Set color to red if enemy, otherise to our HUD color + int theR, theG, theB; + if(theIsEnemy) + { + theR = 255; + theG = theB = 0; + } + else + { + this->GetPrimaryHudColor(theR, theG, theB, true, false); + } + + // If we have auto help on, or we're in top down mode, display as help + if(gEngfuncs.pfnGetCvarFloat(kvAutoHelp) || this->GetInTopDownMode()) + { + this->SetHelpMessage(theHelpText); + + this->mHelpMessage.SetRGB(theR, theG, theB); + + theSetHelpMessage = true; + } + // else, display as reticle message + else + { + this->SetReticleMessage(theHelpText); + + this->mReticleMessage.SetRGB(theR, theG, theB); + + theSetReticleMessage = true; + } + } + } + else + { + //char theMessage[128]; + //sprintf(theMessage, "Entity %d cloaked, not drawing circle.", theProgressEntity->curstate.iuser3); + //CenterPrint(theMessage); + } + } + } + + float theTimePassed = (inCurrentTime - this->mTimeOfLastUpdate); + if(!theSetHelpMessage) + { + this->mHelpMessage.FadeText(theTimePassed, true); + } + if(!theSetReticleMessage) + { + this->mReticleMessage.FadeText(theTimePassed, true); + } +} + +int AvHHud::GetMenuTechSlots() const +{ + return this->mMenuTechSlots; +} + +const AvHTechTree& AvHHud::GetTechNodes() const +{ + return this->mTechNodes; +} + +void AvHHud::GetTooltipDrawingInfo(float& outNormX, float& outNormY) const +{ + outNormX = kHelpMessageLeftEdgeInset; + outNormY = kHelpMessageTopEdgeInset; + + if(this->GetPlayMode() == PLAYMODE_READYROOM) + { + outNormY = 1.0f; + } + else if(this->GetInTopDownMode()) + { + outNormY = kHelpMessageCommanderTopEdgeInset; + } + else if(this->GetIsAlien()) + { + outNormY = kHelpMessageAlienTopEdgeInset; + } + + outNormY -= kToolTipVerticalSpacing; +} + +string AvHHud::GetRankTitle(bool inShowUnspentLevels) const +{ + string theText; + + char* theTeamName = this->GetIsMarine() ? "Marine" : "Alien"; + int theCurrentLevel = this->GetHUDExperienceLevel(); + + char theCharArray[512]; + sprintf(theCharArray, kRankText, theTeamName, theCurrentLevel); + LocalizeString(theCharArray, theText); + + // Add unspent levels, if any + int theUnspentLevels = (this->mExperienceLevel - this->mExperienceLevelSpent - 1); + if(inShowUnspentLevels && (theUnspentLevels > 0)) + { + // We can't accurately draw player's unspent levels when we're dead, as we only know our own + if(this->GetIsAlive(false)) + { + string theUnspentLevelText = "(+" + MakeStringFromInt(theUnspentLevels) + ")"; + theText += theUnspentLevelText; + } + } + + return theText; +} + +void AvHHud::GetReticleTextDrawingInfo(float& outNormX, float& outNormY, bool& outCentered) const +{ + outCentered = false; + + if(this->GetInTopDownMode()) + { + int theCurrentX, theCurrentY; + this->GetMousePos(theCurrentX, theCurrentY); + outNormX = theCurrentX/(float)ScreenWidth(); + outNormY = theCurrentY/(float)ScreenHeight(); + + // Move text up a bit so it doesn't obscure + outNormY -= .1f; + + outCentered = true; + } + else + { + if(gEngfuncs.pfnGetCvarFloat(kvCenterEntityID)) + { + outNormX = .5f; + outNormY = .5f; + outCentered = true; + } + else + { + outNormX = kHelpMessageLeftEdgeInset; + outNormY = kHelpMessageTopEdgeInset - kReticleMessageHeight; + + // Alien HUD forces this to be inset a bit + if(this->GetIsAlien()) + { + outNormX = kHelpMessageAlienLeftedgeInset + kReticleMessageAlienLeftInset; + outNormY = kHelpMessageAlienTopEdgeInset - kReticleMessageHeight; + } + } + } +} + +// Assumes that the tooltips aren't centered +void AvHHud::UpdateTooltips(float inCurrentTime) +{ + float theReticleX; + float theReticleY; + this->GetTooltipDrawingInfo(theReticleX, theReticleY); + + float theBottomMostY = theReticleY; + + float theTimePassed = inCurrentTime - this->mTimeOfLastUpdate; + float kMovement = 1.0f*theTimePassed; + + int theBottomScreenY = theBottomMostY*ScreenHeight(); + + // Clear all tooltips on a role change, or on a playmode change + AvHUser3 theCurrentUser3 = this->GetHUDUser3(); + AvHTeamNumber theCurrentTeam = this->GetHUDTeam(); + AvHPlayMode thePlayMode = this->GetPlayMode(); + + if((theCurrentUser3 != this->mLastUser3) || (this->mLastTeamNumber != theCurrentTeam) || ((thePlayMode != this->mLastPlayMode) && (this->mLastPlayMode != PLAYMODE_UNDEFINED)) ) + { + this->mTooltips.clear(); + } + + // Stuff to get reset on a team change + if(this->mLastTeamNumber != theCurrentTeam) + { + this->mExperienceLevelLastDrawn = 1; + } + + // Run through list, dropping them down as far as they can go. Assumes the first one in the list is the bottommost one + for(AvHTooltipListType::iterator theIter = this->mTooltips.begin(); theIter != this->mTooltips.end(); /* no inc */) + { + // Recompute settings, if it hasn't been updated before + theIter->RecomputeIfNeccessary(); + + // Get height of current tool tip + int theTooltipScreenHeight = theIter->GetScreenHeight(); + + // Move it down towards the current limit + float theCurrentMinScreenY = theIter->GetNormalizedScreenY(); + int theCurrentTipScreenBottom = (int)(theCurrentMinScreenY) + theTooltipScreenHeight; + + float theCurrentMaxNormY = (float)(theBottomScreenY - theTooltipScreenHeight)/ScreenHeight(); + float theNewNormY = min(theCurrentMaxNormY, (theCurrentMinScreenY + kMovement)); + + // Now this is a crazy bug! In release mode without the following two statements, theNewNormY isn't > theCurrentMinScreenY when it should be. + // It's as if a little time is needed between the computation of theNewNormY and using theNewNormY for it to work + char theMessage[256]; + sprintf(theMessage, "theNewNormY %f, minScreenY: %f)", theNewNormY, theCurrentMinScreenY); + //CenterPrint(theMessage); + + // If we moved it down + if(theNewNormY > theCurrentMinScreenY) + { + // Fade it in while it's dropping + theIter->FadeText(theTimePassed, false); + } + else + { + if(theIter == this->mTooltips.begin()) + { + // If it's at the bottom of the list, start fading it out + theIter->FadeText(theTimePassed, true); + } + } + + // Set the new position + theIter->SetNormalizedScreenY(theNewNormY); + + // Subtract it's height to the current limit + theBottomScreenY -= theTooltipScreenHeight; + + // Subtract out small spacing amount + theBottomScreenY -= max(1, kToolTipVerticalSpacing*ScreenHeight()); + + // If it's totally faded out, remove it from the list, else process next + int theAlpha = theIter->GetA(); + if(theAlpha == 0) + { + theIter = this->mTooltips.erase(theIter); + } + else + { + theIter++; + } + + //char theTempBuffer[256]; + //sprintf(theTempBuffer, "UpdateTooltips, alpha: %d\n", theAlpha); + //CenterPrint(theTempBuffer); + } + + // Update combat upgrade menu too + if(this->GetIsCombatMode()) + { + const int kScrollDirection = this->mDrawCombatUpgradeMenu ? 1 : -1; + const float kScrollingSpeed = .7f*kScrollDirection; + + float theXDiff = (theTimePassed*kScrollingSpeed); + const float kNormalizedWidth = this->mCombatUpgradeMenu.GetNormalizedMaxWidth(); + + const float kLeftEdgeInset = .02f; + float theNewX = this->mCombatUpgradeMenu.GetNormalizedScreenX() + theXDiff; + theNewX = max(-kNormalizedWidth, theNewX); + theNewX = min(theNewX, kLeftEdgeInset); + + this->mCombatUpgradeMenu.SetNormalizedScreenX(theNewX); + } +} + +void AvHHud::UpdateStructureNotification(float inCurrentTime) +{ + const float kTimeToDisplayIcon = 6.0f; + const int kMaxIcons = 5; + + for(StructureHUDNotificationListType::iterator theIter = this->mStructureNotificationList.begin(); theIter != this->mStructureNotificationList.end(); /* no inc */) + { + if((inCurrentTime > (theIter->mTime + kTimeToDisplayIcon)) || (this->mStructureNotificationList.size() > kMaxIcons)) + { + theIter = this->mStructureNotificationList.erase(theIter); + } + else + { + theIter++; + } + } +} + + +//void AvHHud::FadeText(float inCurrentTime, bool inFadeDown) +//{ +// const float kReticleInfoFadeUpSpeed = 500; +// const float kReticleInfoFadeDownSpeed = -150; +// +// // Fade reticle nicely +// int theFadeSpeed = inFadeDown ? kReticleInfoFadeDownSpeed : kReticleInfoFadeUpSpeed; +// +// float theTimePassed = (inCurrentTime - this->mTimeOfLastUpdate); +// float theNewAlpha = this->mReticleInfoColorA + theTimePassed*theFadeSpeed; +// this->mReticleInfoColorA = max(0, min(255, theNewAlpha)); +//} + +float AvHHud::GetTimeOfLastUpdate() const +{ + return this->mTimeOfLastUpdate; +} + +void AvHHud::UpdateProgressBar() +{ + // this->HideProgressStatus(); + + float thePercentage; + if(gMiniMap.GetIsProcessing(&thePercentage)) + { + if(gMiniMap.WriteSpritesIfJustFinished()) + { + this->HideProgressStatus(); + } + else + { + this->SetProgressStatus(thePercentage); + } + } +// else if(this->mProgressBarEntityIndex == -1) +// { +// this->HideGenericProgressStatus(); +// } + else + { + // Look up entity, and set progress according to it's fuser1 + cl_entity_s* theProgressEntity = gEngfuncs.GetEntityByIndex(this->mProgressBarEntityIndex); + if(theProgressEntity) + { + ASSERT(this->mProgressBarParam >= 1); + ASSERT(this->mProgressBarParam <= 5); + + float theProgress = 0.0f; + if ( this->mProgressBarParam == 5 ) { + thePercentage=(float)(this->mProgressBarCompleted)/100.0f; + } + else { + switch(this->mProgressBarParam) + { + case 1: + theProgress = theProgressEntity->curstate.fuser1; + break; + case 2: + theProgress = theProgressEntity->curstate.fuser2; + break; + case 3: + theProgress = theProgressEntity->curstate.fuser3; + break; + case 4: // NOTE: check delta.lst for fuser4, it isn't propagated currently + theProgress = theProgressEntity->curstate.fuser4; + break; + } + + if((this->GetHUDUser3() == AVH_USER3_ALIEN_EMBRYO) || this->GetIsDigesting() ) + { + theProgress = pmove->fuser3; + } + + thePercentage = theProgress/kNormalizationNetworkFactor; + } + int theType = (this->GetIsAlien())? PROGRESS_BAR_ALIEN: PROGRESS_BAR_MARINE; + if(thePercentage < 1.0f) + { + this->SetProgressStatus(thePercentage, theType); + } +// else +// { +// this->HideGenericProgressStatus(); +// } + } + else + { + // Look at selection. If selection has research and research isn't done, draw research bar. Else, hide research bar + if(this->mSelected.size() == 1) + { + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(*this->mSelected.begin()); + if(theEntity) + { + this->HideProgressStatus(); + //this->HideResearchProgressStatus(); + + bool theIsBuilding, theIsResearching; + float thePercentage; + AvHSHUGetBuildResearchState(theEntity->curstate.iuser3, theEntity->curstate.iuser4, theEntity->curstate.fuser1, theIsBuilding, theIsResearching, thePercentage); + + if(theIsResearching && (thePercentage > 0) && (thePercentage < 1.0f)) + { + this->SetProgressStatus(thePercentage, PROGRESS_BAR_DEFAULT); + //this->SetResearchProgressStatus(thePercentage); + } + } + } + } + } +} + +void AvHHud::GhostBuildingCallback(struct tempent_s* inEntity, float inFrametime, float inCurrentTime) +{ + if(this->mGhostBuilding != MESSAGE_NULL) + { + // Don't let it die + inEntity->die = gClientTimeLastUpdate + 2.0f; + + // Update position to be where mouse is + VectorCopy(this->mGhostWorldLocation, inEntity->entity.origin); + + // Visually indicate whether this is a valid position or not + if(this->mCurrentGhostIsValid) + { + inEntity->entity.curstate.renderfx = kRenderFxGlowShell; + inEntity->entity.curstate.rendercolor.r = 0; + inEntity->entity.curstate.rendercolor.g = 255; + inEntity->entity.curstate.rendercolor.b = 0; + inEntity->entity.curstate.renderamt = kShellRenderAmount; + } + else + { + inEntity->entity.curstate.renderfx = kRenderFxGlowShell; + inEntity->entity.curstate.rendercolor.r = 255; + inEntity->entity.curstate.rendercolor.g = 0; + inEntity->entity.curstate.rendercolor.b = 0; + inEntity->entity.curstate.renderamt = kShellRenderAmount; + } + } + else + { + // Kill it off immediately + inEntity->die = gClientTimeLastUpdate; + } +} + +void AvHHud::UpdateBuildingPlacement() +{ + if(this->mGhostBuilding != MESSAGE_NULL) + { + // Fetch current mouse up/down state from gScrollHandler and store + bool theMouseOneDown = gScrollHandler.GetMouseOneDown() && !gCommanderHandler.GetMouseOneDown(); + bool theMouseTwoDown = gScrollHandler.GetMouseTwoDown(); + + // If we haven't created the temp entity, create it + if(!this->mCreatedGhost) + { + // Create the temporary entity + int theModelIndex; + char* theModelName = AvHSHUGetBuildTechModelName(this->mGhostBuilding); + if(theModelName) + { + if(this->mLastGhostBuilding) + { + this->mLastGhostBuilding->die = -1; + this->mLastGhostBuilding = NULL; + } + + vec3_t theOrigin = this->GetVisualOrigin(); + struct model_s* theModel = gEngfuncs.CL_LoadModel(theModelName, &theModelIndex); + TEMPENTITY* theTempEntity = gEngfuncs.pEfxAPI->CL_TentEntAllocCustom(gPredictedPlayerOrigin, theModel, 0, CLinkGhostBuildingCallback); + if(theTempEntity) + { + theTempEntity->die += 10.0f; + theTempEntity->flags |= FTENT_PERSIST; + // Temp entities interpret baseline origin as velocity. + VectorCopy(vec3_origin, theTempEntity->entity.baseline.origin); + + // Set special properties for some models + if(this->mGhostBuilding == BUILD_SCAN) + { + theTempEntity->entity.curstate.rendermode = kRenderTransAdd; + theTempEntity->entity.curstate.renderamt = 255; + + theTempEntity->entity.baseline.rendermode = kRenderTransAdd; + theTempEntity->entity.baseline.renderamt = 255; + } + + this->mCreatedGhost = true; + } + + this->mLastGhostBuilding = theTempEntity; + } + } + + // Update location we draw ghosted entity + int theMouseX, theMouseY; + this->GetMousePos(theMouseX, theMouseY); + + Vector theNormMousePos; + CreatePickingRay(theMouseX, theMouseY, theNormMousePos); + + //char theMessage[256]; + //sprintf(theMessage, "Ghost: %f, %f, %f", this->mGhostWorldLocation[0], this->mGhostWorldLocation[1],this->mGhostWorldLocation[2]); + //CenterPrint(theMessage); + + // Was either mouse button pressed? + bool theMouseOneReleased = (theMouseOneDown && !this->mMouseOneDown); + bool theMouseTwoReleased = (theMouseTwoDown && !this->mMouseTwoDown); + if(theMouseOneReleased) + { + VectorCopy(this->mLeftMouseWorldStart, this->mNormBuildLocation); + } + else if(theMouseTwoReleased) + { + VectorCopy(this->mRightMouseWorldStart, this->mNormBuildLocation); + } + + // Test to see if we're in a valid position + this->mCurrentGhostIsValid = false; + + vec3_t theLocation; + if(AvHSHUTraceAndGetIsSiteValidForBuild(this->mGhostBuilding, this->GetVisualOrigin(), theNormMousePos, &theLocation)) + { + // Update with cost and research info + bool theIsResearchable; + int theCost; + float theTime; + if(this->mTechNodes.GetResearchInfo(this->mGhostBuilding, theIsResearchable, theCost, theTime)) + { + // Ghost is valid if message available and + // we have enough resources OR + // tech takes energy and we have enough energy + this->mCurrentGhostIsValid = false; + + if(this->mTechNodes.GetIsMessageAvailable(this->mGhostBuilding)) + { + bool theTakesEnergy = AvHSHUGetDoesTechCostEnergy(this->mGhostBuilding); + if((theCost <= this->mResources) || (theTakesEnergy)) + { + this->mCurrentGhostIsValid = true; + } + } + } + } + + // Draw at selection start range * invalid range multiplier if invalid placement, draw on at target location if valid + //VectorMA(this->GetVisualOrigin(), kSelectionStartRange*kBuildInvalidRangeMultiplier, theNormMousePos, this->mGhostWorldLocation); + VectorMA(this->GetVisualOrigin(), kSelectionStartRange*8, theNormMousePos, this->mGhostWorldLocation); + //if(this->mCurrentGhostIsValid) + //{ + VectorCopy(theLocation, this->mGhostWorldLocation); + //} + + if((theMouseOneReleased) || (theMouseTwoReleased)) + { + // If this is a valid location + if(this->mCurrentGhostIsValid) + { + // Play sound + this->PlayHUDSound(HUD_SOUND_PLACE_BUILDING); + + // Remember it for input to grab + this->mValidatedBuilding = this->mGhostBuilding; + } + } + } +} + +void AvHHud::CancelBuilding() +{ + SetGhostBuildingMode(MESSAGE_NULL); + this->mValidatedBuilding = MESSAGE_NULL; +} + +void AvHHud::UpdateSelection() +{ + // Fetch current mouse up/down state from gScrollHandler and store + bool theMouseOneDown = gScrollHandler.GetMouseOneDown() && !gCommanderHandler.GetMouseOneDown(); + bool theMouseTwoDown = gScrollHandler.GetMouseTwoDown(); + + int theCurrentX, theCurrentY; + this->GetMousePos(theCurrentX, theCurrentY); + + CreatePickingRay(theCurrentX, theCurrentY, this->mMouseWorldPosition); + //ASSERT(this->mMouseWorldPosition.z < 0.0f); + + // Left mouse button // + // If mouse just pressed, set starting point + if(theMouseOneDown && !this->mMouseOneDown) + { + this->mMouseOneStartX = theCurrentX; + this->mMouseOneStartY = theCurrentY; + this->mLeftMouseStarted = true; + + //CreatePickingRay(theCurrentX, theCurrentY, this->mLeftMouseWorldStart); + //ASSERT(this->mLeftMouseWorldStart.z < 0.0f); + VectorCopy(this->mMouseWorldPosition, this->mLeftMouseWorldStart); + } + else + { + this->mLeftMouseStarted = false; + } + + // If mouse just released, set flag to indicate selection just changed + if(!theMouseOneDown && this->mMouseOneDown) + { + //CreatePickingRay(theCurrentX, theCurrentY, this->mLeftMouseWorldEnd); + //ASSERT(this->mLeftMouseWorldEnd.z < 0.0f); + VectorCopy(this->mMouseWorldPosition, this->mLeftMouseWorldEnd); + + //this->mSelectionJustChanged = true; + this->mLeftMouseEnded = true; + } + else + { + this->mLeftMouseEnded = false; + } + + // Right mouse button // + // If mouse two just pressed, set starting point + if(theMouseTwoDown && !this->mMouseTwoDown) + { + this->mMouseTwoStartX = theCurrentX; + this->mMouseTwoStartY = theCurrentY; + this->mRightMouseStarted = true; + + //CreatePickingRay(theCurrentX, theCurrentY, this->mRightMouseWorldStart); + //ASSERT(this->mRightMouseWorldStart.z < 0.0f); + VectorCopy(this->mMouseWorldPosition, this->mRightMouseWorldStart); + } + else + { + this->mRightMouseStarted = false; + } + + // If mouse just released, set flag to indicate selection just changed + if(!theMouseTwoDown && this->mMouseTwoDown) + { + //CreatePickingRay(theCurrentX, theCurrentY, this->mRightMouseWorldEnd); + //ASSERT(this->mRightMouseWorldEnd.z < 0.0f); + VectorCopy(this->mMouseWorldPosition, this->mRightMouseWorldEnd); + + //this->mSelectionJustChanged = true; + this->mRightMouseEnded = true; + } + else + { + this->mRightMouseEnded = false; + } + + // Set extents of marquee control + this->mSelectionBox->SetStartPos(this->mMouseOneStartX, this->mMouseOneStartY); + this->mSelectionBox->SetEndPos(theCurrentX, theCurrentY); + + // Set visibility state of marquee control + //this->mSelectionBox->setVisible(theMouseOneDown); + this->mSelectionBox->setVisible(false); + + this->mSelectionBoxX1 = mMouseOneStartX; + this->mSelectionBoxY1 = mMouseOneStartY; + this->mSelectionBoxX2 = theCurrentX; + this->mSelectionBoxY2 = theCurrentY; + + this->mSelectionBoxVisible = theMouseOneDown; + + // If we're just selecting stuff, don't want to hit this button by mistake + //gCommanderHandler.SetActive(!theMouseOneDown); + + // Change context sensitive cursor depending on current position + //if(this->mSelected.size() > 0) + //{ +// if(this->mGhostBuilding == MESSAGE_NULL) +// { +// Vector theCurrentMouseRay; + // CreatePickingRay(theCurrentX, theCurrentY, theCurrentMouseRay); +// +// int theTargetIndex; +// AvHOrderTargetType theTargetType; +// Vector theTargetLocation; +// AvHUser3 theUser3 = AVH_USER3_NONE; +// AvHOrderType theOrderType = AvHGetDefaultOrderType(this->GetHUDTeam(), this->GetVisualOrigin(), theCurrentMouseRay, theTargetIndex, theTargetLocation, theUser3, theTargetType); + // Test UI blocking +// theOrderType = ORDERTYPEL_DEFAULT; +// if(!AvHSHUGetIsRegionBlockedByUI((float)theCurrentX/ScreenWidth, (float)theCurrentY/ScreenHeight)) +// { +// theOrderType = ORDERTYPET_GUARD; +// } +// this->SetCursor(theOrderType); + + // Change cursor depending on order type + //if(theOrderType != ORDERTYPEL_MOVE && this->mSelected.size() > 0 ) + //{ +// if(!this->GetIsRegionBlockedByUI(theCurrentX/ScreenWidth(), theCurrentY/ScreenHeight())) +// { + //this->SetCursor(theOrderType); +// } + //} +// } + //} + + if(this->mLeftMouseEnded) + { + // Select all units at this click or in this area (but don't select when clicking a building down) + if(!this->mPlacingBuilding) + { + gSelectionHelper.QueueSelection(this->GetVisualOrigin(), this->mLeftMouseWorldStart, this->mLeftMouseWorldEnd, this->GetHUDTeam()); + gSelectionHelper.ProcessPendingSelections(); + } + } + + if(gSelectionHelper.SelectionResultsWaiting()) + { + EntityListType theNewSelection; + gSelectionHelper.GetAndClearSelection(theNewSelection); +// this->mNumLocalSelectEvents++; + + if(theNewSelection != this->mSelected) + { + this->mSelected = theNewSelection; + this->mSelectionJustChanged = true; + } + + this->ClearTrackingEntity(); + } + + // If selection just changed, make sure we have selection effects for everyone + if(this->mSelectionJustChanged) + { + // Create effects + this->SetSelectionEffects(this->mSelected); + + this->PlayHUDSound(HUD_SOUND_SELECT); + + gCommanderHandler.SetSelectedUnits(this->mSelected); + + // Clear flag + this->mSelectionJustChanged = false; + +// // Set default order mode +// if(this->mSelected.size() > 0) +// { +// this->mOrderMode = ORDERTYPEL_DEFAULT; +// } +// else +// { +// this->mOrderMode = ORDERTYPE_UNDEFINED; +// } + } + + this->UpdateBuildingPlacement(); + + // Store current mouse state + this->mMouseOneDown = theMouseOneDown; + this->mMouseTwoDown = theMouseTwoDown; +} + +void AvHHud::UpdateBuildResearchText() +{ + // Hide unchanging ("unhelpful"?) help text after this amount of time + const float kHelpTextInterval = 2.5f; + + if(this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) + { + Label* theHelpTextLabel = NULL; + if(this->GetManager().GetVGUIComponentNamed(kTechHelpText, theHelpTextLabel)) + { + gCommanderHandler.RecalculateBuildResearchText(); + + // Display build/research text + string theBuildResearchText = gCommanderHandler.GetBuildResearchText(); + theHelpTextLabel->setText(theBuildResearchText.c_str()); + + // Center it + int theWidth, theHeight; + theHelpTextLabel->getTextSize(theWidth, theHeight); + + int theX, theY; + theHelpTextLabel->getPos(theX, theY); + + int theScreenWidth = ScreenWidth(); + theHelpTextLabel->setPos(theScreenWidth/2 - theWidth/2, theY); + + // Vanish if no text (but keep build/research text visible) + theHelpTextLabel->setVisible(true); + if(theBuildResearchText.length() == 0)// || ((this->mTimeLastHelpTextChanged != -1) && (this->mTimeLastHelpTextChanged + kHelpTextInterval < this->mTimeOfLastUpdate))) + { + theHelpTextLabel->setVisible(false); + } + + // Display action button tool tip + string theTechHelpText = gCommanderHandler.GetTechHelpText(); + if(theTechHelpText != "") + { + this->SetActionButtonHelpMessage(theTechHelpText); + } + + if(theTechHelpText != this->mPreviousHelpText) + { + this->mTimeLastHelpTextChanged = this->mTimeOfLastUpdate; + } + + this->mPreviousHelpText = theTechHelpText; + } + } +} + +void AvHHud::UpdateTechNodes() +{ + this->UpdateBuildResearchText(); + + // Don't get new node until existing one has been processed + if(this->mTechEvent == MESSAGE_NULL) + { + //if(this->mGameStarted) + //{ + //AvHTechNode theTechNode; + //if(gCommanderHandler.GetAndClearTechNodePressed(theTechNode)) + AvHMessageID theMessageID; + if(gCommanderHandler.GetAndClearTechNodePressed(theMessageID)) + { + // Check for grouping and request events and handle separately + if(this->mTechNodes.GetIsMessageAvailable(theMessageID)) + { + bool theIsResearchable; + int theCost; + float theTime; + + if(this->mTechNodes.GetResearchInfo(theMessageID, theIsResearchable, theCost, theTime)) + { + if(AvHSHUGetIsBuildTech(theMessageID)) + { + // Don't check for enough points yet, they might get enough points before they put it down + + // Get ready to build it, don't send a research message + this->SetGhostBuildingMode(theMessageID); + } + else if(AvHSHUGetIsResearchTech(theMessageID) && theIsResearchable) + { + // If we hit cancel, and we're in building mode, get out of build mode and throw event away + if((theMessageID == MESSAGE_CANCEL) && (this->GetGhostBuilding() != MESSAGE_NULL)) + { + CancelBuilding(); + } + else if(theCost <= this->mResources) + { + this->mTechEvent = theMessageID; + } + else + { + // Emit error message that you don't have the resources + this->PlayHUDSound(HUD_SOUND_MARINE_MORE); + } + } + else if(theMessageID == BUILD_RECYCLE) + { + this->mTechEvent = theMessageID; + } + } + else + { + this->mTechEvent = theMessageID; + } + } + else if((theMessageID >= SAYING_1) && (theMessageID <= SAYING_9)) + { + this->mTechEvent = theMessageID; + } + } + //} + } +} + +void AvHHud::UpdateAmbientSounds() +{ + Vector theListenerPosition; + VectorCopy(gPredictedPlayerOrigin, theListenerPosition); + + // Commanders have a different PAS then themselves + if(this->mInTopDownMode) + { + VectorCopy(this->mCommanderPAS, theListenerPosition); + } + + for(AmbientSoundListType::iterator theIter = this->mAmbientSounds.begin(); theIter != this->mAmbientSounds.end(); theIter++) + { + theIter->StartPlayingIfNot(); + theIter->UpdateVolume(theListenerPosition); + } +} + +void AvHHud::UpdateFromEntities(float inCurrentTime) +{ + // Only update every so often for performance reasons + const float kEntityUpdateInterval = .4f; + if(inCurrentTime > (this->mTimeOfLastEntityUpdate + kEntityUpdateInterval)) + { + this->mHelpIcons.clear(); + this->mHelpEnts.clear(); + + bool theAutoHelpEnabled = gEngfuncs.pfnGetCvarFloat(kvAutoHelp); + + // Clear help icons + this->mHelpEnts.clear(); + + // Clear building effects + //this->mBuildingEffectsEntityList.clear(); + + this->mTimeOfLastEntityUpdate = inCurrentTime; + + // Scan for entities, adding help icons + // Only draw if enabled + const int kHelpDistance = 350; + const int kBuildDistance = 500; + const int kHealthDistance = 125; + + // Look for entities that have help icons in front of us. Don't search to far away, it could give players away. + EntityListType theHelpEntities; + AvHSHUGetEntities(-1, theHelpEntities); + + cl_entity_t* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + + for(EntityListType::iterator theIter = theHelpEntities.begin(); theIter != theHelpEntities.end(); theIter++) + { + vec3_t theLocation; + if(AvHSHUGetEntityLocation(*theIter, theLocation)) + { + float theDistance = VectorDistance((float*)&theLocation, theLocalPlayer->origin); + if(theDistance < kHelpDistance) + { + // If iuser3 isn't 0, try looking up an icon for it + physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theIter); + if(theEntity) + { + // Don't add cloaked entities (I wish this could be more general purpose) + if((theEntity->team == this->GetHUDTeam()) || (theEntity->rendermode == kRenderNormal)) + { + int theUser3 = theEntity->iuser3; + vec3_t theEntityOrigin = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs); + + // Some entities always draw + bool theAlwaysDraw = false; + switch(theUser3) + { + case AVH_USER3_WELD: + //case AVH_USER3_COMMANDER_STATION: + //case AVH_USER3_HIVE: + theAlwaysDraw = true; + break; + } + + if(theAutoHelpEnabled || theAlwaysDraw) + { + this->mHelpIcons.push_back( make_pair(theEntityOrigin, theUser3) ); + + // Push back entity for displaying helpful tooltips + this->mHelpEnts.push_back(*theIter); + } + } + } + } + } + } + } +} + +void AvHHud::UpdateViewModelEffects() +{ + cl_entity_t* theViewModel = GetViewEntity(); + if(theViewModel) + { + int theRenderMode = kRenderNormal; + int theRenderAmount = 0; + int theRenderFx = theViewModel->curstate.renderfx; + color24 theRenderColor = theViewModel->curstate.rendercolor; + short theSkin = 0; + + // Set the skin, stored in playerclass + + //cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + + // Use the visible player so that when we are spectating we can tell + // when the player is cloaked. + cl_entity_s* theLocalPlayer = GetVisiblePlayer(); + + if(theLocalPlayer) + { + + //theViewModel->curstate.skin = theLocalPlayer->curstate.skin; + theRenderMode = theLocalPlayer->curstate.rendermode; + theRenderAmount = theLocalPlayer->curstate.renderamt; + theRenderFx = theLocalPlayer->curstate.renderfx; + theRenderColor = theLocalPlayer->curstate.rendercolor; + theSkin = theLocalPlayer->curstate.skin; + // Hack to make cloaking work for viewmodels (if only view models rendered in additive properly). + // Draw view model normally unless fully cloaked. + //bool theIsCloakedViaUpgrade = GetHasUpgrade(theLocalPlayer->curstate.iuser4, MASK_ALIEN_CLOAKED); + int old=theRenderAmount; + if((theRenderMode == kRenderTransTexture) /*|| theIsCloakedViaUpgrade*/) + { + theRenderFx = kRenderFxNone; + theRenderColor.r = theRenderColor.g = theRenderColor.b = 0; + + if ( theRenderAmount == kAlienSelfCloakingBaseOpacity ) + { + theRenderMode = kAlienCloakViewModelRenderMode; + theRenderAmount = 10; + } + else if( theRenderAmount > kAlienSelfCloakingBaseOpacity && theRenderAmount < kAlienSelfCloakingMinOpacity) + { + theRenderMode = kAlienCloakViewModelRenderMode; + theRenderAmount = 40; + } + else if ( theRenderAmount == kAlienSelfCloakingMinOpacity ) { + theRenderMode = kAlienCloakViewModelRenderMode; + theRenderAmount = 50; + } + else + { + theRenderMode = kRenderNormal; + theRenderAmount = 255; + } + } + + + //char theMessage[128]; + //sprintf(theMessage, "Setting view model mode: %d amount: %d\n", theRenderMode, theRenderAmount); + //CenterPrint(theMessage); + } + +// if(GetHasUpgrade(this->GetLocalUpgrades(), MASK_ALIEN_CLOAKED)) +// { +// theRenderMode = kAlienCloakViewModelRenderMode; +// int theCloakingLevel = AvHGetAlienUpgradeLevel(this->GetLocalUpgrades(), MASK_UPGRADE_7); +// theRenderAmount = kAlienCloakViewModelAmount - theCloakingLevel*kAlienCloakViewModelLevelAmount; +// +// // Put in color, because texture rendering needs it +// theRenderFx = kRenderFxNone; +// theRenderColor.r = theRenderColor.g = theRenderColor.b = 0; +// } + + if(this->mInTopDownMode) + { + theRenderMode = kRenderTransAdd; + theRenderAmount = 1; + } + + theViewModel->curstate.skin = theSkin; + theViewModel->curstate.rendermode = theRenderMode; + theViewModel->curstate.renderamt = theRenderAmount; + theViewModel->curstate.renderfx = theRenderFx; + theViewModel->curstate.rendercolor = theRenderColor; + } +} + +void AvHHud::UpdateResources(float inTimePassed) +{ + const float kResourceRate = this->GetMaxAlienResources()/50.0f; + int thePointsToMove = max(inTimePassed*kResourceRate, 1); + + // Update visual resources if different this resources + if(this->mVisualResources != this->mResources) + { + if(abs(this->mVisualResources - this->mResources) <= thePointsToMove) + { + this->mVisualResources = this->mResources; + } + else + { + if(this->mVisualResources < this->mResources) + { + this->mVisualResources += thePointsToMove; + } + else + { + this->mVisualResources -= thePointsToMove; + } + } + } + + // Smoothly adjust energy level + float theCurrentEnergyLevel = pmove->fuser3/kNormalizationNetworkFactor; + + if((g_iUser1 == OBS_IN_EYE) && (g_iUser2 != this->mUser2OfLastEnergyLevel)) + { + // This isn't working yet + this->mVisualEnergyLevel = theCurrentEnergyLevel; + this->mUser2OfLastEnergyLevel = g_iUser2; + } + else + { + float kEnergyRate = 1.0f; + float theEnergyToMove = inTimePassed*kEnergyRate; + if(this->mVisualEnergyLevel != theCurrentEnergyLevel) + { + float theDiff = fabs(this->mVisualEnergyLevel - theCurrentEnergyLevel); + if(theDiff <= theEnergyToMove) + { + this->mVisualEnergyLevel = theCurrentEnergyLevel; + } + else + { + if(this->mVisualEnergyLevel < theCurrentEnergyLevel) + { + this->mVisualEnergyLevel += theEnergyToMove; + } + else + { + this->mVisualEnergyLevel -= theEnergyToMove; + } + } + } + } + + string theResourceText; + char theResourceBuffer[64]; + + if(this->GetInTopDownMode() && this->mCommanderResourceLabel) + { + LocalizeString(kMarineResourcePrefix, theResourceText); + sprintf(theResourceBuffer, "%s %d", theResourceText.c_str(), this->mVisualResources); + this->mCommanderResourceLabel->setText(64, theResourceBuffer); + } + + // Update visual resource indicators, expiring old ones + const float kNumericalInfoEffectLifetime = 1.1f; + const float kNumericalInfoScrollSpeed = 24; + for(NumericalInfoEffectListType::iterator theIter = this->mNumericalInfoEffects.begin(); theIter != this->mNumericalInfoEffects.end(); /* no inc */) + { + if((theIter->GetTimeCreated() + kNumericalInfoEffectLifetime) < this->mTimeOfLastUpdate) + { + theIter = this->mNumericalInfoEffects.erase(theIter); + } + else + { + // Update position + float thePosition[3]; + theIter->GetPosition(thePosition); + + thePosition[2] += inTimePassed*kNumericalInfoScrollSpeed; + + theIter->SetPosition(thePosition); + + // Next + theIter++; + } + } +} + + + + +//void AvHHud::ChangeUpgradeCosts(int inOldMessageID, int inNewMessageID, const char* inText) +//{ +// this->ChangeUpgradeCostsForMenu(this->mSoldierMenu, inOldMessageID, inNewMessageID, inText); +// this->ChangeUpgradeCostsForMenu(this->mLeaderMenu, inOldMessageID, inNewMessageID, inText); +// this->ChangeUpgradeCostsForMenu(this->mCommanderMenu, inOldMessageID, inNewMessageID, inText); +// +// this->UpdateUpgradeCosts(); +//} +// +//void AvHHud::ChangeUpgradeCostsForMenu(PieMenu* inMenu, int inOldMessageID, int inNewMessageID, const char* inText) +//{ +// if(inMenu) +// { +// inMenu->ChangeNode(inOldMessageID, inNewMessageID, string(inText)); +// } +//} +// +//void AvHHud::ResetUpgradeCosts() +//{ +// this->ResetUpgradeCostsForMenu(this->mSoldierMenu); +// this->ResetUpgradeCostsForMenu(this->mLeaderMenu); +// this->ResetUpgradeCostsForMenu(this->mCommanderMenu); +// +// this->UpdateUpgradeCosts(); +//} + +//void AvHHud::ResetUpgradeCostsForMenu(PieMenu* inMenu) +//{ +// if(inMenu) +// { +// inMenu->ResetToDefaults(); +// } +//} + +bool AvHHud::GetParticlesVisible() const +{ + + if (g_iUser1 == OBS_NONE) + { + return true; + } + + if (m_Spectator.IsInOverviewMode()) + { + return m_Spectator.m_iDrawCycle == 1; + } + else + { + return true; + } + +} + +// Sprite drawing on a level change is problematic and can cause crashing or disconcerting "no such sprite" error messages +bool AvHHud::GetSafeForSpriteDrawing() const +{ + bool theSafeForDrawing = false; + + const char* theLevelName = gEngfuncs.pfnGetLevelName(); + string theCurrentMapName = this->GetMapName(true); + if(theLevelName && (theCurrentMapName != "")) + { + string theLevelNameString(theLevelName); + int thePos = (int)theLevelNameString.find(theCurrentMapName); + if(thePos != string::npos) + { + theSafeForDrawing = true; + } + } + + return theSafeForDrawing; +} + +bool AvHHud::GetShouldDisplayUser3(AvHUser3 inUser3) const +{ + bool theShouldDisplay = false; + + if((inUser3 > AVH_USER3_NONE) && (inUser3 < AVH_USER3_MAX)) + { + theShouldDisplay = true; + + switch(inUser3) + { + case AVH_USER3_BREAKABLE: + case AVH_USER3_USEABLE: + case AVH_USER3_PARTICLE_ON: + case AVH_USER3_PARTICLE_OFF: + case AVH_USER3_ALPHA: + case AVH_USER3_WAYPOINT: + case AVH_USER3_NOBUILD: + case AVH_USER3_SPAWN_TEAMA: + case AVH_USER3_SPAWN_TEAMB: + theShouldDisplay = false; + break; + } + } + + return theShouldDisplay; +} + +bool AvHHud::GetTranslatedUser3Name(AvHUser3 inUser3, string& outString) const +{ + bool theSuccess = false; + + if(this->GetShouldDisplayUser3(inUser3)) + { + char theUser3String[512]; + sprintf(theUser3String, "#%s%d", kUser3Name, inUser3); + theSuccess = LocalizeString(theUser3String, outString); + } + + return theSuccess; +} + +bool AvHHud::GetTranslatedUser3Description(AvHUser3 inUser3, bool inFriendly, string& outString) const +{ + bool theSuccess = false; + + if(this->GetShouldDisplayUser3(inUser3)) + { + char theUser3String[512]; + sprintf(theUser3String, "#%s%d", kUser3Description, inUser3); + theSuccess = LocalizeString(theUser3String, outString); + + // If we're commanding, look for that description if it exists + if(this->GetInTopDownMode()) + { + string theCommanderDescription; + sprintf(theUser3String, "#%s%d", kUser3CommanderDescription, inUser3); + if(LocalizeString(theUser3String, theCommanderDescription)) + { + outString = theCommanderDescription; + theSuccess = true; + } + } + // Else look for a message that tell us what to do with this thing (assumes we're not commanding though) + else if(inFriendly) + { + string theFriendlyDescription; + sprintf(theUser3String, "#%s%d", kUser3FriendlyDescription, inUser3); + if(LocalizeString(theUser3String, theFriendlyDescription)) + { + outString = theFriendlyDescription; + theSuccess = true; + } + } + } + + return theSuccess; +} + + +void AvHHud::UpdateUpgradeCosts() +{ + PieMenu* theCurrentMenu = NULL; + if(this->GetManager().GetVGUIComponentNamed(this->mPieMenuControl, theCurrentMenu)) + { + this->UpdateEnableState(theCurrentMenu); + } +} + +void AvHHud::AddMiniMapAlert(float x, float y) +{ + mOverviewMap.AddAlert(x, y); +} + +void AvHHud::UpdateEnableState(PieMenu* inMenu) +{ + if(inMenu) + { + int thePurchaseLevel = this->GetIsCombatMode() ? max(0, this->mExperienceLevel - this->mExperienceLevelSpent - 1) : this->mResources; + + inMenu->UpdateMenuFromTech(this->mTechNodes, thePurchaseLevel); + +// if(this->GetIsNSMode()) + //{ + // Now disable any nodes whose children are all disabled (in NS, only end nodes can be chosen) + //inMenu->DisableNodesWhoseChildrenAreDisabled(); + //} + + inMenu->RecomputeVisibleSize(); + } +} + +void AvHHud::ShowMap() +{ + bool isNsMode=false; + if ( strnicmp(gHUD.GetMapName().c_str(), "ns_", 3) == 0 ) + isNsMode=true; + + if ( (!sShowMap || gEngfuncs.IsSpectateOnly()) && isNsMode ) + { + sShowMap = true; + gHUD.HideCrosshair(); + gHUD.GetManager().UnhideComponent(kShowMapHierarchy); + } +} + +void AvHHud::HideMap() +{ + if (sShowMap) + { + sShowMap = false; + gHUD.GetManager().HideComponent(kShowMapHierarchy); + gHUD.ShowCrosshair(); + } +} + +void AvHHud::ShowCommandMenu() +{ + gViewPort->ShowCommandMenu(gViewPort->m_StandardMenu); +} + +void AvHHud::HideCommandMenu() +{ + gViewPort->HideCommandMenu(); +} + +void AvHHud::GetSpriteForUser3(AvHUser3 inUser3, int& outSprite, int& outFrame, int& outRenderMode) +{ + + switch (inUser3) + { + + // Marines + case AVH_USER3_WAYPOINT: + outSprite = Safe_SPR_Load(kSmallOrderSprite); + outFrame = 2; + outRenderMode = kRenderTransAdd; + break; + case AVH_USER3_MARINE_PLAYER: + outSprite = Safe_SPR_Load(kMarinePlayersSprite); + outFrame = 0; + break; + case AVH_USER3_HEAVY: // This really means a marine with heavy armor, not a heavy armor object. + outSprite = Safe_SPR_Load(kMarinePlayersSprite); + outFrame = 1; + break; + case AVH_USER3_COMMANDER_STATION: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 5; + break; + case AVH_USER3_TURRET_FACTORY: + case AVH_USER3_ADVANCED_TURRET_FACTORY: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 6; + break; + case AVH_USER3_ARMORY: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 7; + break; + case AVH_USER3_ADVANCED_ARMORY: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 8; + break; + case AVH_USER3_ARMSLAB: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 9; + break; + case AVH_USER3_PROTOTYPE_LAB: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 10; + break; + case AVH_USER3_OBSERVATORY: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 11; + break; + case AVH_USER3_TURRET: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 12; + break; + case AVH_USER3_SIEGETURRET: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 13; + break; + case AVH_USER3_RESTOWER: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 14; + break; + case AVH_USER3_INFANTRYPORTAL: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 15; + break; + case AVH_USER3_PHASEGATE: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 16; + break; + + // Aliens + case AVH_USER3_DEFENSE_CHAMBER: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 17; + break; + case AVH_USER3_MOVEMENT_CHAMBER: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 18; + break; + case AVH_USER3_OFFENSE_CHAMBER: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 19; + break; + case AVH_USER3_SENSORY_CHAMBER: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 20; + break; + case AVH_USER3_ALIENRESTOWER: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 21; + break; + case AVH_USER3_HIVE: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 3; + break; + case AVH_USER3_ALIEN_PLAYER1: + outSprite = Safe_SPR_Load(kAlienPlayersSprite); + outFrame = 0; + break; + case AVH_USER3_ALIEN_PLAYER2: + outSprite = Safe_SPR_Load(kAlienPlayersSprite); + outFrame = 1; + break; + case AVH_USER3_ALIEN_PLAYER3: + outSprite = Safe_SPR_Load(kAlienPlayersSprite); + outFrame = 2; + break; + case AVH_USER3_ALIEN_PLAYER4: + outSprite = Safe_SPR_Load(kAlienPlayersSprite); + outFrame = 3; + break; + case AVH_USER3_ALIEN_PLAYER5: + outSprite = Safe_SPR_Load(kAlienPlayersSprite); + outFrame = 4; + break; + case AVH_USER3_ALIEN_EMBRYO : + outSprite = Safe_SPR_Load(kAlienPlayersSprite); + outFrame = 5; + break; + + case AVH_USER3_FUNC_RESOURCE: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 4; + break; + case AVH_USER3_WELD: + outSprite = Safe_SPR_Load(kStructuresSprite); + outFrame = 0; + break; + + default: + outSprite = 0; + outFrame = 0; + + } + +} + +int AvHHud::GetCurrentSquad() const +{ + return mCurrentSquad; +} + +AvHOverviewMap& AvHHud::GetOverviewMap() +{ + return mOverviewMap; +} + +void AvHHud::ShowCrosshair() +{ + + ++mCrosshairShowCount; + + if (mCrosshairShowCount > 0) + { + SetCrosshair(mCrosshairSprite, mCrosshairRect, mCrosshairR, mCrosshairG, mCrosshairB); + } + +} + +void AvHHud::HideCrosshair() +{ + + --mCrosshairShowCount; + + if (mCrosshairShowCount <= 0) + { + wrect_t nullrect = { 0, 0, 0, 0 }; + SetCrosshair(0, nullrect, 0, 0, 0); + } + +} + +void AvHHud::SetCurrentCrosshair(HSPRITE hspr, wrect_t rc, int r, int g, int b) +{ + mCrosshairSprite = hspr; + mCrosshairRect = rc; + mCrosshairR = r; + mCrosshairG = g; + mCrosshairB = b; + + if (mCrosshairShowCount > 0) + { + SetCrosshair(mCrosshairSprite, mCrosshairRect, mCrosshairR, mCrosshairG, mCrosshairB); + } + +} + +void AvHHud::SetViewport(const int inViewport[4]) +{ + if (!m_Spectator.IsInOverviewMode()) + { + mViewport[0] = inViewport[0]; + mViewport[1] = inViewport[1]; + mViewport[2] = inViewport[2]; + mViewport[3] = inViewport[3]; + } + else + { + mSpecialViewport[0] = inViewport[0]; + mSpecialViewport[1] = inViewport[1]; + mSpecialViewport[2] = inViewport[2]; + mSpecialViewport[3] = inViewport[3]; + + mViewport[0] = 0; + mViewport[1] = 0; + mViewport[2] = ScreenWidth(); + mViewport[3] = ScreenHeight(); + + } +} + +void AvHHud::GetViewport(int outViewport[4]) const +{ + outViewport[0] = mViewport[0]; + outViewport[1] = mViewport[1]; + outViewport[2] = mViewport[2]; + outViewport[3] = mViewport[3]; +} + +const AvHFont& AvHHud::GetSmallFont() const +{ + return mSmallFont; +} + +void AvHHud::PlayStream() +{ + if(gEngfuncs.Cmd_Argc() <= 1) + { + gEngfuncs.Con_Printf( "usage: playstream \n" ); + } + else + { + if ( gEngfuncs.Cmd_Argc() >= 2 ) + { + // Read URL + string theURL; + theURL = string("http://") + string(gEngfuncs.Cmd_Argv(1)); + + string theError; + if(!gHUD.PlayInternetStream(theURL, theError)) + { + gHUD.AddTooltip(theError.c_str()); + } + } + } +} + +void AvHHud::StopStream() +{ + gHUD.StopInternetStream(); +} + +float AvHHud::GetServerVariableFloat(const char* inName) const +{ + ServerVariableMapType::const_iterator iterator; + iterator = mServerVariableMap.find(inName); + + if ( iterator == mServerVariableMap.end() ) + { + return 0; + } + else + { + return iterator->second; + } + +} + + +/** + * Prints the call stack when an unhandled exception occurs. + */ +LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* pExp) +{ + + /* + // E-mail the exception log to the programmers. + + std::stringstream buffer; + LogException(buffer, pExp); + + const char* serverName = "66.111.4.62"; + const char* fromAddress = "noreply@overmind.com"; + + const char* programmerAddress[] = + { + "max_mcguire@yahoo.com", + }; + + for (int i = 0; i < sizeof(programmerAddress) / sizeof(char*); ++i) + { + SendMail(serverName, fromAddress, fromAddress, programmerAddress[i], + "Exception Log", buffer.str().c_str()); + } + */ + + AvHHud::ResetGammaAtExit(); + + return EXCEPTION_EXECUTE_HANDLER; + +} + +// Added DLL entry point to try to reset gamma properly under VAC +BOOL WINAPI DllMain(HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + + // Install a crash handler. + //SetUnhandledExceptionFilter(ExceptionFilter); + + } + else if (fdwReason == DLL_PROCESS_DETACH) + { + AvHHud::ResetGammaAtExit(); + } + return TRUE; +} diff --git a/main/source/mod/AvHHud.h b/main/source/mod/AvHHud.h index b0f5975..2d7923c 100644 --- a/main/source/mod/AvHHud.h +++ b/main/source/mod/AvHHud.h @@ -1,844 +1,869 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: Main NS HUD, also interface to client network messages -// -// $Workfile: AvHHud.h $ -// $Date: 2002/10/24 21:29:49 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHHud.h,v $ -// Revision 1.60 2002/10/24 21:29:49 Flayra -// - Moved help client-side -// - Fixed particle/changelevel crash -// - Reworked marine upgrade drawing -// - Added lots of utility functions for help system (mirrors functions on server) -// - Removed gamma message unless it failed or if maxplayers is 1 -// - Fixed alien hive sight crash -// - Show players under reticle while in ready room and spectating -// - Removed ugly/too-prevalent user3 icons -// -// Revision 1.59 2002/10/03 20:24:39 Flayra -// - Changes for "more resources required" -// -// Revision 1.58 2002/10/03 18:54:31 Flayra -// - Allow right-click to cancel building placement -// - Fixed help icons -// - Added a couple utility functions -// - Reworked order notification -// - Reworked blip network messages to avoid hard-coded limit -// - Sent max resources down with current resources -// - Countdown sound no longer prevents other hud sounds -// - Alien trigger sounds -// - New order sounds -// - No longer disable nodes out of our cost range -// -// Revision 1.57 2002/09/25 20:47:22 Flayra -// - Don't draw elements on HUD when dead -// - UI refactoring -// - Split reticle help into help text and reticle text -// - Removed use order -// - Added separate select sound for alien -// - Multiple move sounds -// - Only draw entity build/health status when under reticle (no more scanning around you) -// - Added 3 new sayings -// -// Revision 1.56 2002/09/23 22:18:25 Flayra -// - Added alien build circles -// - Game status changes so particles aren't sent every time -// - Demo playback changes (save restore basic data that HUD already has) -// - New alert sounds -// - Skin support -// -// Revision 1.55 2002/09/09 19:55:24 Flayra -// - Added hive info indicator -// - Fixed bug where reticle tooltip help text wasn't being set until a weapon was selected -// - Fixed release mode bug where tooltips weren't expiring -// - Fixed bug where marine upgrades blinked -// - "No commander" indicator now blinks -// -// Revision 1.54 2002/08/31 18:01:01 Flayra -// - Work at VALVe -// -// Revision 1.53 2002/08/16 02:37:49 Flayra -// - HUD sounds no longer cut each other off (they won't play instead of cutting off another sound) -// - Tooltip sounds -// - Selection issues -// - Draw rings around buildings that need to be built -// - Removed old overwatch code -// -// Revision 1.52 2002/08/09 01:02:51 Flayra -// - Added hooks for demo playback, removed prediction selection -// -// Revision 1.51 2002/08/02 22:51:28 Flayra -// - Fixed memory overwrite...eek! -// -// Revision 1.50 2002/08/02 21:59:12 Flayra -// - Added reticle help, new tooltip system and much nicer order drawing! Refactored view model drawing a bit, hoping to make texture blending work for it. -// -// Revision 1.49 2002/07/26 23:05:01 Flayra -// - Generate numerical feedback for damage events -// - Refactoring for more info when looking at something (instead of bad-looking player names only) -// -// Revision 1.48 2002/07/23 17:07:37 Flayra -// - Added visually-smooth energy level, added versatile location code, new hive sight info, refactored to remove extra sprites (128 HUD sprites bug), commander tech help fixes -// -// Revision 1.47 2002/07/08 17:07:56 Flayra -// - Started to add display of marine upgrade sprite, fixed bug where building indicators aren't displayed after a map change, info_location drawing changes, primal scream color tweak, removed old hive drawing code -// -// Revision 1.46 2002/07/01 21:35:05 Flayra -// - Removed lots of outdated sprites and sprite code, added building ranges, fixed ghost building problem (bug #82) -// -// Revision 1.45 2002/06/25 18:03:09 Flayra -// - Added info_locations, removed old weapon help system, added smooth resource swelling, lots of alien UI usability changes, fixed problem with ghost building -// -// Revision 1.44 2002/06/10 19:55:36 Flayra -// - New commander UI (bindable via hotkeys, added REMOVE_SELECTION for when clicking menu options when no players selected) -// -// Revision 1.43 2002/06/03 16:48:45 Flayra -// - Help sprites moved into one animated sprite, select sound volume reduced (now that sound is normalized) -// -// Revision 1.42 2002/05/28 17:48:14 Flayra -// - Minimap refactoring, reinforcement refactoring, new hive sight fixes, recycling support -// -// Revision 1.41 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifndef AVHHUD_H -#define AVHHUD_H - -#include "ui/UIHud.h" -#include "mod/AvHConstants.h" -#include "mod/AvHSpecials.h" -#include "mod/AvHSharedTypes.h" -#include "mod/AvHParticleSystem.h" -#include "common/entity_state.h" -#include "VGUI_ProgressBar.h" -#include "mod/AvHEntityHierarchy.h" -#include "ui/MarqueeComponent.h" -#include "mod/AvHOrder.h" -#include "mod/AvHMessage.h" -#include "mod/AvHAmbientSound.h" -#include "mod/AvHTechTree.h" -#include "mod/AvHVisibleBlipList.h" -#include "mod/AvHMapExtents.h" -#include "mod/AvHSpecials.h" -#include "util/GammaTable.h" -#include "mod/AvHBaseInfoLocation.h" -#include "mod/AvHTooltip.h" -#include "mod/AvHTechSlotManager.h" -#include "mod/AvHHudConstants.h" -#include "mod/AvHOverviewMap.h" - -class AvHTeamHierarchy; -class PieMenu; -class FadingImageLabel; -using vgui::Cursor; - -struct AnimatedSprite -{ - float mCurrentFrame; - int mData; -}; - -class SelectionEffect -{ -public: - SelectionEffect() : mEntIndex(-1), mAngleOffset(0) - {} - - int mEntIndex; - int mAngleOffset; -}; - -typedef enum -{ - MAIN_MODE, - EDITPS_MODE, - SCOREBOARD_MODE -} UIMode; - -typedef vector SelectionListType; - -class NumericalInfoEffect -{ -public: - NumericalInfoEffect(float inPosition[3], float inNumber, int inEventType, float inTimeCreated); - void GetPosition(float* outPosition) const; - float GetNumber() const; - int GetEventType() const; - float GetTimeCreated() const; - void SetPosition(float inPosition[3]); - -private: - float mPosition[3]; - float mNumber; - int mEventType; - float mTimeCreated; -}; - -const int kNumUpgradeLines = 5; - -class AvHHud : public UIHud -{ -public: - AvHHud(const string& inFilename, UIFactory* inFactory); - virtual ~AvHHud(void); - - void OnActivateSteamUI(); - void OnDeactivateSteamUI(); - void OnLostFocus(); - bool OnKeyEvent(int virtualKey, int scanCode, bool pressed); - - void AddMiniMapAlert(float x, float y); - void AddNumericalInfoMessage(float inOrigin[3], float inNumber, int inEventType); - void AddTooltip(const char* inMessageText, bool inIsToolTip = true, float inTooltipWidth = kToolTipMaxWidth); - bool AddTooltipOnce(const char* inMessageText, bool inIsToolTip = true); - void Cancel(); - - void ClearSelection(); - - void ClientProcessEntity(struct entity_state_s* inEntity); - - void ComponentJustPainted(Panel* inPanel); - - bool GetAndClearAlienAbility(AvHMessageID& outMessageID); - bool GetAndClearGroupEvent(AvHMessageID& outMessageID); - bool GetAndClearTechEvent(AvHMessageID& outMessageID); - bool GetLastHotkeySelectionEvent(AvHMessageID& outMessageID); - void SetLastHotkeySelectionEvent(AvHMessageID inMessageID); - - // Returns true if particles should be rendered in the HUD. - bool GetParticlesVisible() const; - bool GetSafeForSpriteDrawing() const; - - void ClearTrackingEntity(); - int GetTrackingEntity() const; - - bool GetIsRegionBlockedByUI(float inNormX, float inNormY); - - //int GetArmorLevel(void) const; - int GetCommanderIndex() const; - bool GetHasJetpack() const; - int GetHelpIconFrameFromUser3(AvHUser3 inUser3); - HSPRITE GetHelpSprite() const; - bool GetHasAlienUpgradesAvailable() const; - bool GetIsAlien() const; - bool GetIsBeingDigested() const; - bool GetIsEnsnared() const; - bool GetIsDigesting() const; - bool GetIsStunned() const; - bool GetIsNotInControl() const; - bool GetIsInTopDownMode() const; - bool GetIsMarine() const; - bool GetIsRelevant() const; - bool GetIsShowingMap() const; - AvHPlayMode GetPlayMode(void) const; - bool GetAlienHelpForMessage(int inMessageID, string& outHelpText, int& outPointCost) const; - bool GetDoesPlayerHaveOrder() const; - bool GetHelpForMessage(int inMessageID, string& outHelpText) const; - bool GetInTopDownMode() const; - bool GetIsSelecting() const; - OrderListType GetOrderList() const; - AvHOrderType GetOrderMode() const; - bool GetCenterPositionForGroup(int inGroupNumber, vec3_t& outCenterPosition) const; - bool GetMouseOneDown() const; - bool GetMouseTwoDown() const; - bool GetAndClearTopDownScrollAmount(int& outX, int& outY, int& outZ); - bool GetAndClearSelectionEvent(vec3_t& outSelectionXY, AvHMessageID& outMessageID); - EntityListType GetSelected() const; - const AvHTechSlotManager& GetTechSlotManager() const; - bool GetTranslatedUser3Name(AvHUser3 inUser3, string& outString) const; - bool GetTranslatedUser3Description(AvHUser3 inUser3, bool inFriendly, string& outString) const; - vec3_t GetVisualOrigin() const; - - AvHMessageID HotKeyHit(char inChar); - - virtual void Init(void); - virtual void PostUIInit(void); - virtual void VidInit(void); - - bool GetGameStarted() const; - int GetGameTime() const; - int GetGameTimeLimit() const; - int GetCombatAttackingTeamNumber() const; - static bool GetShowingMap(); - - static void PlayStream(); - static void StopStream(); - - bool GetIsAlive(bool inIncludeSpectating = true) const; - void GhostBuildingCallback( struct tempent_s *ent, float frametime, float currenttime); - void CancelBuilding(); - - void PlayHUDSound(const char *szSound, float vol, float inSoundLength = 0.0f); - void PlayHUDSound(int iSound, float vol, float inSoundLength = 0.0f); - void PlayHUDSound(AvHHUDSound inSound); - - float GetHUDExperience() const; - int GetHUDExperienceLevel() const; - float GetHUDHandicap() const; - AvHUser3 GetHUDUser3() const; - AvHPlayMode GetHUDPlayMode() const; - AvHTeamNumber GetHUDTeam() const; - int GetHUDUpgrades() const; - int GetHUDMaxArmor() const; - int GetHUDMaxHealth() const; - void GetPrimaryHudColor(int& outR, int& outG, int& outB, bool inIgnoreUpgrades = false, bool gammaCorrect = true) const; - float GetTimeOfLastUpdate() const; - int GetMenuTechSlots() const; - - //void GetVisibleBlips(VisibleBlipListType& outBlipList); - - virtual int Redraw( float flTime, int intermission ); - virtual void ResetComponentsForUser3(); - void SetSelectingWeaponID(int inWeaponID, int inR = -1, int inG = -1, int inB = -1); - void SetTechHelpText(const string& inTechHelpText); - void DrawSelectionCircleOnGroundAtPoint(vec3_t inOrigin, int inRadius); - // tankefugl: 0000988 - void DrawBuildHealthEffectsForEntity(int inEntityIndex, float inAlpha = 1.0f); - // :tankefugl - void DrawSelectionAndBuildEffects(); - void DrawHUDNumber(int inX, int inY, int inFlags, int inNumber); - - int InitializeDemoPlayback(int inSize, unsigned char* inBuffer); - int InitializeDemoPlayback2(int inSize, unsigned char* inBuffer); - int InitializeWeaponInfoPlayback(int inSize, unsigned char* inBuffer); - - virtual bool Update(float inCurrentTime, string& outErrorString); - virtual void UpdateMusic(float inCurrentTime); - - bool SlotInput(int inSlot); - - const AvHMapExtents& GetMapExtents(); - float GetGammaSlope() const; - string GetMapName(bool inLocalOnly = false) const; - int GetMaxAlienResources() const; - int GetNumActiveHives() const; - void HideProgressStatus(); - void SetProgressStatus(float inPercentage); - - AvHVisibleBlipList& GetEnemyBlipList(); - AvHVisibleBlipList& GetFriendlyBlipList(); - - AvHEntityHierarchy& GetEntityHierarchy(); - - int GetLocalUpgrades() const; - string GetNameOfLocation(vec3_t inLocation) const; - const AvHTechTree& GetTechNodes() const; - - UIMode GetUIMode() const; - bool SwitchUIMode(UIMode inNewMode); - bool GetIsCombatMode() const; - bool GetIsNSMode() const; - bool GetIsScriptedMode() const; - - void HideResearchProgressStatus(); - void SetResearchProgressStatus(float inPercentage); - - AvHMessageID GetGhostBuilding() const; - void SetGhostBuildingMode(AvHMessageID inGhostBuilding); - - void SetAlienAbility(AvHMessageID inAlienAbility); - void SetRenderingSelectionView(bool inState); - - void SetClientDebugCSP(weapon_data_t* inWeaponData, float inNextPlayerAttack); - void SetCurrentWeaponData(int inCurrentWeaponID, bool inEnabled); - int GetCurrentWeaponID(void); - - void DrawTopDownBG(); - void DrawTranslatedString(int inX, int inY, const char* inStringToTranslate, bool inCentered = false, bool inIgnoreUpgrades = false, bool inTrimExtraInfo = false); - void HandleFog(); - void PostModelRender(char* inModelName); - void PreRenderFrame(); - void PostRenderFrame(); - void RenderNoZBuffering(); - - void Render(); - void RenderCommonUI(); - void RenderMarineUI(); - void RenderCommanderUI(); - void RenderAlienUI(); - void RenderMiniMap(int inX, int inY, int inWidth, int inHeight); - - void RenderStructureRanges(); - void RenderStructureRange(vec3_t inOrigin, int inRadius, HSPRITE inSprite, int inRenderMode = kRenderNormal, int inFrame = 0, float inR = 0, float inG = 0.5, float inB = 0, float inAlpha = 1.0f); - - void DrawWarpedOverlaySprite(int spriteHandle, int numXFrames, int numYFrames, - float inWarpXAmount = 0.0f, float inWarpYAmount = 0.0f, - float inWarpXSpeed = 0.0f, float inWarpYSpeed = 0.0f); - - void DrawActionButtons(); - void DrawHotgroups(); - void DrawPendingRequests(); - - void SetCurrentUseableEnergyLevel(float inEnergyLevel); - - // Network messages - int AlienInfo(const char* pszName, int iSize, void* pbuf); - int BlipList(const char* pszName, int iSize, void* pbuf); - int ClScript(const char *pszName, int iSize, void *pbuf); - int Countdown(const char* pszName, int iSize, void* pbuf); - int DebugCSP(const char* pszName, int iSize, void* pbuf); - int EditPS(const char* pszName, int iSize, void* pbuf); - int EntHier(const char *pszName, int iSize, void *pbuf); - int Fog(const char* pszName, int iSize, void* pbuf); - int ListPS(const char* pszName, int iSize, void* pbuf); - int Particles(const char *pszName, int iSize, void *pbuf); - int SoundNames(const char *pszName, int iSize, void *pbuf); - int PlayHUDNot(const char* pszName, int iSize, void* pbuf); - - int BalanceVar(const char* pszName, int iSize, void* pbuf); - int ServerVar(const char* pszName, int iSize, void* pbuf); - - int GameStatus(const char* pszName, int iSize, void* pbuf); - int MiniMap(const char* pszName, int iSize, void* pbuf); - int IssueOrder(const char* pszName, int iSize, void* pbuf); - int LUAmsg(const char* pszName, int iSize, void* pbuf); - int Progress(const char* pszName, int iSize, void* pbuf); - int SetGmma(const char* pszName, int iSize, void* pbuf); - int SetSelect(const char* pszName, int iSize, void* pbuf); - int SetRequest(const char* pszName, int iSize, void* pbuf); - int SetOrder(const char* pszName, int iSize, void* pbuf); - int SetupMap(const char* pszName, int iSize, void* pbuf); - int SetTopDown(const char* pszName, int iSize, void* pbuf); - int SetTech(const char* pszName, int iSize, void* pbuf); - int TechSlots(const char* pszName, int iSize, void* pbuf); - - void GetSpriteForUser3(AvHUser3 inUser3, int& outSprite, int& outFrame, int& outRenderMode); - - int GetCurrentSquad() const; - AvHOverviewMap& GetOverviewMap(); - - void ShowCrosshair(); - void HideCrosshair(); - - // This function should be used instead of the global SetCrosshair. - void SetCurrentCrosshair(HSPRITE hspr, wrect_t rc, int r, int g, int b); - - static void ResetGammaAtExit(); - static int ResetGammaAtExitForOnExit(); - static void ResetGammaAtExit(int inSig); - - void SetViewport(const int inViewport[4]); - void GetViewport(int outViewport[4]) const; - - const AvHFont& GetSmallFont() const; - cl_entity_s* GetVisiblePlayer() const; - - float GetServerVariableFloat(const char* inName) const; - - // tankefugl: - void SetCenterText(const char* inText); - void DrawCenterText(); - void ClearCenterText(); - // :tankefugl - -private: - - // tankefugl: - std::string mCenterText; - float mCenterTextTime; - // :tankefugl - - bool GetCommanderLabelText(std::string& outCommanderName) const; - - void AddCommands(); - void ClearData(); - void DisplayCombatUpgradeMenu(bool inVisible); - void DrawMouseCursor(int inBaseX, int inBaseY); - void DrawOrders(); - void DrawHelpIcons(); - // tankefugl: 0000971 - void GetOrderDirection(vec3_t inTarget, int inOrderType); - void DrawTeammateOrders(); - // tankefugl: 0000992 - void DrawDisplayOrder(); - void SetDisplayOrder(int inOrderType, int inOrderIndex, string inText1, string inText2, string inText3); - // :tankefugl - void DrawHUDStructureNotification(); - void DrawInfoLocationText(); - void DrawPlayerNames(); - void DrawReticleInfo(); - void DrawToolTips(); - // tankefugl: 0000971 -- added inAlpha - void DrawWorldSprite(int inSpriteHandle, int inRenderMode, vec3_t inWorldPosition, int inFrame, float inWorldSize, float inAlpha = 1.0f); - // :tankefugl - void DrawOrderIcon(const AvHOrder& inOrder); - void DrawOrderText(const AvHOrder& inOrder); - int GetFrameForOrderType(AvHOrderType inOrderType) const; - void GetReticleTextDrawingInfo(float& outNormX, float& outNormY, bool& outCentered) const; - void DrawTechTreeSprite(AvHMessageID inMessageID, int inPosX, int inPosY, int inWidth, int inHeight, int inFrame); - int GetTechTreeSprite(AvHMessageID inMessageID); - void GetTooltipDrawingInfo(float& outNormX, float& outNormY) const; - string GetRankTitle(bool inShowUnspentLevels = false) const; - bool GetShouldDisplayUser3(AvHUser3 inUser3) const; - void InitCommanderMode(); - void InitializeDemoRecording(); - void InitMenu(const string& inMenuName); - void ChangeUpgradeCosts(int inOldMessageID, int inNewMessageID, const char* inText); - void ChangeUpgradeCostsForMenu(PieMenu* inMenu, int inOldMessageID, int inNewMessageID, const char* inText); - void DisplayMessage(const char* inMessage); - bool GetAmbientSoundNameFromIndex(string& outSoundName, int inSoundIndex) const; - EntityListType GetDrawPlayerOrders() const; - bool GetEntityInfoString(int inEntityID, string& outEntityInfoString, bool& outIsEnemy); - void ModifyAmbientSoundEntryIfChanged(bool inSoundOn, int inSoundIndex, int inEntIndex, float inTimeStarted, int inVolume, int inFadeDistance, int inFlags, Vector inOrigin); - void ResetTopDownUI(); - bool SetGamma(float inSlope); - void SetReinforcements(int inReinforcements); - void SetHelpMessage(const string& inHelpText, bool inForce = false, float inNormX = -1, float inNormY = -1); - void SetActionButtonHelpMessage(const string& inHelpText); - void SetReticleMessage(const string& inHelpText); - void OrderNotification(const AvHOrder& inOrder); - virtual void ResetGame(bool inMapChanged = false); - - bool SetCursor(AvHOrderType inOrderType); - void GetCursor(HSPRITE& outSprite, int& outFrame); - - void SetSelectionEffects(EntityListType& inUnitList); - //void UpdateSelectionEffects(float inTimePassed); - void TraceEntityID(int& outEntityID); - bool GetIsMouseInRegion(int inX, int inY, int inWidth, int inHeight); - void GetMousePos(int& outX, int& outY) const; - - // Help system - void InternalHelpTextThink(); - bool ProcessAlien(); - bool ProcessAlienHelp(); - bool ProcessEntityHelp(); - bool ProcessGeneralHelp(); - bool ProcessOrderHelp(); - bool ProcessWeaponsHelp(); - - //void ResetUpgradeCosts(); - //void ResetUpgradeCostsForMenu(PieMenu* inMenu); - void UpdateAlienUI(float inCurrentTime); - void UpdateCommonUI(); - void UpdateDataFromVuser4(float inCurrentTime); - void UpdateExploitPrevention(); - void UpdateMarineUI(float inCurrentTime); - void UpdateUpgradeCosts(); - void UpdateEnableState(PieMenu* inMenu); - void UpdateCountdown(float inCurrentTime); - void UpdateHierarchy(); - void UpdateInfoLocation(); - void UpdatePieMenuControl(); - void UpdateEntityID(float inCurrentTime); - void UpdateTooltips(float inCurrentTime); - void UpdateStructureNotification(float inCurrentTime); - void UpdateProgressBar(); - void UpdateDemoRecordPlayback(); - void UpdateBuildingPlacement(); - void UpdateResources(float inTimePassed); - void UpdateSelection(); - void UpdateSpectating(); - void UpdateBuildResearchText(); - void UpdateHelpText(); - void UpdateTechNodes(); - void UpdateAmbientSounds(); - void UpdateFromEntities(float inCurrentTime); - void UpdateViewModelEffects(); - - int mResources; - int mUser2OfLastResourceMessage; - int mMaxResources; - int mVisualResources; - float mExperience; - int mExperienceLevel; - int mExperienceLevelLastDrawn; - int mExperienceLevelSpent; - float mTimeOfLastLevelUp; - AvHMessageID mMenuImpulses[kNumUpgradeLines]; - - float mCountDownClock; - int mLastTickPlayed; - int mNumTicksToPlay; - float mTimeOfLastUpdate; - float mTimeOfNextHudSound; - AvHHUDSound mLastHUDSoundPlayed; - float mTimeOfCurrentUpdate; - - AvHOverviewMap mOverviewMap; - - AvHTeamHierarchy* mHierarchy; - AvHTeamHierarchy* mShowMapHierarchy; - AvHEntityHierarchy mEntityHierarchy; - - EntityListType mSelected; - EntityListType mGroups[kNumHotkeyGroups]; - EntityListType mSelectAllGroup; - AvHUser3 mGroupTypes[kNumHotkeyGroups]; - AvHAlertType mGroupAlerts[kNumHotkeyGroups]; - - typedef map PendingRequestListType; - PendingRequestListType mPendingRequests; - - AvHUser3 mLastUser3; - AvHTeamNumber mLastTeamNumber; - AvHPlayMode mLastPlayMode; - - bool mSelectionJustChanged; - bool mMouseOneDown; - bool mMouseTwoDown; - int mMouseOneStartX; - int mMouseOneStartY; - bool mLeftMouseStarted; - bool mLeftMouseEnded; - bool mPlacingBuilding; - int mMouseTwoStartX; - int mMouseTwoStartY; - bool mRightMouseStarted; - bool mRightMouseEnded; - vec3_t mMouseWorldPosition; - vec3_t mLeftMouseWorldStart; - vec3_t mLeftMouseWorldEnd; - vec3_t mRightMouseWorldStart; - vec3_t mRightMouseWorldEnd; - MarqueeComponent* mSelectionBox; - int mMouseCursorX; - int mMouseCursorY; - string mPieMenuControl; - - OrderListType mOrders; - //AvHOrderType mOrderMode; - - // tankefugl: 0000971 - map< int, TeammateOrderType > mTeammateOrder; - // tankefugl: 0000992 - float mDisplayOrderTime; - int mDisplayOrderType; - int mDisplayOrderDirection; - int mDisplayOrderIndex; - string mDisplayOrderText1; - string mDisplayOrderText2; - string mDisplayOrderText3; - int mCurrentOrderTarget; - int mCurrentOrderType; - float mCurrentOrderTime; - // :tankefugl - AvHMessageID mTechEvent; - AvHMessageID mAlienAbility; - AvHMessageID mGroupEvent; - AvHMessageID mLastHotkeySelectionEvent; - int mTrackingEntity; - - AvHAlienUpgradeListType mUpgrades; - AvHAlienUpgradeCategory mCurrentUpgradeCategory[ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE]; - int mNumUpgradesAvailable; - - Label* mCommanderResourceLabel; - Label* mOverwatchRangeLabel; - ProgressBar* mGenericProgressBar; - ProgressBar* mResearchProgressBar; - ProgressBar* mAlienProgressBar; - Label* mResearchLabel; - string mPreviousHelpText; - float mTimeLastHelpTextChanged; - - int mSelectedNodeResourceCost; - float mCurrentUseableEnergyLevel; - float mVisualEnergyLevel; - int mUser2OfLastEnergyLevel; - - static void PlayRandomSongHook(); - static void ShowMap(); - static void HideMap(); - - float mTimeLastOverwatchPulse; - bool mInTopDownMode; - int mNumLocalSelectEvents; - AvHMapMode mMapMode; - - static GammaTable sPregameGammaTable; - static GammaTable sGameGammaTable; - float mDesiredGammaSlope; - - typedef vector AmbientSoundListType; - AmbientSoundListType mAmbientSounds; - - AvHTechTree mTechNodes; - - string mMapName; - AvHMapExtents mMapExtents; - - // If this isn't MESSAGE_NULL, it means we are trying to build a building of this tech - AvHMessageID mGhostBuilding; - AvHMessageID mValidatedBuilding; - bool mCreatedGhost; - bool mCancelBuilding; - bool mCurrentGhostIsValid; - vec3_t mNormBuildLocation; - vec3_t mGhostWorldLocation; - - // Added by mmcguire. - int mSelectionBoxX1; - int mSelectionBoxY1; - int mSelectionBoxX2; - int mSelectionBoxY2; - bool mSelectionBoxVisible; - - Vector mCommanderPAS; - - StringList mSoundNameList; - - AvHVisibleBlipList mEnemyBlips; - AvHVisibleBlipList mFriendlyBlips; - - bool mMarineUIDrawUI; - HSPRITE mMarineUIJetpackSprite; - - HSPRITE mAlienUIEnergySprite; - - HSPRITE mMembraneSprite; - HSPRITE mDigestingSprite; - HSPRITE mBackgroundSprite; - HSPRITE mTopDownTopSprite; - HSPRITE mTopDownBottomSprite; - HSPRITE mMarineTopSprite; - HSPRITE mLogoutSprite; - HSPRITE mCommandButtonSprite; - HSPRITE mCommandStatusSprite; - HSPRITE mSelectAllSprite; - - HSPRITE mMarineOrderIndicator; - HSPRITE mMarineUpgradesSprite; - - // tankefugl: 0000971 - HSPRITE mTeammateOrderSprite; - // :tankefugl - typedef map SpriteListType; - SpriteListType mActionButtonSprites; - //SpriteListType mHelpSprites; - int mHelpSprite; - - typedef vector< pair > HelpIconListType; - HelpIconListType mHelpIcons; - EntityListType mHelpEnts; - - EntityListType mBuildingEffectsEntityList; - float mTimeOfLastEntityUpdate; - - HSPRITE mAlienUIUpgrades; - HSPRITE mAlienUIUpgradeCategories; - - HSPRITE mAlienBuildSprite; - HSPRITE mMarineBuildSprite; - - HSPRITE mAlienHealthSprite; - HSPRITE mMarineHealthSprite; - - HSPRITE mHealthEffectsSprite; - HSPRITE mBuildCircleSprite; - //HSPRITE mSiegeTurretSprite; - SelectionListType mSelectionEffects; - - - //HSPRITE mMappingTechSprite; - - HSPRITE mHiveInfoSprite; - HSPRITE mHiveHealthSprite; - HSPRITE mOrderSprite; - HSPRITE mCursorSprite; - HSPRITE mMarineCursor; - HSPRITE mAlienCursor; - HSPRITE mAlienLifeformsCursor; - int mCurrentCursorFrame; - - int mProgressBarEntityIndex; - int mProgressBarParam; - - bool mFogActive; - vec3_t mFogColor; - float mFogStart; - float mFogEnd; - - AvHBaseInfoLocationListType mInfoLocationList; - string mLocationText; - - string mReticleInfoText; - //int mReticleInfoColorR; - //int mReticleInfoColorG; - //int mReticleInfoColorB; - //float mReticleInfoColorA; - //int mReticleInfoScreenX; - //int mReticleInfoScreenY; - int mSelectingWeaponID; - string mTechHelpText; - AvHMessageID mSelectingNodeID; - - struct tempent_s* mLastGhostBuilding; - - typedef vector NumericalInfoEffectListType; - NumericalInfoEffectListType mNumericalInfoEffects; - - AvHTooltip mHelpMessage; - AvHTooltip mReticleMessage; - AvHTooltip mTopDownPlayerNameMessage; - AvHTooltip mTopDownActionButtonHelp; - AvHTooltip mCombatUpgradeMenu; - bool mDrawCombatUpgradeMenu; - - typedef vector AvHTooltipListType; - AvHTooltipListType mTooltips; - - HiveInfoListType mHiveInfoList; - - bool mRecordingLastFrame; - - float mTimeOfLastHelpText; - StringList mDisplayedToolTipList; - - int mCurrentWeaponID; - bool mCurrentWeaponEnabled; - - AvHTechSlotManager mTechSlotManager; - UIMode mCurrentUIMode; - - int mMenuTechSlots; - int mCurrentSquad; - int mBlinkingAlertType; - - float mGameTime; - int mTimeLimit; - int mCombatAttackingTeamNumber; - bool mGameStarted; - bool mGameEnded; - - AvHTeamNumber mLastTeamSpectated; - - typedef struct - { - AvHMessageID mStructureID; - float mTime; - int mPlayerIndex; - Vector mLocation; - } HUDNotificationType; - - typedef vector< HUDNotificationType > StructureHUDNotificationListType; - StructureHUDNotificationListType mStructureNotificationList; - - int mCrosshairShowCount; - HSPRITE mCrosshairSprite; - wrect_t mCrosshairRect; - int mCrosshairR; - int mCrosshairG; - int mCrosshairB; - - int mViewport[4]; // the viewport coordinates x ,y , width, height - int mSpecialViewport[4]; // the viewport coordinates x ,y , width, height - - bool mSteamUIActive; - - typedef std::map ServerVariableMapType; - ServerVariableMapType mServerVariableMap; - - static bool sShowMap; - -}; - -#endif +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: Main NS HUD, also interface to client network messages +// +// $Workfile: AvHHud.h $ +// $Date: 2002/10/24 21:29:49 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHHud.h,v $ +// Revision 1.60 2002/10/24 21:29:49 Flayra +// - Moved help client-side +// - Fixed particle/changelevel crash +// - Reworked marine upgrade drawing +// - Added lots of utility functions for help system (mirrors functions on server) +// - Removed gamma message unless it failed or if maxplayers is 1 +// - Fixed alien hive sight crash +// - Show players under reticle while in ready room and spectating +// - Removed ugly/too-prevalent user3 icons +// +// Revision 1.59 2002/10/03 20:24:39 Flayra +// - Changes for "more resources required" +// +// Revision 1.58 2002/10/03 18:54:31 Flayra +// - Allow right-click to cancel building placement +// - Fixed help icons +// - Added a couple utility functions +// - Reworked order notification +// - Reworked blip network messages to avoid hard-coded limit +// - Sent max resources down with current resources +// - Countdown sound no longer prevents other hud sounds +// - Alien trigger sounds +// - New order sounds +// - No longer disable nodes out of our cost range +// +// Revision 1.57 2002/09/25 20:47:22 Flayra +// - Don't draw elements on HUD when dead +// - UI refactoring +// - Split reticle help into help text and reticle text +// - Removed use order +// - Added separate select sound for alien +// - Multiple move sounds +// - Only draw entity build/health status when under reticle (no more scanning around you) +// - Added 3 new sayings +// +// Revision 1.56 2002/09/23 22:18:25 Flayra +// - Added alien build circles +// - Game status changes so particles aren't sent every time +// - Demo playback changes (save restore basic data that HUD already has) +// - New alert sounds +// - Skin support +// +// Revision 1.55 2002/09/09 19:55:24 Flayra +// - Added hive info indicator +// - Fixed bug where reticle tooltip help text wasn't being set until a weapon was selected +// - Fixed release mode bug where tooltips weren't expiring +// - Fixed bug where marine upgrades blinked +// - "No commander" indicator now blinks +// +// Revision 1.54 2002/08/31 18:01:01 Flayra +// - Work at VALVe +// +// Revision 1.53 2002/08/16 02:37:49 Flayra +// - HUD sounds no longer cut each other off (they won't play instead of cutting off another sound) +// - Tooltip sounds +// - Selection issues +// - Draw rings around buildings that need to be built +// - Removed old overwatch code +// +// Revision 1.52 2002/08/09 01:02:51 Flayra +// - Added hooks for demo playback, removed prediction selection +// +// Revision 1.51 2002/08/02 22:51:28 Flayra +// - Fixed memory overwrite...eek! +// +// Revision 1.50 2002/08/02 21:59:12 Flayra +// - Added reticle help, new tooltip system and much nicer order drawing! Refactored view model drawing a bit, hoping to make texture blending work for it. +// +// Revision 1.49 2002/07/26 23:05:01 Flayra +// - Generate numerical feedback for damage events +// - Refactoring for more info when looking at something (instead of bad-looking player names only) +// +// Revision 1.48 2002/07/23 17:07:37 Flayra +// - Added visually-smooth energy level, added versatile location code, new hive sight info, refactored to remove extra sprites (128 HUD sprites bug), commander tech help fixes +// +// Revision 1.47 2002/07/08 17:07:56 Flayra +// - Started to add display of marine upgrade sprite, fixed bug where building indicators aren't displayed after a map change, info_location drawing changes, primal scream color tweak, removed old hive drawing code +// +// Revision 1.46 2002/07/01 21:35:05 Flayra +// - Removed lots of outdated sprites and sprite code, added building ranges, fixed ghost building problem (bug #82) +// +// Revision 1.45 2002/06/25 18:03:09 Flayra +// - Added info_locations, removed old weapon help system, added smooth resource swelling, lots of alien UI usability changes, fixed problem with ghost building +// +// Revision 1.44 2002/06/10 19:55:36 Flayra +// - New commander UI (bindable via hotkeys, added REMOVE_SELECTION for when clicking menu options when no players selected) +// +// Revision 1.43 2002/06/03 16:48:45 Flayra +// - Help sprites moved into one animated sprite, select sound volume reduced (now that sound is normalized) +// +// Revision 1.42 2002/05/28 17:48:14 Flayra +// - Minimap refactoring, reinforcement refactoring, new hive sight fixes, recycling support +// +// Revision 1.41 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifndef AVHHUD_H +#define AVHHUD_H + +#include "ui/UIHud.h" +#include "mod/AvHConstants.h" +#include "mod/AvHSpecials.h" +#include "mod/AvHSharedTypes.h" +#include "mod/AvHParticleSystem.h" +#include "common/entity_state.h" +#include "VGUI_ProgressBar.h" +#include "mod/AvHEntityHierarchy.h" +#include "ui/MarqueeComponent.h" +#include "mod/AvHOrder.h" +#include "mod/AvHMessage.h" +#include "mod/AvHAmbientSound.h" +#include "mod/AvHTechTree.h" +#include "mod/AvHVisibleBlipList.h" +#include "mod/AvHMapExtents.h" +#include "mod/AvHSpecials.h" +#include "util/GammaTable.h" +#include "mod/AvHBaseInfoLocation.h" +#include "mod/AvHTooltip.h" +#include "mod/AvHTechSlotManager.h" +#include "mod/AvHHudConstants.h" +#include "mod/AvHOverviewMap.h" + +class AvHTeamHierarchy; +class PieMenu; +class FadingImageLabel; +using vgui::Cursor; + +struct AnimatedSprite +{ + float mCurrentFrame; + int mData; +}; + +class SelectionEffect +{ +public: + SelectionEffect() : mEntIndex(-1), mAngleOffset(0) + {} + + int mEntIndex; + int mAngleOffset; +}; + +typedef enum +{ + MAIN_MODE, + EDITPS_MODE, + SCOREBOARD_MODE +} UIMode; + +typedef vector SelectionListType; + +class NumericalInfoEffect +{ +public: + NumericalInfoEffect(float inPosition[3], float inNumber, int inEventType, float inTimeCreated); + void GetPosition(float* outPosition) const; + float GetNumber() const; + int GetEventType() const; + float GetTimeCreated() const; + void SetPosition(float inPosition[3]); + +private: + float mPosition[3]; + float mNumber; + int mEventType; + float mTimeCreated; +}; + +const int kNumUpgradeLines = 5; + +class AvHHud : public UIHud +{ +public: + AvHHud(const string& inFilename, UIFactory* inFactory); + virtual ~AvHHud(void); + + void OnActivateSteamUI(); + void OnDeactivateSteamUI(); + void OnLostFocus(); + bool OnKeyEvent(int virtualKey, int scanCode, bool pressed); + + void AddMiniMapAlert(float x, float y); + void AddNumericalInfoMessage(float inOrigin[3], float inNumber, int inEventType); + void AddTooltip(const char* inMessageText, bool inIsToolTip = true, float inTooltipWidth = kToolTipMaxWidth); + bool AddTooltipOnce(const char* inMessageText, bool inIsToolTip = true); + void Cancel(); + + void ClearSelection(); + + void ClientProcessEntity(struct entity_state_s* inEntity); + + void ComponentJustPainted(Panel* inPanel); + + bool GetAndClearAlienAbility(AvHMessageID& outMessageID); + bool GetAndClearGroupEvent(AvHMessageID& outMessageID); + bool GetAndClearTechEvent(AvHMessageID& outMessageID); + bool GetLastHotkeySelectionEvent(AvHMessageID& outMessageID); + void SetLastHotkeySelectionEvent(AvHMessageID inMessageID); + + // Returns true if particles should be rendered in the HUD. + bool GetParticlesVisible() const; + bool GetSafeForSpriteDrawing() const; + + void ClearTrackingEntity(); + int GetTrackingEntity() const; + + bool GetIsRegionBlockedByUI(float inNormX, float inNormY); + + //int GetArmorLevel(void) const; + int GetCommanderIndex() const; + bool GetHasJetpack() const; + int GetHelpIconFrameFromUser3(AvHUser3 inUser3); + HSPRITE GetHelpSprite() const; + bool GetHasAlienUpgradesAvailable() const; + bool GetIsAlien() const; + bool GetIsBeingDigested() const; + bool GetIsEnsnared() const; + bool GetIsDigesting() const; + bool GetIsStunned() const; + bool GetIsNotInControl() const; + bool GetIsInTopDownMode() const; + bool GetIsMarine() const; + bool GetIsRelevant() const; + bool GetIsShowingMap() const; + AvHPlayMode GetPlayMode(void) const; + bool GetAlienHelpForMessage(int inMessageID, string& outHelpText, int& outPointCost) const; + bool GetDoesPlayerHaveOrder() const; + bool GetHelpForMessage(int inMessageID, string& outHelpText) const; + bool GetInTopDownMode() const; + bool GetIsSelecting() const; + OrderListType GetOrderList() const; + AvHOrderType GetOrderMode() const; + bool GetCenterPositionForGroup(int inGroupNumber, vec3_t& outCenterPosition) const; + bool GetMouseOneDown() const; + bool GetMouseTwoDown() const; + bool GetAndClearTopDownScrollAmount(int& outX, int& outY, int& outZ); + bool GetAndClearSelectionEvent(vec3_t& outSelectionXY, AvHMessageID& outMessageID); + EntityListType GetSelected() const; + const AvHTechSlotManager& GetTechSlotManager() const; + bool GetTranslatedUser3Name(AvHUser3 inUser3, string& outString) const; + bool GetTranslatedUser3Description(AvHUser3 inUser3, bool inFriendly, string& outString) const; + vec3_t GetVisualOrigin() const; + + AvHMessageID HotKeyHit(char inChar); + + virtual void Init(void); + virtual void PostUIInit(void); + virtual void VidInit(void); + virtual void InitHUDData( void ); + virtual void InitExploitPrevention( void ); + void UpdateExploitPrevention(); + + bool GetGameStarted() const; + int GetGameTime() const; + int GetGameTimeLimit() const; + int GetCombatAttackingTeamNumber() const; + static bool GetShowingMap(); + + static void PlayStream(); + static void StopStream(); + + bool GetIsAlive(bool inIncludeSpectating = true) const; + void GhostBuildingCallback( struct tempent_s *ent, float frametime, float currenttime); + void CancelBuilding(); + + void PlayHUDSound(const char *szSound, float vol, float inSoundLength = 0.0f); + void PlayHUDSound(int iSound, float vol, float inSoundLength = 0.0f); + void PlayHUDSound(AvHHUDSound inSound); + + float GetHUDExperience() const; + int GetHUDExperienceLevel() const; + float GetHUDHandicap() const; + AvHUser3 GetHUDUser3() const; + AvHPlayMode GetHUDPlayMode() const; + AvHTeamNumber GetHUDTeam() const; + int GetHUDUpgrades() const; + int GetHUDMaxArmor() const; + int GetHUDMaxHealth() const; + void GetPrimaryHudColor(int& outR, int& outG, int& outB, bool inIgnoreUpgrades = false, bool gammaCorrect = true) const; + float GetTimeOfLastUpdate() const; + int GetMenuTechSlots() const; + + //void GetVisibleBlips(VisibleBlipListType& outBlipList); + + virtual int Redraw( float flTime, int intermission ); + virtual void ResetComponentsForUser3(); + void SetSelectingWeaponID(int inWeaponID, int inR = -1, int inG = -1, int inB = -1); + void SetTechHelpText(const string& inTechHelpText); + void DrawSelectionCircleOnGroundAtPoint(vec3_t inOrigin, int inRadius); + // : 0000988 + void DrawBuildHealthEffectsForEntity(int inEntityIndex, float inAlpha = 1.0f); + // : + void DrawSelectionAndBuildEffects(); + void DrawHUDNumber(int inX, int inY, int inFlags, int inNumber); + + int InitializeDemoPlayback(int inSize, unsigned char* inBuffer); + int InitializeDemoPlayback2(int inSize, unsigned char* inBuffer); + int InitializeWeaponInfoPlayback(int inSize, unsigned char* inBuffer); + + virtual bool Update(float inCurrentTime, string& outErrorString); + virtual void UpdateMusic(float inCurrentTime); + + bool SlotInput(int inSlot); + + const AvHMapExtents& GetMapExtents(); + float GetGammaSlope() const; + string GetMapName(bool inLocalOnly = false) const; + int GetMaxAlienResources() const; + int GetNumActiveHives() const; + void HideProgressStatus(); + void SetProgressStatus(float inPercentage, int inProgressbarType = 0); + + AvHVisibleBlipList& GetEnemyBlipList(); + AvHVisibleBlipList& GetFriendlyBlipList(); + + AvHEntityHierarchy& GetEntityHierarchy(); + + int GetLocalUpgrades() const; + string GetNameOfLocation(vec3_t inLocation) const; + const AvHTechTree& GetTechNodes() const; + + UIMode GetUIMode() const; + bool SwitchUIMode(UIMode inNewMode); + bool GetIsCombatMode() const; + bool GetIsNSMode() const; + + void HideResearchProgressStatus(); + void SetResearchProgressStatus(float inPercentage); + + AvHMessageID GetGhostBuilding() const; + void SetGhostBuildingMode(AvHMessageID inGhostBuilding); + + void SetAlienAbility(AvHMessageID inAlienAbility); + void SetRenderingSelectionView(bool inState); + + void SetClientDebugCSP(weapon_data_t* inWeaponData, float inNextPlayerAttack); + void SetCurrentWeaponData(int inCurrentWeaponID, bool inEnabled); + int GetCurrentWeaponID(void); + + void DrawTopDownBG(); + void DrawTranslatedString(int inX, int inY, const char* inStringToTranslate, bool inCentered = false, bool inIgnoreUpgrades = false, bool inTrimExtraInfo = false, float alpha = 1.0f); + void HandleFog(); + void PostModelRender(char* inModelName); + void PreRenderFrame(); + void PostRenderFrame(); + void RenderNoZBuffering(); + + void Render(); + void RenderCommonUI(); + void RenderMarineUI(); + void RenderCommanderUI(); + void RenderAlienUI(); + void RenderProgressBar(char *spriteName); + void RenderMiniMap(int inX, int inY, int inWidth, int inHeight); + + void RenderStructureRanges(); + void RenderStructureRange(vec3_t inOrigin, int inRadius, HSPRITE inSprite, int inRenderMode = kRenderNormal, int inFrame = 0, float inR = 0, float inG = 0.5, float inB = 0, float inAlpha = 1.0f); + + void DrawWarpedOverlaySprite(int spriteHandle, int numXFrames, int numYFrames, + float inWarpXAmount = 0.0f, float inWarpYAmount = 0.0f, + float inWarpXSpeed = 0.0f, float inWarpYSpeed = 0.0f); + + void DrawActionButtons(); + void DrawHotgroups(); + void DrawPendingRequests(); + + void SetCurrentUseableEnergyLevel(float inEnergyLevel); + + // Network messages + int AlienInfo(const char* pszName, int iSize, void* pbuf); + int BlipList(const char* pszName, int iSize, void* pbuf); + int ClScript(const char *pszName, int iSize, void *pbuf); + int Countdown(const char* pszName, int iSize, void* pbuf); + int DebugCSP(const char* pszName, int iSize, void* pbuf); + int EditPS(const char* pszName, int iSize, void* pbuf); + int DelEntHier(const char *pszName, int iSize, void *pbuf); + int EntHier(const char *pszName, int iSize, void *pbuf); + int Fog(const char* pszName, int iSize, void* pbuf); + int SetUpgrades(const char* pszName, int iSize, void* pbuf); + int ListPS(const char* pszName, int iSize, void* pbuf); + int DelParts(const char *pszName, int iSize, void *pbuf); + int Particles(const char *pszName, int iSize, void *pbuf); + int SoundNames(const char *pszName, int iSize, void *pbuf); + int PlayHUDNot(const char* pszName, int iSize, void* pbuf); + + int BalanceVar(const char* pszName, int iSize, void* pbuf); + int ServerVar(const char* pszName, int iSize, void* pbuf); + + int GameStatus(const char* pszName, int iSize, void* pbuf); + int MiniMap(const char* pszName, int iSize, void* pbuf); + // : 0000971 + int IssueOrder(const char* pszName, int iSize, void* pbuf); + // : + int Progress(const char* pszName, int iSize, void* pbuf); + int SetGmma(const char* pszName, int iSize, void* pbuf); + int SetSelect(const char* pszName, int iSize, void* pbuf); + int SetRequest(const char* pszName, int iSize, void* pbuf); + int SetOrder(const char* pszName, int iSize, void* pbuf); + int SetupMap(const char* pszName, int iSize, void* pbuf); + int SetTopDown(const char* pszName, int iSize, void* pbuf); + int SetTech(const char* pszName, int iSize, void* pbuf); + int TechSlots(const char* pszName, int iSize, void* pbuf); + + void GetSpriteForUser3(AvHUser3 inUser3, int& outSprite, int& outFrame, int& outRenderMode); + + int GetCurrentSquad() const; + AvHOverviewMap& GetOverviewMap(); + + void ShowCrosshair(); + void HideCrosshair(); + + // This function should be used instead of the global SetCrosshair. + void SetCurrentCrosshair(HSPRITE hspr, wrect_t rc, int r, int g, int b); + + static void ResetGammaAtExit(); + static int ResetGammaAtExitForOnExit(); + static void ResetGammaAtExit(int inSig); + + void SetViewport(const int inViewport[4]); + void GetViewport(int outViewport[4]) const; + + const AvHFont& GetSmallFont() const; + cl_entity_s* GetVisiblePlayer() const; + + float GetServerVariableFloat(const char* inName) const; + + // : + void SetCenterText(const char* inText); + void DrawCenterText(); + void ClearCenterText(); + // : + +private: + + // : + std::string mCenterText; + float mCenterTextTime; + // : + + bool GetCommanderLabelText(std::string& outCommanderName) const; + + void AddCommands(); + void ClearData(); + void DisplayCombatUpgradeMenu(bool inVisible); + void DrawMouseCursor(int inBaseX, int inBaseY); + void DrawOrders(); + void DrawHelpIcons(); + // : 0000971 + void GetOrderDirection(vec3_t inTarget, int inOrderType); + void DrawTeammateOrders(); + // : 0000992 + void DrawDisplayOrder(); + void SetDisplayOrder(int inOrderType, int inOrderIndex, string inText1, string inText2, string inText3); + // : + void DrawHUDStructureNotification(); + void DrawInfoLocationText(); + void DrawPlayerNames(); + void DrawReticleInfo(); + void DrawToolTips(); + // : 0000971 -- added inAlpha + void DrawWorldSprite(int inSpriteHandle, int inRenderMode, vec3_t inWorldPosition, int inFrame, float inWorldSize, float inAlpha = 1.0f); + // : + void DrawOrderIcon(const AvHOrder& inOrder); + void DrawOrderText(const AvHOrder& inOrder); + int GetFrameForOrderType(AvHOrderType inOrderType) const; + void GetReticleTextDrawingInfo(float& outNormX, float& outNormY, bool& outCentered) const; + void DrawTechTreeSprite(AvHMessageID inMessageID, int inPosX, int inPosY, int inWidth, int inHeight, int inFrame); + int GetTechTreeSprite(AvHMessageID inMessageID); + void GetTooltipDrawingInfo(float& outNormX, float& outNormY) const; + string GetRankTitle(bool inShowUnspentLevels = false) const; + bool GetShouldDisplayUser3(AvHUser3 inUser3) const; + void InitCommanderMode(); + void InitializeDemoRecording(); + void InitMenu(const string& inMenuName); + void ChangeUpgradeCosts(int inOldMessageID, int inNewMessageID, const char* inText); + void ChangeUpgradeCostsForMenu(PieMenu* inMenu, int inOldMessageID, int inNewMessageID, const char* inText); + void DisplayMessage(const char* inMessage); + bool GetAmbientSoundNameFromIndex(string& outSoundName, int inSoundIndex) const; + EntityListType GetDrawPlayerOrders() const; + bool GetEntityInfoString(int inEntityID, string& outEntityInfoString, bool& outIsEnemy); + void ModifyAmbientSoundEntryIfChanged(bool inSoundOn, int inSoundIndex, int inEntIndex, float inTimeStarted, int inVolume, int inFadeDistance, int inFlags, Vector inOrigin); + void ResetTopDownUI(); + bool SetGamma(float inSlope); + void SetReinforcements(int inReinforcements); + void SetHelpMessage(const string& inHelpText, bool inForce = false, float inNormX = -1, float inNormY = -1); + void SetActionButtonHelpMessage(const string& inHelpText); + void SetReticleMessage(const string& inHelpText); + void OrderNotification(const AvHOrder& inOrder); + virtual void ResetGame(bool inMapChanged = false); + + bool SetCursor(AvHOrderType inOrderType); + void GetCursor(HSPRITE& outSprite, int& outFrame); + + void SetSelectionEffects(EntityListType& inUnitList); + //void UpdateSelectionEffects(float inTimePassed); + void TraceEntityID(int& outEntityID); + bool GetIsMouseInRegion(int inX, int inY, int inWidth, int inHeight); + void GetMousePos(int& outX, int& outY) const; + + // Help system + void InternalHelpTextThink(); + bool ProcessAlien(); + bool ProcessAlienHelp(); + bool ProcessEntityHelp(); + bool ProcessGeneralHelp(); + bool ProcessOrderHelp(); + bool ProcessWeaponsHelp(); + + //void ResetUpgradeCosts(); + //void ResetUpgradeCostsForMenu(PieMenu* inMenu); + void UpdateAlienUI(float inCurrentTime); + void UpdateCommonUI(); + void UpdateDataFromVuser4(float inCurrentTime); + void UpdateMarineUI(float inCurrentTime); + void UpdateUpgradeCosts(); + void UpdateEnableState(PieMenu* inMenu); + void UpdateCountdown(float inCurrentTime); + void UpdateHierarchy(); + void UpdateInfoLocation(); + void UpdatePieMenuControl(); + void UpdateEntityID(float inCurrentTime); + void UpdateTooltips(float inCurrentTime); + void UpdateStructureNotification(float inCurrentTime); + void UpdateProgressBar(); + void UpdateDemoRecordPlayback(); + void UpdateBuildingPlacement(); + void UpdateResources(float inTimePassed); + void UpdateSelection(); + void UpdateSpectating(); + void UpdateBuildResearchText(); + void UpdateHelpText(); + void UpdateTechNodes(); + void UpdateAmbientSounds(); + void UpdateFromEntities(float inCurrentTime); + void UpdateViewModelEffects(); + + int mResources; + int mUser2OfLastResourceMessage; + int mMaxResources; + int mVisualResources; + float mExperience; + int mExperienceLevel; + int mExperienceLevelLastDrawn; + int mExperienceLevelSpent; + float mTimeOfLastLevelUp; + AvHMessageID mMenuImpulses[kNumUpgradeLines]; + + float mCountDownClock; + int mLastTickPlayed; + int mNumTicksToPlay; + float mTimeOfLastUpdate; + float mTimeOfNextHudSound; + AvHHUDSound mLastHUDSoundPlayed; + float mTimeOfCurrentUpdate; + + AvHOverviewMap mOverviewMap; + + AvHTeamHierarchy* mHierarchy; + AvHTeamHierarchy* mShowMapHierarchy; + AvHEntityHierarchy mEntityHierarchy; + + EntityListType mSelected; + EntityListType mGroups[kNumHotkeyGroups]; + EntityListType mSelectAllGroup; + AvHUser3 mGroupTypes[kNumHotkeyGroups]; + AvHAlertType mGroupAlerts[kNumHotkeyGroups]; + + typedef map PendingRequestListType; + PendingRequestListType mPendingRequests; + + AvHUser3 mLastUser3; + AvHTeamNumber mLastTeamNumber; + AvHPlayMode mLastPlayMode; + + bool mSelectionJustChanged; + bool mMouseOneDown; + bool mMouseTwoDown; + int mMouseOneStartX; + int mMouseOneStartY; + bool mLeftMouseStarted; + bool mLeftMouseEnded; + bool mPlacingBuilding; + int mMouseTwoStartX; + int mMouseTwoStartY; + bool mRightMouseStarted; + bool mRightMouseEnded; + vec3_t mMouseWorldPosition; + vec3_t mLeftMouseWorldStart; + vec3_t mLeftMouseWorldEnd; + vec3_t mRightMouseWorldStart; + vec3_t mRightMouseWorldEnd; + MarqueeComponent* mSelectionBox; + int mMouseCursorX; + int mMouseCursorY; + string mPieMenuControl; + + OrderListType mOrders; + //AvHOrderType mOrderMode; + + // : 0000971 + map< int, TeammateOrderType > mTeammateOrder; + // : 0000992 + float mDisplayOrderTime; + int mDisplayOrderType; + int mDisplayOrderDirection; + int mDisplayOrderIndex; + string mDisplayOrderText1; + string mDisplayOrderText2; + string mDisplayOrderText3; + int mCurrentOrderTarget; + int mCurrentOrderType; + float mCurrentOrderTime; + // : + AvHMessageID mTechEvent; + AvHMessageID mAlienAbility; + AvHMessageID mGroupEvent; + AvHMessageID mLastHotkeySelectionEvent; + int mTrackingEntity; + + AvHAlienUpgradeListType mUpgrades; + AvHAlienUpgradeCategory mCurrentUpgradeCategory[ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE]; + int mNumUpgradesAvailable; + + Label* mCommanderResourceLabel; + Label* mOverwatchRangeLabel; + ProgressBar* mGenericProgressBar; + ProgressBar* mResearchProgressBar; + ProgressBar* mAlienProgressBar; + Label* mResearchLabel; + string mPreviousHelpText; + float mTimeLastHelpTextChanged; + + int mNumMovement; + int mNumSensory; + int mNumDefense; + + bool mHasGrenades; + bool mHasWelder; + bool mHasMines; + + int mSelectedNodeResourceCost; + float mCurrentUseableEnergyLevel; + float mVisualEnergyLevel; + int mUser2OfLastEnergyLevel; + + static void PlayRandomSongHook(); + static void ShowMap(); + static void HideMap(); + + static void ShowCommandMenu(); + static void HideCommandMenu(); + + float mTimeLastOverwatchPulse; + bool mInTopDownMode; + int mNumLocalSelectEvents; + AvHMapMode mMapMode; + + static GammaTable sPregameGammaTable; + static GammaTable sGameGammaTable; + float mDesiredGammaSlope; + + typedef vector AmbientSoundListType; + AmbientSoundListType mAmbientSounds; + + AvHTechTree mTechNodes; + + string mMapName; + AvHMapExtents mMapExtents; + + // If this isn't MESSAGE_NULL, it means we are trying to build a building of this tech + AvHMessageID mGhostBuilding; + AvHMessageID mValidatedBuilding; + bool mCreatedGhost; + bool mCancelBuilding; + bool mCurrentGhostIsValid; + vec3_t mNormBuildLocation; + vec3_t mGhostWorldLocation; + + // Added by mmcguire. + int mSelectionBoxX1; + int mSelectionBoxY1; + int mSelectionBoxX2; + int mSelectionBoxY2; + bool mSelectionBoxVisible; + + Vector mCommanderPAS; + + StringList mSoundNameList; + + AvHVisibleBlipList mEnemyBlips; + AvHVisibleBlipList mFriendlyBlips; + + bool mMarineUIDrawUI; + HSPRITE mMarineUIJetpackSprite; + + HSPRITE mAlienUIEnergySprite; + HSPRITE mAlienUICloakSprite; + + HSPRITE mMembraneSprite; + HSPRITE mDigestingSprite; + HSPRITE mBackgroundSprite; + HSPRITE mTopDownTopSprite; + HSPRITE mTopDownBottomSprite; + HSPRITE mMarineTopSprite; + HSPRITE mLogoutSprite; + HSPRITE mCommandButtonSprite; + HSPRITE mCommandStatusSprite; + HSPRITE mSelectAllSprite; + + HSPRITE mMarineOrderIndicator; + HSPRITE mMarineUpgradesSprite; + + // : 0000971 + HSPRITE mTeammateOrderSprite; + // : + typedef map SpriteListType; + SpriteListType mActionButtonSprites; + //SpriteListType mHelpSprites; + int mHelpSprite; + + typedef vector< pair > HelpIconListType; + HelpIconListType mHelpIcons; + EntityListType mHelpEnts; + + EntityListType mBuildingEffectsEntityList; + float mTimeOfLastEntityUpdate; + + HSPRITE mAlienUIUpgrades; + HSPRITE mAlienUIUpgradeCategories; + + HSPRITE mAlienBuildSprite; + HSPRITE mMarineBuildSprite; + + HSPRITE mAlienHealthSprite; + HSPRITE mMarineHealthSprite; + + HSPRITE mHealthEffectsSprite; + HSPRITE mBuildCircleSprite; + //HSPRITE mSiegeTurretSprite; + SelectionListType mSelectionEffects; + + + //HSPRITE mMappingTechSprite; + + HSPRITE mHiveInfoSprite; + HSPRITE mHiveHealthSprite; + HSPRITE mOrderSprite; + HSPRITE mCursorSprite; + HSPRITE mMarineCursor; + HSPRITE mAlienCursor; + HSPRITE mAlienLifeformsCursor; + int mCurrentCursorFrame; + + int mProgressBarEntityIndex; + int mProgressBarParam; + int mProgressBarCompleted; + float mProgressBarStatus; + float mProgressBarLastDrawn; + int mProgressBarDrawframe; + + bool mFogActive; + vec3_t mFogColor; + float mFogStart; + float mFogEnd; + + HSPRITE mExperienceBarSprite; + HSPRITE mProgressBarSprite; + + AvHBaseInfoLocationListType mInfoLocationList; + string mLocationText; + + string mReticleInfoText; + //int mReticleInfoColorR; + //int mReticleInfoColorG; + //int mReticleInfoColorB; + //float mReticleInfoColorA; + //int mReticleInfoScreenX; + //int mReticleInfoScreenY; + int mSelectingWeaponID; + string mTechHelpText; + AvHMessageID mSelectingNodeID; + + struct tempent_s* mLastGhostBuilding; + + typedef vector NumericalInfoEffectListType; + NumericalInfoEffectListType mNumericalInfoEffects; + + AvHTooltip mHelpMessage; + AvHTooltip mReticleMessage; + AvHTooltip mTopDownPlayerNameMessage; + AvHTooltip mTopDownActionButtonHelp; + AvHTooltip mCombatUpgradeMenu; + bool mDrawCombatUpgradeMenu; + + typedef vector AvHTooltipListType; + AvHTooltipListType mTooltips; + + HiveInfoListType mHiveInfoList; + + bool mRecordingLastFrame; + + float mTimeOfLastHelpText; + StringList mDisplayedToolTipList; + + int mCurrentWeaponID; + bool mCurrentWeaponEnabled; + + AvHTechSlotManager mTechSlotManager; + UIMode mCurrentUIMode; + + int mMenuTechSlots; + int mCurrentSquad; + int mBlinkingAlertType; + + float mGameTime; + int mTimeLimit; + int mCombatAttackingTeamNumber; + bool mGameStarted; + bool mGameEnded; + + AvHTeamNumber mLastTeamSpectated; + + typedef struct + { + AvHMessageID mStructureID; + float mTime; + int mPlayerIndex; + Vector mLocation; + } HUDNotificationType; + + typedef vector< HUDNotificationType > StructureHUDNotificationListType; + StructureHUDNotificationListType mStructureNotificationList; + + int mCrosshairShowCount; + HSPRITE mCrosshairSprite; + wrect_t mCrosshairRect; + int mCrosshairR; + int mCrosshairG; + int mCrosshairB; + + int mViewport[4]; // the viewport coordinates x ,y , width, height + int mSpecialViewport[4]; // the viewport coordinates x ,y , width, height + + bool mSteamUIActive; + + typedef std::map ServerVariableMapType; + ServerVariableMapType mServerVariableMap; + + static bool sShowMap; + +}; + +#endif diff --git a/main/source/mod/AvHHudRender.cpp b/main/source/mod/AvHHudRender.cpp index ef0f255..761091b 100644 --- a/main/source/mod/AvHHudRender.cpp +++ b/main/source/mod/AvHHudRender.cpp @@ -1,4259 +1,4450 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: All graphical HUD operations -// -// $Workfile: AvHHudRender.cpp $ -// $Date: 2002/10/24 21:31:13 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHHudRender.cpp,v $ -// Revision 1.25 2002/10/24 21:31:13 Flayra -// - Removed some parts of energy drawing (unneeded clutter) -// - New marine HUD artwork (mainhud.spr) -// - Reworked upgrade drawing to only draw one of a type of upgrade (this was the last thing for v1.0, so it's a bit yucky) -// -// Revision 1.24 2002/10/18 22:20:14 Flayra -// - Fixed alien mass crash when a player left the server -// -// Revision 1.23 2002/10/16 00:59:33 Flayra -// - Added titles for umbra and primal scream -// - Don't draw building circles for entities on the other team (not even for commander) -// - Tried drawing building circles for ghost buildings, but it was confusing -// -// Revision 1.22 2002/10/03 18:56:10 Flayra -// - Moved alien energy to fuser3 -// - Changed limits for energy and resources -// - Draw order icons centered around order position -// - Don't draw health rings for opposing teams -// -// Revision 1.21 2002/09/25 20:48:37 Flayra -// - Allow more UI to draw when gestating -// - Only draw text for blip closest to reticle -// - Don't draw stuff when dead -// - Changed order blinking -// -// Revision 1.20 2002/09/23 22:19:58 Flayra -// - Added "paralyzed" indicator -// - HUD element repositioning and refactoring -// - Added alien build circles -// - Added visible motion-tracking sprite to marine HUD -// - Removed special siege sprite -// -// Revision 1.19 2002/09/09 19:57:33 Flayra -// - Fixed black edges in D3D -// - Added blinking "webbed" indicator -// - Refactored UI constants -// - Fixed help icons -// - Added hive info indicator -// - Draw more info as spectator -// -// Revision 1.18 2002/08/31 18:01:01 Flayra -// - Work at VALVe -// -// Revision 1.17 2002/08/16 02:38:44 Flayra -// - Draw "webbed" message -// - Draw health for buildings and players -// - Removed old overwatch code -// -// Revision 1.16 2002/08/09 01:03:36 Flayra -// - Started refactoring for moving variable sprite hole drawing into TriAPI -// -// Revision 1.15 2002/08/02 21:56:54 Flayra -// - Added reticle help, new tooltip system and much nicer order drawing! Also changed jetpack label to use font, and refactored some sprite names. -// -// Revision 1.14 2002/07/26 23:05:06 Flayra -// - Generate numerical feedback for damage events -// -// Revision 1.13 2002/07/23 17:09:41 Flayra -// - Add ability to centered, translated strings, visually-smooth energy level, new hive sight info, draw parasited message, draw marine upgrades, new method of drawing alien upgrades (overlapping and offset) -// -// Revision 1.12 2002/07/10 14:42:26 Flayra -// - Removed cl_particleinfo drawing differences -// -// Revision 1.11 2002/07/08 17:07:56 Flayra -// - Started to add display of marine upgrade sprite, fixed bug where building indicators aren't displayed after a map change, info_location drawing changes, primal scream color tweak, removed old hive drawing code -// -// Revision 1.10 2002/07/01 21:36:23 Flayra -// - Added primal scream effect, added building ranges for ghost buildings, removed outdated code, removed mapping build sprite, call vidinit() on hive sight sprites -// -// Revision 1.9 2002/06/25 18:03:09 Flayra -// - Added info_locations, removed old weapon help system, added smooth resource swelling, lots of alien UI usability changes, fixed problem with ghost building -// -// Revision 1.8 2002/06/10 19:57:01 Flayra -// - Allow drawing just a portion of a texture when scaling it, draw team hierarchy for soldiers -// -// Revision 1.7 2002/06/03 16:49:20 Flayra -// - Help sprites moved into one animated sprite -// -// Revision 1.6 2002/05/28 17:49:06 Flayra -// - Hive sight sprite changes -// -// Revision 1.5 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHHud.h" -#include "cl_dll/hud.h" -#include "cl_dll/cl_util.h" -#include "mod/AvHConstants.h" -#include "mod/AvHClientVariables.h" -#include "mod/AvHSpecials.h" -#include "common/cl_entity.h" -#include "mod/AvHTitles.h" -#include "pm_shared/pm_debug.h" -#include "util/MathUtil.h" -#include "common/r_efx.h" -#include "cl_dll/eventscripts.h" -#include "mod/AvHSprites.h" -#include "ui/UIUtil.h" -#include "types.h" -#include -#include "common/com_model.h" -#include "cl_dll/studio_util.h" -#include "cl_dll/r_studioint.h" -#include "mod/AvHMiniMap.h" -#include "mod/AvHActionButtons.h" -#include "util/STLUtil.h" -#include "mod/AvHSharedUtil.h" -#include "common/event_api.h" -#include "mod/AvHScriptManager.h" -#include -#include -#include "mod/AvHParticleSystemManager.h" -#include "mod/AvHTeamHierarchy.h" -#include "mod/AvHClientUtil.h" -#include "mod/AvHTooltip.h" -#include "cl_dll/demo.h" -#include "common/demo_api.h" -#include "mod/AvHHudConstants.h" -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHCommanderModeHandler.h" -#include "common/ref_params.h" -#include "mod/AvHTechImpulsePanel.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHSpriteAPI.h" -#include "mod/AvHParticleEditorHandler.h" -#include -#include "common/entity_types.h" - -void IN_GetMousePos( int *mx, int *my ); - -// Externs -extern int g_iVisibleMouse; -extern playermove_t* pmove; -extern engine_studio_api_t IEngineStudio; -extern "C" Vector gPredictedPlayerOrigin; -extern vec3_t v_origin; -extern vec3_t v_angles; - -//extern vec3_t gPlayerOrigin; -//extern vec3_t gPlayerAngles; -extern ref_params_s* pDemoRefParams; -extern vec3_t gPlaybackViewOrigin; -extern AvHCommanderModeHandler gCommanderHandler; -extern AvHParticleEditorHandler gParticleEditorHandler; -float kD3DErrorValue = 0.01f; - - -vec3_t GetViewOrigin() -{ - vec3_t theOrigin = v_origin; - - //cl_entity_t* theViewEntity = gEngfuncs.GetLocalPlayer();//gEngfuncs.GetViewModel(); - //if(theViewEntity && /*pDemoRefParams &&*/ gEngfuncs.pDemoAPI->IsPlayingback()) - //{ - // //theOrigin = pDemoRefParams->vieworg; - // theOrigin = theViewEntity->origin; - //} - - if(gEngfuncs.pDemoAPI->IsPlayingback()) - { - theOrigin = gPlaybackViewOrigin; - } - - return theOrigin; - // return v_origin; -} - -void BuildLerpedPoint(float inXPercentage, float inYPercentage, const Vector& inUpperLeft, const Vector& inUpperRight, const Vector& inLowerLeft, Vector& outPoint) -{ - ASSERT(inXPercentage >= 0.0f); - ASSERT(inXPercentage <= 1.0f); - ASSERT(inYPercentage >= 0.0f); - ASSERT(inYPercentage <= 1.0f); - - Vector theUpperLeftToUpperRight; - VectorSubtract(inUpperRight, inUpperLeft, theUpperLeftToUpperRight); - - Vector theUpperLeftToLowerLeft; - VectorSubtract(inLowerLeft, inUpperLeft, theUpperLeftToLowerLeft); - - Vector theXComp; - VectorScale(theUpperLeftToUpperRight, inXPercentage, theXComp); - - Vector theYComp; - VectorScale(theUpperLeftToLowerLeft, inYPercentage, theYComp); - - outPoint = inUpperLeft + theXComp + theYComp; - - //float theXPercentage = (outPoint.x - inUpperLeft.x)/(theXComp.x + theYComp.x); - //float theYPercentage = (outPoint.y - inUpperLeft.y)/(theXComp.y + theYComp.y); -} - -void CalculatePlaneInfo(const Vector& inUpperLeft, const Vector& inUpperRight, const Vector& inLowerLeft, float& outD, Vector& outPlaneNormal) -{ - // Cross two vectors for plane normal - Vector theUpperRightToUpperLeft; - VectorSubtract(inUpperLeft, inUpperRight, theUpperRightToUpperLeft); - theUpperRightToUpperLeft = theUpperRightToUpperLeft.Normalize(); - - Vector theUpperLeftToLowerLeft; - VectorSubtract(inLowerLeft, inUpperLeft, theUpperLeftToLowerLeft); - theUpperLeftToLowerLeft = theUpperLeftToLowerLeft.Normalize(); - - // Calculate plane normal - CrossProduct(theUpperRightToUpperLeft, theUpperLeftToLowerLeft, outPlaneNormal); - - // Plug in one of the points for D (Ax + By + Cz = -D) - outD = -(outPlaneNormal.x*inUpperLeft.x + outPlaneNormal.y*inUpperLeft.y + outPlaneNormal.z*inUpperLeft.z); -} - -void CalculatePointOnPlane(int inXPos, int inYPos, const Vector& inOrigin, const Vector& inPlaneNormal, float inPlaneD, Vector& outPoint) -{ - Vector theRay; - CreatePickingRay(inXPos, inYPos, theRay); - - // Solve for parametric t - float thePlaneA = inPlaneNormal.x; - float thePlaneB = inPlaneNormal.y; - float thePlaneC = inPlaneNormal.z; - - float theT = -(thePlaneA*inOrigin.x + thePlaneB*inOrigin.y + thePlaneC*inOrigin.z + inPlaneD)/(thePlaneA*theRay.x + thePlaneB*theRay.y + thePlaneC*theRay.z); - - // Now we have t, solve for the endpoint - outPoint.x = inOrigin.x + theT*theRay.x; - outPoint.y = inOrigin.y + theT*theRay.y; - outPoint.z = inOrigin.z + theT*theRay.z; -} - -void ProjectPointFromViewOrigin(int inDistanceToProject, int inScreenX, int inScreenY, Vector& outResult) -{ - Vector theRay; - CreatePickingRay(inScreenX, inScreenY, theRay); - - // project default distance along picking ray - VectorMA(GetViewOrigin(), inDistanceToProject, theRay, outResult); -} - -void AvHHud::DrawTranslatedString(int inX, int inY, const char* inStringToTranslate, bool inCentered, bool inIgnoreUpgrades, bool inTrimExtraInfo) -{ - // Translate - string theTranslatedText; - LocalizeString(inStringToTranslate, theTranslatedText); - if(theTranslatedText[0] == '#') - { - theTranslatedText = theTranslatedText.substr(1, theTranslatedText.size()); - } - - if(inTrimExtraInfo) - { - AvHCUTrimExtraneousLocationText(theTranslatedText); - } - - // Draw it - if(theTranslatedText != "") - { - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, inIgnoreUpgrades, false); - - char theCharBuffer[512]; - sprintf(theCharBuffer, "%s", theTranslatedText.c_str()); - - if(inCentered) - { - this->DrawHudStringCentered(inX, inY, ScreenWidth(), theCharBuffer, theR, theG, theB); - } - else - { - this->DrawHudString(inX, inY, ScreenWidth(), theCharBuffer, theR, theG, theB); - } - } -} - -bool gWarpHUDSprites = false; -float gWarpXAmount = 0.0f; -float gWarpYAmount = 0.0f; -float gWarpXSpeed = 0.0f; -float gWarpYSpeed = 0.0f; - -void SetWarpHUDSprites(bool inMode, float inWarpXAmount = 0.0f, float inWarpYAmount = 0.0f, float inWarpXSpeed = 0.0f, float inWarpYSpeed = 0.0f) -{ - gWarpHUDSprites = inMode; - gWarpXAmount = inWarpXAmount; - gWarpYAmount = inWarpYAmount; - gWarpXSpeed = inWarpXSpeed; - gWarpYSpeed = inWarpYSpeed; -} - -void DrawScaledHUDSprite(int inSpriteHandle, int inMode, int inRowsInSprite = 1, int inX = 0, int inY = 0, int inWidth = ScreenWidth(), int inHeight = ScreenHeight(), int inForceSpriteFrame = -1, float inStartU = 0.0f, float inStartV = 0.0f, float inEndU = 1.0f, float inEndV = 1.0f, float inRotateUVRadians = 0.0f, bool inUVWrapsOverFrames = false) -{ - // Count number of frames - int theNumFrames = SPR_Frames(inSpriteHandle); - - //int theSpriteWidth = SPR_Width(inSpriteHandle, 0); - //int theSpriteHeight = SPR_Height(inSpriteHandle, 0); - //float theAspectRatio = theSpriteWidth/theSpriteHeight; - - // ASSERT that the the number of rows makes sense for the number of frames - ASSERT(theNumFrames > 0); - float theFloatNumCols = (float)theNumFrames/inRowsInSprite; - int theNumCols = (int)theFloatNumCols; - ASSERT(theNumCols == theFloatNumCols); - - //int theNumRows = theNumFrames/theNumCols; - int theNumRows = inRowsInSprite; - - // Allow scaling of one frame, without tiling - if(inForceSpriteFrame != -1) - { - theNumRows = theNumCols = 1; - } - - // Make sure coords are bounded to allowable ranges - - inStartU = min(max(inStartU, kD3DErrorValue), 1.0f - kD3DErrorValue); - inStartV = min(max(inStartV, kD3DErrorValue), 1.0f - kD3DErrorValue); - inEndU = min(max(inEndU, kD3DErrorValue), 1.0f - kD3DErrorValue); - inEndV = min(max(inEndV, kD3DErrorValue), 1.0f - kD3DErrorValue); - - if(inRotateUVRadians != 0.0f) - { - // Rotate all the UV coords - vec3_t theAngles(0.0f, 0.0f, inRotateUVRadians); - float theMatrix[3][4]; - AngleMatrix(theAngles, theMatrix); - - float theRotatedValues[3]; - - float theStartValues[3] = {inStartU, inStartV, 0.0f}; - VectorRotate(theStartValues, theMatrix, theRotatedValues); - inStartU = theRotatedValues[0]; - inStartV = theRotatedValues[1]; - - float theEndValues[3] = {inEndU, inEndV, 0.0f}; - VectorRotate(theEndValues, theMatrix, theRotatedValues); - inEndU = theRotatedValues[0]; - inEndV = theRotatedValues[1]; - } - - // Calculate width and height of each quad - int theQuadScreenWidth = inWidth/theNumCols; - int theQuadScreenHeight = inHeight/theNumRows; - - //Vector thePickRay; - //int theHalfWidth = ScreenWidth/2; - //int theHalfHeight = ScreenHeight/2; - //CreatePickingRay(theHalfWidth, theHalfHeight, thePickRay); - - //char gDebugMessage[256]; - //sprintf(gDebugMessage, "(%d, %d): %f, %f, %f", theHalfWidth, theHalfHeight, thePickRay.x, thePickRay.y, thePickRay.z); - //CenterPrint(gDebugMessage); - - //float theRenderOrigin[3]; - //pVector theUp; - //pVector theRight; - //pVector theNormal; - //IEngineStudio.GetViewInfo(theRenderOrigin, (float*)&theUp, (float*)&theRight, (float*)&theNormal); - - //Demo_WriteVector(TYPE_VIEWANGLES, v_angles); - //Demo_WriteVector(TYPE_VIEWORIGIN, v_origin); - - vec3_t theRenderOrigin; - VectorCopy(GetViewOrigin(), theRenderOrigin); - - vec3_t theForward, theRight, theUp; - AngleVectors(v_angles, theForward, theRight, theUp); - - Vector theRenderOriginVector; - theRenderOriginVector.x = theRenderOrigin[0]; - theRenderOriginVector.y = theRenderOrigin[1]; - theRenderOriginVector.z = theRenderOrigin[2]; - - // This is the smallest value that displays and it still clips with the view model a little - const int kDistanceToProject = 10; - - // create picking ray for upper left of quad - Vector theUpperLeftRay; - //CreatePickingRay(inX, inY, theUpperLeftRay); - CreatePickingRay(0, 0, theUpperLeftRay); - - // create picking ray for lower left of quad - Vector theLowerLeftRay; - //CreatePickingRay(inX, inY+inHeight, theLowerLeftRay); - CreatePickingRay(0, ScreenHeight(), theLowerLeftRay); - - // create picking ray for upper right of quad - Vector theUpperRightRay; - //CreatePickingRay(inX+inWidth, inY, theUpperRightRay); - CreatePickingRay(ScreenWidth(), 0, theUpperRightRay); - - // project default distance along picking ray - Vector theUpperLeftWorldPos; - VectorMA(theRenderOrigin, kDistanceToProject, theUpperLeftRay, theUpperLeftWorldPos); - - Vector theUpperRightWorldPos; - VectorMA(theRenderOrigin, kDistanceToProject, theUpperRightRay, theUpperRightWorldPos); - - Vector theLowerLeftWorldPos; - VectorMA(theRenderOrigin, kDistanceToProject, theLowerLeftRay, theLowerLeftWorldPos); - - // Create formula for plane - float thePlaneD; - Vector thePlaneNormal; - CalculatePlaneInfo(theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePlaneD, thePlaneNormal); - - // loop through screen height - int theCurrentFrame = 0; - - // Allow drawing of just one frame - if(inForceSpriteFrame != -1) - { - theCurrentFrame = inForceSpriteFrame; - } - - for(int i = 0; i < theNumRows; i++) - { - // loop through screen width - for(int j = 0; j < theNumCols; j++) - { - // draw quad - gEngfuncs.pTriAPI->RenderMode(inMode); - gEngfuncs.pTriAPI->CullFace(TRI_NONE); - //gEngfuncs.pTriAPI->Brightness(1); - - if(gEngfuncs.pTriAPI->SpriteTexture((struct model_s*)gEngfuncs.GetSpritePointer(inSpriteHandle), theCurrentFrame)) - { - gEngfuncs.pTriAPI->Begin( TRI_TRIANGLE_STRIP ); - //gEngfuncs.pTriAPI->Color4f(1.0f, 1.0f, 1.0f, 1.0f); - - bool theIsFirstRow = (i == 0); - bool theIsFirstCol = (j == 0); - bool theIsLastRow = (i == (theNumRows-1)); - bool theIsLastCol = (j == (theNumCols-1)); - - float theStartU = inStartU; - float theStartV = inStartV; - float theEndU = inEndU; - float theEndV = inEndV; - - if(inUVWrapsOverFrames) - { - if((theNumCols > 1) && !theIsFirstCol) - { - theStartU = 0.0f; - } - if((theNumRows > 1) && !theIsFirstRow) - { - theStartV = 0.0f; - } - if((theNumCols > 1) && !theIsLastCol) - { - theEndU = 1.0f; - } - if((theNumRows > 1) && !theIsLastRow) - { - theEndV = 1.0f; - } - } - - // Optionally warp XY coords using current time - int theWarpXStartAmount = 0; - int theWarpYStartAmount = 0; - int theWarpXEndAmount = 0; - int theWarpYEndAmount = 0; - - if(gWarpHUDSprites) - { - float theCurrentTime = gHUD.GetTimeOfLastUpdate(); - float theNormXAmount = theCurrentTime*gWarpXSpeed - (int)(theCurrentTime*gWarpXSpeed); // Get fractional part of second - float theNormYAmount = theCurrentTime*gWarpYSpeed - (int)(theCurrentTime*gWarpYSpeed); - float theSinusoidalNormXAmount = cos(theNormXAmount*2.0f*M_PI); - float theSinusoidalNormYAmount = sin(theNormYAmount*2.0f*M_PI); - float theXAmount = theSinusoidalNormXAmount*gWarpXAmount;// - gWarpUAmount/2.0f; - float theYAmount = theSinusoidalNormYAmount*gWarpYAmount;// - gWarpVAmount/2.0f; - - if(!theIsFirstCol) - { - theWarpXStartAmount = theXAmount*ScreenWidth(); - } - if(!theIsLastCol) - { - theWarpXEndAmount = theXAmount*ScreenWidth(); - } - if(!theIsFirstRow) - { - theWarpYStartAmount = theYAmount*ScreenHeight(); - } - if(!theIsLastRow) - { - theWarpYEndAmount = theYAmount*ScreenHeight(); - } - } - - // Compensate for gamma - float theGammaSlope = gHUD.GetGammaSlope(); - float theColorComponent = 1.0f/theGammaSlope; - gEngfuncs.pTriAPI->Color4f(theColorComponent, theColorComponent, theColorComponent, 1.0f); - - Vector thePoint; - - //float theMinXPercentage = (float)j/theNumCols; - //float theMaxXPercentage = (float)(j+1)/theNumCols; - - //float theMinYPercentage = (float)i/theNumRows; - //float theMaxYPercentage = (float)(i+1)/theNumRows; - - int theMinXPos = inX + ((float)j/theNumCols)*inWidth + theWarpXStartAmount; - int theMinYPos = inY + ((float)i/theNumRows)*inHeight + theWarpYStartAmount; - - int theMaxXPos = inX + ((float)(j+1)/theNumCols)*inWidth + theWarpXEndAmount; - int theMaxYPos = inY + ((float)(i+1)/theNumRows)*inHeight + theWarpYEndAmount; - - // Lower left - Vector thePointOne; - //BuildLerpedPoint(theMinXPercentage, theMaxYPercentage, theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePointOne); - CalculatePointOnPlane(theMinXPos, theMaxYPos, theRenderOriginVector, thePlaneNormal, thePlaneD, thePointOne); - float theU = theStartU; - float theV = theEndV; - gEngfuncs.pTriAPI->TexCoord2f(theU, theV);// 0,1 - gEngfuncs.pTriAPI->Vertex3fv((float*)&thePointOne); - - // Upper left - Vector thePointTwo; - //BuildLerpedPoint(theMinXPercentage, theMinYPercentage, theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePointTwo); - CalculatePointOnPlane(theMinXPos, theMinYPos, theRenderOriginVector, thePlaneNormal, thePlaneD, thePointTwo); - theU = theStartU; - theV = theStartV; - //gEngfuncs.pTriAPI->TexCoord2f(inStartU, inStartV); // 0,0 - gEngfuncs.pTriAPI->TexCoord2f(theU, theV);// 0,1 - gEngfuncs.pTriAPI->Vertex3fv((float*)&thePointTwo); - - // Lower right - Vector thePointFour; - //BuildLerpedPoint(theMaxXPercentage, theMaxYPercentage, theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePointFour); - CalculatePointOnPlane(theMaxXPos, theMaxYPos, theRenderOriginVector, thePlaneNormal, thePlaneD, thePointFour); - theU = theEndU; - theV = theEndV; - //gEngfuncs.pTriAPI->TexCoord2f(inEndU, inEndV);// 1,1 - gEngfuncs.pTriAPI->TexCoord2f(theU, theV);// 0,1 - gEngfuncs.pTriAPI->Vertex3fv((float*)&thePointFour); - - // Upper right - Vector thePointThree; - //BuildLerpedPoint(theMaxXPercentage, theMinYPercentage, theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePointThree); - CalculatePointOnPlane(theMaxXPos, theMinYPos, theRenderOriginVector, thePlaneNormal, thePlaneD, thePointThree); - theU = theEndU; - theV = theStartV; - //gEngfuncs.pTriAPI->TexCoord2f(inEndU, inStartV); // 1,0 - gEngfuncs.pTriAPI->TexCoord2f(theU, theV);// 0,1 - gEngfuncs.pTriAPI->Vertex3fv((float*)&thePointThree); - - gEngfuncs.pTriAPI->End(); - } - - // Increment frame - theCurrentFrame++; - - // Allow drawing of just one frame - if(inForceSpriteFrame != -1) - { - theCurrentFrame = inForceSpriteFrame; - } - - // increment current x and y - //theCurrentScreenX += theQuadScreenWidth; - } - //theCurrentScreenX = 0; - //theCurrentScreenY += theQuadScreenHeight; - } -} - -void DrawVariableScaledHUDSprite(float inFactor, int inSpriteHandle, int inMode, int inX, int inY, int inWidth, int inHeight) -{ - // Draw as two scaled sprites, one for the level and one for the "empty" level - // Assumes that sprite has two frames, with the empty level being frame 0 and the full frame being frame 1 - int theWidth = inWidth; - float theStartU = 0.0f; - float theEndU = 1.0f; - - int theHeight = inFactor*inHeight; - float theStartV = 1.0f - inFactor; - float theEndV = 1.0f; - int theX = inX; - int theY = inY + inHeight - theHeight; - DrawScaledHUDSprite(inSpriteHandle, inMode, 1, theX, theY, theWidth, theHeight, 1, theStartU, theStartV, theEndU, theEndV); - - // Draw background - theHeight = (1.0f - inFactor)*inHeight; - theY = inY; - theStartV = 0.0f; - theEndV = 1.0f - inFactor; - DrawScaledHUDSprite(inSpriteHandle, inMode, 1, theX, theY, theWidth, theHeight, 0, theStartU, theStartV, theEndU, theEndV); -} - -void DrawSpriteOnGroundAtPoint(vec3_t inOrigin, int inRadius, HSPRITE inSprite, int inRenderMode = kRenderNormal, int inFrame = 0, float inAlpha = 1.0f) -{ - if(gEngfuncs.pTriAPI->SpriteTexture((struct model_s *)gEngfuncs.GetSpritePointer(inSprite), inFrame)) - { - gEngfuncs.pTriAPI->CullFace(TRI_NONE); - - gEngfuncs.pTriAPI->RenderMode(inRenderMode); - - //gEngfuncs.pTriAPI->RenderMode( kRenderTransTexture ); - //gEngfuncs.pTriAPI->Color4f(inR, inG, inB, inA); - - gEngfuncs.pTriAPI->Begin(TRI_TRIANGLE_STRIP); - - // Draw one quad - vec3_t thePoint = inOrigin; - - float theGammaSlope = gHUD.GetGammaSlope(); - ASSERT(theGammaSlope > 0.0f); - float theColorComponent = 1.0f/theGammaSlope; - - gEngfuncs.pTriAPI->Color4f(theColorComponent, theColorComponent, theColorComponent, inAlpha); - gEngfuncs.pTriAPI->Brightness(1.6f); - - thePoint[0] = inOrigin[0] - inRadius; - thePoint[1] = inOrigin[1] - inRadius; - gEngfuncs.pTriAPI->TexCoord2f(0, 0); - gEngfuncs.pTriAPI->Vertex3f(thePoint[0], thePoint[1], thePoint[2]); - - thePoint[0] = inOrigin[0] - inRadius; - thePoint[1] = inOrigin[1] + inRadius; - gEngfuncs.pTriAPI->TexCoord2f(0, 1); - gEngfuncs.pTriAPI->Vertex3f(thePoint[0], thePoint[1], thePoint[2]); - - thePoint[0] = inOrigin[0] + inRadius; - thePoint[1] = inOrigin[1] - inRadius; - gEngfuncs.pTriAPI->TexCoord2f(1, 0); - gEngfuncs.pTriAPI->Vertex3f(thePoint[0], thePoint[1], thePoint[2]); - - thePoint[0] = inOrigin[0] + inRadius; - thePoint[1] = inOrigin[1] + inRadius; - gEngfuncs.pTriAPI->TexCoord2f(1, 1); - gEngfuncs.pTriAPI->Vertex3f(thePoint[0], thePoint[1], thePoint[2]); - - gEngfuncs.pTriAPI->End(); - gEngfuncs.pTriAPI->RenderMode( kRenderNormal ); - } -} - -void AvHHud::DrawPlayerNames() -{ - bool inReadyRoom = false;//(this->GetPlayMode() == PLAYMODE_READYROOM); - bool inTopDownMode = this->GetInTopDownMode(); - - if(inTopDownMode || inReadyRoom /*&& !gEngfuncs.pDemoAPI->IsPlayingback()*/) - { - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - if(theLocalPlayer) - { - int theLocalPlayerIndex = theLocalPlayer->index; - - // Loop through all players - for(int i = 1; i <= kMaxPlayers; i++) - { - if((i != theLocalPlayerIndex) && AvHCUGetIsEntityInPVSAndVisible(i)) - { - cl_entity_s* theCurrentPlayer = gEngfuncs.GetEntityByIndex(i); - bool theDrawEntity = (inReadyRoom || (theCurrentPlayer && theCurrentPlayer->player && (AvHTeamNumber(theCurrentPlayer->curstate.team) == this->GetHUDTeam()) && (i != theLocalPlayerIndex)) ); - if(theDrawEntity) - { - string theEntityName; - bool theIsEnemy; - if(this->GetEntityInfoString(i, theEntityName, theIsEnemy)) - { - vec3_t theEntityOrigin; - VectorCopy(theCurrentPlayer->curstate.origin, theEntityOrigin); - theEntityOrigin.z += AvHCUGetIconHeightForPlayer((AvHUser3)theCurrentPlayer->curstate.iuser3); - - // If they are on screen - Vector theScreenPos; - if(AvHCUWorldToScreen(theEntityOrigin, (float*)&theScreenPos)) - { - // Set color - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, true, false); - - // If selected, draw in different color - if(inTopDownMode) - { - bool theIsSelected = (std::find(this->mSelected.begin(), this->mSelected.end(), i) != this->mSelected.end()); - if(theIsSelected) - { - // Selected color - UnpackRGB(theR, theG, theB, RGB_MARINE_SELECTED); - - if(GetHasUpgrade(theCurrentPlayer->curstate.iuser4, MASK_PARASITED)) - { - string thePrePendString; - LocalizeString(kParasited, thePrePendString); - theEntityName = string(theEntityName + " (" + thePrePendString + ")"); - - // Set color to parasited color - UnpackRGB(theR, theG, theB, RGB_MARINE_PARASITED); - } - } - } - - // Set text color draw in different color - this->mTopDownPlayerNameMessage.SetRGB(theR, theG, theB); - this->mTopDownPlayerNameMessage.SetIgnoreFadeForLifetime(true); - - // Set the message info and draw it - this->mTopDownPlayerNameMessage.SetText(theEntityName); - - // Set position - Vector theNormPos; - float theNormX = theScreenPos.x/ScreenWidth(); - - int theBoxHeight = this->mTopDownPlayerNameMessage.GetScreenHeight(); - float theNormY = (theScreenPos.y - theBoxHeight)/ScreenHeight(); - - if((inTopDownMode && !this->GetIsRegionBlockedByUI(theNormX, theNormY)) || inReadyRoom) - { - this->mTopDownPlayerNameMessage.SetNormalizedScreenX(theNormX); - this->mTopDownPlayerNameMessage.SetNormalizedScreenY(theNormY); - this->mTopDownPlayerNameMessage.SetCentered(true); - this->mTopDownPlayerNameMessage.SetNormalizedMaxWidth(kReticleMaxWidth); - this->mTopDownPlayerNameMessage.Draw(); - } - } - } - } - } - } - } - } -} - -//bool AvHHud::ChopStringOfMaxScreenWidth(int inMaxScreenWidth, string& ioBaseString, string& outChoppedString) -//{ -// // Loop backwards through the string, until we get a string that fits in this screen width -// int theCurrentLength = ioBaseString.length(); -// int theMaxLength = ioBaseString.length(); -// bool theSuccess = false; -// -// while(!theSuccess) -// { -// string theCurrentString = ioBaseString.substr(0, theCurrentLength); -// int theCurrentStringScreenWidth = this->GetHudStringWidth(theCurrentString.c_str()); -// if(theCurrentStringScreenWidth <= inMaxScreenWidth) -// { -// // Look for a word to break the line -// while((theCurrentLength > 0) && !theSuccess) -// { -// char theCurrentChar = ioBaseString[theCurrentLength-1]; -// if((theCurrentChar == ' ') || (theCurrentLength == theMaxLength)) -// { -// outChoppedString = ioBaseString.substr(0, theCurrentLength); -// ioBaseString = ioBaseString.substr(theCurrentLength, ioBaseString.length() - theCurrentLength); -// theSuccess = true; -// break; -// } -// else -// { -// theCurrentLength--; -// } -// } -// } -// else -// { -// theCurrentLength--; -// } -// } -// -// return theSuccess; -//} - -void AvHHud::DrawReticleInfo() -{ - this->mReticleMessage.Draw(); - - // if(this->mReticleInfoText != "") - // { - // const float kMaxWidth = .3f; - // int kMaxScreenWidth = kMaxWidth*ScreenWidth; - // - // StringList theStringList; - // string theHelpString = this->mReticleInfoText; - // - // do - // { - // string theNewString; - // if(ChopStringOfMaxScreenWidth(kMaxScreenWidth, theHelpString, theNewString)) - // { - // theStringList.push_back(theNewString); - // } - // else - // { - // theHelpString = ""; - // } - // } - // while(theHelpString != ""); - // - // // For each line, if the line contains any special markers, move them to their own lines - // - // // Compute max width of all the strings, add some extra for a frame - // int theBoxWidth = 0; - // StringList::iterator theStringListIter; - // for(theStringListIter = theStringList.begin(); theStringListIter != theStringList.end(); theStringListIter++) - // { - // int theCurrentScreenWidth = this->GetHudStringWidth(theStringListIter->c_str()); - // theBoxWidth = max(theBoxWidth, theCurrentScreenWidth); - // } - // int theLineHSpacing = .01f*ScreenWidth; - // theBoxWidth += 2*theLineHSpacing; - // - // // Compute max height needed to contain all the strings, add some extra for a frame - // int theLineVSpacing = .01f*ScreenHeight(); - // int theLineHeight = this->GetHudStringHeight(); - // int theBoxHeight = 2*theLineVSpacing + (theStringList.size()*theLineHeight); - // - // int theFillStartX = this->mReticleInfoScreenX; - // int theFillStartY = this->mReticleInfoScreenY; - // - // theFillStartX -= theBoxWidth/2; - // theFillStartY -= theBoxHeight/2; - // - // // Draw nice border and shaded background - // const float kReticleInfoMaxAlpha = 25; - // float theNormalizedAlpha = this->mReticleInfoColorA/255; - // int theAlphaComponent = theNormalizedAlpha*kReticleInfoMaxAlpha; - // - // FillRGBA(theFillStartX, theFillStartY, theBoxWidth, theBoxHeight, this->mReticleInfoColorR, this->mReticleInfoColorG, this->mReticleInfoColorB, theAlphaComponent); - // vguiSimpleBox(theFillStartX, theFillStartY, theFillStartX + theBoxWidth, theFillStartY + theBoxHeight, this->mReticleInfoColorR, this->mReticleInfoColorG, this->mReticleInfoColorB, theAlphaComponent); - // - // // Now draw each line, non-centered, left-aligned - // int theLineNumber = 0; - // for(theStringListIter = theStringList.begin(); theStringListIter != theStringList.end(); theStringListIter++) - // { - // int theR = this->mReticleInfoColorR; - // int theG = this->mReticleInfoColorG; - // int theB = this->mReticleInfoColorB; - // - // // If the line starts with a marker, draw it in a special color - // //string theDamageMarker(kDamageMarker); - // //if(theStringListIter->substr(0, theDamageMarker.length()) == theDamageMarker) - // //{ - // // // Draw string in yellow - // // theR = theG = 255; - // // theB = 25; - // //} - // - // int theBaseY = theFillStartY + theLineVSpacing + theLineNumber*theLineHeight; - // - // // Draw message (DrawHudStringCentered only centers in x) - // this->DrawHudString(theFillStartX + theLineHSpacing, theBaseY /*- theLineHeight/2*/, ScreenWidth, theStringListIter->c_str(), theR*theNormalizedAlpha, theG*theNormalizedAlpha, theB*theNormalizedAlpha); - // - // theLineNumber++; - // } - // } -} - -void AvHHud::DrawToolTips() -{ - if(!gEngfuncs.pDemoAPI->IsPlayingback()) - { - this->mHelpMessage.Draw(); - - // Draw each one - for(AvHTooltipListType::iterator theIter = this->mTooltips.begin(); theIter != this->mTooltips.end(); theIter++) - { - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, true, false); - theIter->SetRGB(theR, theG, theB); - theIter->Draw(); - } - } -} - -void AvHHud::DrawWorldSprite(int inSpriteHandle, int inRenderMode, vec3_t inWorldPosition, int inFrame, float inWorldSize, float inAlpha) -// tankefugl: added inAlpha -{ - vec3_t theUpperLeft; - vec3_t theLowerRight; - - vec3_t theForward, theRight, theUp; - AngleVectors(v_angles, theForward, theRight, theUp); - - vec3_t theToUpperLeft; - VectorAdd(-theRight, theUp, theToUpperLeft); - VectorNormalize(theToUpperLeft); - - VectorMA(inWorldPosition, inWorldSize, theToUpperLeft, theUpperLeft); - - vec3_t theToLowerRight; - VectorAdd(theRight, -theUp, theToLowerRight); - VectorNormalize(theToLowerRight); - - VectorMA(inWorldPosition, inWorldSize, theToLowerRight, theLowerRight); - - vec3_t theScreenUpperLeft; - vec3_t theScreenLowerRight; - - // World to screen returns true if the world pos is behind the viewer - if(!gEngfuncs.pTriAPI->WorldToScreen((float*)theUpperLeft, (float*)theScreenUpperLeft)) - { - if(!gEngfuncs.pTriAPI->WorldToScreen((float*)theLowerRight, (float*)theScreenLowerRight)) - { - // If the sprite is behind you, push it to the bottom or top of the screen -// cl_entity_t* theLocalPlayer = gEngfuncs.GetLocalPlayer(); -// ASSERT(theLocalPlayer); -// -// vec3_t theDirectionToOrder; -// VectorSubtract(inWorldPosition, theLocalPlayer->origin, theDirectionToOrder); - - // float theDotProduct = DotProduct(theDirectionToOrder, theLocalPlayer->angles); - // if(theDotProduct < 0) - // { - // if(theWorldPos.z < theLocalPlayer->origin.z) - // { - // theY = theScreenHeight - theSpriteHeight - theScreenBorder; - // } - // else - // { - // theY = theScreenBorder; - // } - - // vec3_t theCrossProduct; - // theCrossProduct = CrossProduct(theLocalPlayer->angles, theDirectionToOrder); - // - // // It's to our right - // if(theCrossProduct.z > 0) - // { - // theX = theScreenWidth - theSpriteWidth - theScreenBorder; - // } - // else - // { - // theX = theScreenBorder; - // } - // } - -// float theDistanceToLocation = (float)VectorDistance(inWorldPosition, theLocalPlayer->origin); -// const theMaxDistance = 1500; -// float theEffectiveDistance = min(theDistanceToLocation, theMaxDistance); -// const int theMinColorComponent = 100; -// int theColorComponent = max(theMinColorComponent, 255 - ((theEffectiveDistance/theMaxDistance)*255)); - - //SPR_Set(inSpriteHandle, theColorComponent, theColorComponent, theColorComponent); - ////SPR_DrawHoles((int)0, theX, theY, NULL); - //if(inRenderMode == kRenderNormal) - //{ - // SPR_Draw(inFrame, theX, theY, NULL); - //} - //else if(inRenderMode == kRenderTransAdd) - //{ - // SPR_DrawAdditive(inFrame, theX, theY, NULL); - //} - - float theScreenX = XPROJECT(theScreenUpperLeft.x); - float theScreenY = YPROJECT(theScreenUpperLeft.y); - float theWidth = XPROJECT(theScreenLowerRight.x) - theScreenX; - float theHeight = YPROJECT(theScreenLowerRight.y) - theScreenY; - - //DrawScaledHUDSprite(inSpriteHandle, inRenderMode, 1, theScreenX, theScreenY, theWidth, theHeight, inFrame); - - AvHSpriteSetColor(1, 1, 1, inAlpha); - AvHSpriteSetRenderMode(inRenderMode); - AvHSpriteDraw(inSpriteHandle, inFrame, theScreenX, theScreenY, theScreenX + theWidth, theScreenY + theHeight, 0, 0, 1, 1); - - - - } - } -} - -void AvHHud::DrawOrderIcon(const AvHOrder& inOrder) -{ - if(this->mOrderSprite) - { - int theNumFrames = SPR_Frames(this->mOrderSprite); - int theCurrentFrame = this->GetFrameForOrderType(inOrder.GetOrderType()); - - if((theCurrentFrame >= 0) && (theCurrentFrame < theNumFrames)) - { - vec3_t theWorldPos; - inOrder.GetLocation(theWorldPos); - if ( inOrder.GetOrderType() == ORDERTYPET_ATTACK ) { - theWorldPos[2]+=kAttackOrderZOffset; - } - - // Draw icon above pos, text below - theWorldPos.z += BALANCE_VAR(kOrderIconDrawSize); - - this->DrawWorldSprite(this->mOrderSprite, kRenderTransAdd, theWorldPos, theCurrentFrame, BALANCE_VAR(kOrderIconDrawSize)); - - // If the order is our own order, draw the order indicator around it - if((this->GetHUDPlayMode() == PLAYMODE_PLAYING) && this->GetIsMarine() && !this->GetInTopDownMode()) - { - this->DrawWorldSprite(this->mMarineOrderIndicator, kRenderTransAdd, theWorldPos, 0, BALANCE_VAR(kOrderIconDrawSize)); - //DrawScaledHUDSprite(theSpriteHandle, kRenderNormal, 1, thePosX, thePosY, theWidth, theHeight, theFrame, theStartU, theStartV, theEndU, theEndV); - - } - - vec3_t orderDir; - inOrder.GetLocation(orderDir); - this->GetOrderDirection(orderDir, 2); - } - } -} - -void AvHHud::DrawOrderText(const AvHOrder& inOrder) -{ - int theIndex = (int)(inOrder.GetOrderType()); - - // Now draw text describing the waypoint - string theTitle; - sprintf(theTitle, "Order%d", theIndex); - - string theLocalizedTitle(" "); - LocalizeString(theTitle.c_str(), theLocalizedTitle); - - if((theIndex == ORDERTYPET_BUILD) || (theIndex == ORDERTYPET_GUARD) || (theIndex == ORDERTYPET_GET)) - { - AvHUser3 theUser3 = inOrder.GetTargetUser3Type(); - string theUser3Name; - if(this->GetTranslatedUser3Name(theUser3, theUser3Name)) - { - // "Get %s" -> "Get heavy machine gun" - // "Guard %s -> "Guard soldier" - // "Build %s" -> "Build sentry turret" - string theTitleWithTarget; - sprintf(theTitleWithTarget, theLocalizedTitle.c_str(), theUser3Name.c_str()); - theLocalizedTitle = theTitleWithTarget; - } - } - - vec3_t theOrderLocation; - inOrder.GetLocation(theOrderLocation); - - // Because the commander may not have information about the players heading to this waypoint (outside of his PVS), we - // can't draw a range for the commander - string theRangeDisplayString; - if(!this->GetInTopDownMode()) - { - float theDistanceToWaypoint = VectorDistance(gPredictedPlayerOrigin, theOrderLocation); - int theVisibleDistance = max(1, (int)theDistanceToWaypoint/100); - - string theVisibleUnits; - if(LocalizeString("Range", theVisibleUnits)) - { - sprintf(theRangeDisplayString, theVisibleUnits.c_str(), theVisibleDistance); - } - } - - string theLocationOfOrder; - theLocationOfOrder = this->GetNameOfLocation(theOrderLocation); - - // It's OK if this fails, as only official maps will have these translated - string theTranslatedLocation = theLocationOfOrder; - LocalizeString(theLocationOfOrder.c_str(), theTranslatedLocation); - - // tankefugl: 0000992 - string theFirstLine = theLocalizedTitle; - if(theRangeDisplayString != "") - { - theFirstLine += string(" : ") + theRangeDisplayString; - } - // :tankefugl - - Vector theScreenPos; - if(AvHCUWorldToScreen((float*)theOrderLocation, (float*)&theScreenPos)) - { - float theNormX = theScreenPos.x/ScreenWidth(); - float theNormY = theScreenPos.y/ScreenHeight(); - - if(!this->GetIsRegionBlockedByUI(theNormX, theNormY)) - { - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, false, false); - - string theFirstLine = theLocalizedTitle; - if(theRangeDisplayString != "") - { - theFirstLine += string(" : ") + theRangeDisplayString; - } - - // Draw order (icon above world position, text below it) - int theBaseX = theScreenPos.x; - int theBaseY = theScreenPos.y; - int theStringHeight = this->GetHudStringHeight(); - this->DrawHudStringCentered(theBaseX, theBaseY + theStringHeight, ScreenWidth(), theFirstLine.c_str(), theR, theG, theB); - - // Draw location below it - this->DrawHudStringCentered(theBaseX, theBaseY + 2*theStringHeight, ScreenWidth(), theTranslatedLocation.c_str(), theR, theG, theB); - } - } - // tankefugl: 0000992 - if (this->mDisplayOrderType == 2) - { - // this->mDisplayOrderText1 = "The commander issued an order:"; - this->mDisplayOrderText1 = theFirstLine.c_str(); - this->mDisplayOrderText2 = theTranslatedLocation.c_str(); - } - // :tankefugl -} - -// tankefugl: -#define CENTER_TEXT_LENGTH 10 -#define CENTER_TEXT_FADEOUT 2 -void AvHHud::DrawCenterText() -{ - if ((this->mCenterTextTime > -1) && (this->mTimeOfLastUpdate < this->mCenterTextTime + CENTER_TEXT_LENGTH + CENTER_TEXT_FADEOUT)) - { - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, false, false); - - if (this->mTimeOfLastUpdate > this->mCenterTextTime + CENTER_TEXT_LENGTH) - { - float fraction = this->mTimeOfLastUpdate - (this->mCenterTextTime + CENTER_TEXT_LENGTH); - fraction = 1 - fraction / CENTER_TEXT_FADEOUT; - theR *= fraction; - theG *= fraction; - theB *= fraction; - } - - int posX = 0.5 * ScreenWidth() - this->mFont.GetStringWidth(this->mCenterText.c_str()) / 2; - int posY = 0.4 * ScreenHeight(); - - this->mFont.DrawString(posX, posY, this->mCenterText.c_str(), theR, theG, theB); - } -} -// :tankefugl - -// tankefugl: 0000992 -void AvHHud::SetDisplayOrder(int inOrderType, int inOrderIndex, string inText1, string inText2, string inText3) -{ - this->mDisplayOrderTime = this->mTimeOfLastUpdate; - this->mDisplayOrderType = inOrderType; - this->mDisplayOrderIndex = inOrderIndex; - this->mDisplayOrderText1 = inText1; - this->mDisplayOrderText2 = inText2; - this->mDisplayOrderText3 = inText3; -} - -void AvHHud::DrawDisplayOrder() -{ - const float flashLength = 1.0f; - const float fadeLimit = 6.0f; - const float fadeEnd = 2.0f; - - if ((this->mDisplayOrderType > 0) && (this->mDisplayOrderTime + fadeLimit + fadeEnd) > this->mTimeOfLastUpdate && (this->GetInTopDownMode() == false)) - { - float theFade = 1.0f; - if ((this->mDisplayOrderTime + fadeLimit) < this->mTimeOfLastUpdate) - { - theFade = 1.0f - (this->mTimeOfLastUpdate - (this->mDisplayOrderTime + fadeLimit)) / fadeEnd; - if(theFade < 0.0f) - { - this->mDisplayOrderType = 0; - return; - } - } - - // flash the icon for the first second - if ((this->mDisplayOrderTime + flashLength) > this->mTimeOfLastUpdate) - { - if (((int)((this->mTimeOfLastUpdate - this->mDisplayOrderTime) * 8)) % 2) - { - theFade = 0.0f; - } - } - - // draw the panel -// int sprite = Safe_SPR_Load(kWhiteSprite); - - int r, g, b; - GetPrimaryHudColor(r, g, b, true, false); - - int theStringHeight = this->GetHudStringHeight(); - - float mIconX1 = 0.47f * ScreenWidth(); - float mIconY1 = 0.10f * ScreenHeight(); - float mIconX2 = mIconX1 + 0.06f * ScreenWidth(); - float mIconY2 = mIconY1 + 0.06f * ScreenWidth(); - float mLeftX = mIconX1 - 0.06f * ScreenWidth(); - float mRightX = mIconX2 + 0.06f * ScreenWidth(); - - float mTextX1 = 0.50f * ScreenWidth(); - - AvHSpriteSetRenderMode(kRenderTransAdd); - AvHSpriteSetDrawMode(kSpriteDrawModeFilled); - AvHSpriteSetColor(1, 1, 1, 1 * theFade); - - int theTeamAdd = 0; - if (this->GetIsAlien()) - theTeamAdd = 2; - - if (this->mDisplayOrderDirection == 1) - AvHSpriteDraw(this->mTeammateOrderSprite, TEAMMATE_MARINE_LEFT_ARROW + theTeamAdd, - mLeftX, mIconY1, mIconX1, mIconY2, 0, 0, 1, 1); // Left - else if (this->mDisplayOrderDirection == 2) - AvHSpriteDraw(this->mTeammateOrderSprite, TEAMMATE_MARINE_RIGHT_ARROW + theTeamAdd, - mIconX2, mIconY1, mRightX, mIconY2, 0, 0, 1, 1); // Right - - if (this->mDisplayOrderType == 1) - { - AvHSpriteDraw(this->mTeammateOrderSprite, this->mDisplayOrderIndex + 8, mIconX1, mIconY1, mIconX2, mIconY2, 0, 0, 1, 1); - this->DrawHudStringCentered(mTextX1, mIconY2, ScreenWidth(), this->mDisplayOrderText1.c_str(), r, g, b); - } - else if (this->mDisplayOrderType == 2) - { - AvHSpriteDraw(this->mOrderSprite, this->mDisplayOrderIndex, mIconX1, mIconY1, mIconX2, mIconY2, 0, 0, 1, 1); - this->DrawHudStringCentered(mTextX1, mIconY2, ScreenWidth(), this->mDisplayOrderText1.c_str(), r, g, b); - this->DrawHudStringCentered(mTextX1, mIconY2 + theStringHeight, ScreenWidth(), this->mDisplayOrderText2.c_str(), r, g, b); - } - -// float mTextX1 = mIconX2 + 0.02 * ScreenWidth(); -// this->DrawHudString(mTextX1, mIconY1, ScreenWidth(), this->mDisplayOrderText1.c_str(), r, g, b); -// this->DrawHudString(mTextX1, mIconY1 + theStringHeight, ScreenWidth(), this->mDisplayOrderText2.c_str(), r, g, b); -// this->DrawHudString(mTextX1, mIconY1 + theStringHeight * 2, ScreenWidth(), this->mDisplayOrderText3.c_str(), r, g, b); - } -} -// :tankefugl - -// tankefugl: 0000971 -void AvHHud::GetOrderDirection(vec3_t inTarget, int inOrderType) -{ - if (this->mDisplayOrderType == inOrderType) - { - // find left/right/none direction for the order popup notificator - vec3_t theForward, theRight, theUp, theDir; - AngleVectors(v_angles, theForward, theRight, theUp); - VectorSubtract(inTarget, v_origin, theDir); - theForward[2] = theDir[2] = 0; - VectorNormalize(theForward); - VectorNormalize(theDir); - - this->mDisplayOrderDirection = 0; - // if it is too close to screen center, ignore it - if (DotProduct(theForward, theDir) < 0.9f) - { - // Determine left and right - vec3_t theCrossProd = CrossProduct(theForward, theDir); - if (theCrossProd[2] > 0.0f) - this->mDisplayOrderDirection = 1; // Left - else if (theCrossProd[2] < 0.0f) - this->mDisplayOrderDirection = 2; // Right - } - } -} - -void AvHHud::DrawTeammateOrders() -{ - TeammateOrderListType::iterator toErase = NULL; - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - - const float flashLength = 1.0f; - const float fadeLimit = 6.0f; - const float fadeEnd = 2.0f; - - for(TeammateOrderListType::iterator theIter = this->mTeammateOrder.begin(); theIter != this->mTeammateOrder.end(); theIter++) - { - float lastOrder = 0; - TeammateOrderType theOrder = (*theIter).second; - int theEntIndex = (*theIter).first; - float theFade = 1.0f; - - // remove the order if it has expired - if((theOrder.second + fadeEnd + fadeLimit) < this->mTimeOfLastUpdate) - { - toErase = theIter; - continue; - } - // draw the order fading away - else if((theOrder.second + fadeLimit) < this->mTimeOfLastUpdate) - { - theFade = 1.0f - (this->mTimeOfLastUpdate - (theOrder.second + fadeLimit)) / fadeEnd; - if(theFade < 0.0f) - theFade = 0.0f; - } - // else, draw the order normally - - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(theEntIndex); - if (theEntity && (theEntIndex < MAX_PLAYERS && theEntIndex >= 0) && (theEntity->index != theLocalPlayer->index)) - { - if (AvHTraceLineAgainstWorld(theLocalPlayer->origin, theEntity->origin) == 1.0f) - { - vec3_t theVec; - VectorCopy(theEntity->origin, theVec); - theVec[2] += AvHCUGetIconHeightForPlayer((AvHUser3)theEntity->curstate.iuser3); - this->DrawWorldSprite(this->mTeammateOrderSprite, kRenderTransAdd, theVec, theOrder.first, kHelpIconDrawSize, theFade); - - if (lastOrder < theOrder.second) - { - lastOrder = theOrder.second; - this->GetOrderDirection(theVec, 1); - } - } - } - } - - if (toErase != NULL) - this->mTeammateOrder.erase(toErase); - - // flash target player - if (((this->mCurrentOrderTime + flashLength) > this->mTimeOfLastUpdate) && (this->mCurrentOrderTarget > 0)) - { - if (((int)((this->mTimeOfLastUpdate - (this->mCurrentOrderTime + flashLength)) * 8)) % 2) - { - cl_entity_s* theTargetEntity = gEngfuncs.GetEntityByIndex(this->mCurrentOrderTarget); - - vec3_t theVec; - VectorCopy(theTargetEntity->origin, theVec); - theVec[2] += AvHCUGetIconHeightForPlayer((AvHUser3)theTargetEntity->curstate.iuser3); - this->DrawWorldSprite(this->mTeammateOrderSprite, kRenderTransAdd, theVec, this->mCurrentOrderType, kHelpIconDrawSize, 1.0f); - - } - } - - -} -// :tankefugl - -void AvHHud::DrawOrders() -{ - if(1/*!this->mIsRenderingSelectionView*/) - { - // Draw them blinking for soldiers, but always for commanders - float theFractionalLastUpdate = this->mTimeOfLastUpdate - (int)this->mTimeOfLastUpdate; - if((theFractionalLastUpdate > .25f) || (this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER)) - { - OrderListType theOrders = this->GetOrderList(); - - EntityListType theDrawPlayerList = this->GetDrawPlayerOrders(); - - // Run through the order list type - for(OrderListType::iterator theIter = theOrders.begin(); theIter != theOrders.end(); theIter++) - { - // For each one, if the order is for a player in the theDrawPlayerList, draw it - vec3_t theOrderLocation; - theIter->GetLocation(theOrderLocation); - - if(theIter->GetOrderTargetType() == ORDERTARGETTYPE_TARGET) - { - int theTargetIndex = theIter->GetTargetIndex(); - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(theTargetIndex); - if(theEntity) - { - //voogru: dont follow if they are cloaked, leave the waypoint active so they have a clue where they may be at, the wp should snap back to the baddy - //once they are spotted again. - - if(theEntity->curstate.rendermode != kRenderTransTexture && theEntity->curstate.renderamt > 128) - VectorCopy(theEntity->origin, theOrderLocation); - } - } - - // 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::iterator theSearchIter = std::find(theDrawPlayerList.begin(), theDrawPlayerList.end(), theReceiverPlayer); - if(theSearchIter != theDrawPlayerList.end() && *theSearchIter == theReceiverPlayer) - { - //gEngfuncs.pEfxAPI->R_ParticleLine((float*)theEntity->origin, (float*)theOrderLocation, 0, 255, 0, .05f); - theDrawWaypoint = true; - } - if(theDrawWaypoint) - { - this->DrawOrderIcon(*theIter); - this->DrawOrderText(*theIter); - } - } - } - } -} - -int AvHHud::GetHelpIconFrameFromUser3(AvHUser3 inUser3) -{ - int theFrame = -1; - - switch(inUser3) - { - case AVH_USER3_WELD: - theFrame = 0; - break; - case AVH_USER3_MARINEITEM: - theFrame = 1; - break; - case AVH_USER3_HIVE: - theFrame = 2; - break; - //case AVH_USER3_USEABLE: - // theFrame = 3; - // break; - case AVH_USER3_COMMANDER_STATION: - theFrame = 4; - break; - //case AVH_USER3_BREAKABLE: - // theFrame = 5; - // break; - } - - return theFrame; -} - -HSPRITE AvHHud::GetHelpSprite() const -{ - return this->mHelpSprite; -} - -void AvHHud::DrawHelpIcons() -{ - // Lookup table - - // Only draw if enabled - //if(gEngfuncs.pfnGetCvarFloat(kvAutoHelp)) - //{ - // Iterate through help icons, drawing each one if we have an sprite for it - for(HelpIconListType::iterator theIter = this->mHelpIcons.begin(); theIter != this->mHelpIcons.end(); theIter++) - { - int theUser3 = theIter->second; - int theFrame = GetHelpIconFrameFromUser3(AvHUser3(theUser3)); - - // Lookup sprite to see if it's loaded -// if(this->mHelpSprites[theUser3] == 0) -// { -// string theIconName = string(kHelpIconPrefix) + MakeStringFromInt(theUser3) + ".spr"; -// this->mHelpSprites[theUser3] = Safe_SPR_Load(theIconName.c_str()); -// } -// -// int theSpriteHandle = this->mHelpSprites[theUser3]; -// if(theSpriteHandle > 0) -// { -// // Draw icon at entity center -// this->DrawWorldSprite(theSpriteHandle, kRenderTransAdd, theIter->first, 0); -// } - - if((theFrame >= 0) && this->mHelpSprite) - { - this->DrawWorldSprite(this->mHelpSprite, kRenderTransAdd, theIter->first, theFrame, kHelpIconDrawSize); - } - } - //} -} - -// inDrawMode determines if we're drawing text or sprites -void AvHHud::DrawHUDStructureNotification() -{ - const float kHUDStructureNotificationStartX = .02f; - const float kHUDStructureNotificationStartY = .11f; - const float kHUDStructureNotificationIconWidth = .03f; - const float kHUDStructureNotificationIconHeight = kHUDStructureNotificationIconWidth*1.333f; - const float kHUDStructureNotificationIconHorizontalSpacing = .01f; - const float kHUDStructureNotificationIconVerticalSpacing = kHUDStructureNotificationIconHorizontalSpacing*1.333f; - const float kHUDStructureNotificationMaxTextWidth = .2f; - - // Draw them all in order - if(this->GetIsAlive() && CVAR_GET_FLOAT(kvBuildMessages)) - { - // Get starting coords - float theCurrentX = kHUDStructureNotificationStartX; - float theCurrentY = kHUDStructureNotificationStartY; - - - 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(); - this->DrawTranslatedString(theStartX, theCurrentY*ScreenHeight(), theLocationName.c_str(), false, true); - } - - // Increment coords - theCurrentY += (kHUDStructureNotificationIconHeight + kHUDStructureNotificationIconVerticalSpacing); - } - - } -} - -void AvHHud::DrawInfoLocationText() -{ - string theTimeLeftText; - - // Get drawing color and position - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, false, false); - - // Get position of health and draw to the right of it (must be left-justified) - int theX = 0; - int theY = 0; - if(this->GetInTopDownMode()) - { - const float kLeftInset = .4f; - theX = mViewport[0] + kLeftInset*ScreenWidth(); - theY = mViewport[1] + mViewport[3] - (1-.78f)*ScreenHeight(); - } - // Draw info location text the same for aliens and marines - else if(this->GetHUDUser3() != AVH_USER3_ALIEN_EMBRYO) - { - const float kLeftInset = .08f; - theX = mViewport[0] + kLeftInset*ScreenWidth(); - theY = mViewport[1] + mViewport[3] - (1-.88f)*ScreenHeight(); - } - int theLeftEdge = theX; - - // Draw location text, translation if possible - string theLocalizedLocationText; - if(this->mLocationText != "") - { - LocalizeString(this->mLocationText.c_str(), theLocalizedLocationText); - if(theLocalizedLocationText[0] == '#') - { - theLocalizedLocationText = theLocalizedLocationText.substr(1, theLocalizedLocationText.size()); - } - - // Draw handicap text as well - int theHandicap = (int)this->GetHUDHandicap(); - if(theHandicap < 100) - { - // draw "(handicap 70%)" - string theHandicapString; - if(LocalizeString(kHandicap, theHandicapString)) - { - char theFormattedHandicapString[256]; - sprintf(theFormattedHandicapString, " (%d%% %s)", theHandicap, theHandicapString.c_str()); - theLocalizedLocationText += string(theFormattedHandicapString); - } - } - - // Draw info location - if(theLocalizedLocationText != "") - { - char theCharArray[512]; - sprintf(theCharArray, "%s", theLocalizedLocationText.c_str()); - - this->DrawHudString(theX, theY, ScreenWidth(), theCharArray, theR, theG, theB); - } - } - - // Don't draw time when playing back, it isn't right and not worth breaking backward-compatibility over. - // TODO: Draw time next time demo version changes - if(!gEngfuncs.pDemoAPI->IsPlayingback() && this->GetHUDUser3() != AVH_USER3_ALIEN_EMBRYO) - { - // Whether we draw first line or not, increment for second line below - theY += this->GetHudStringHeight(); - - // Optionally draw time left below it - int theTimeLimitSeconds = (int)this->GetGameTimeLimit(); - int theTimeElapsed = this->GetGameTime(); - int theMinutesElapsed = theTimeElapsed/60; - int theSecondsElapsed = theTimeElapsed%60; - int theTimeLeftSeconds = theTimeLimitSeconds - theTimeElapsed; - int theMinutesLeft = theTimeLeftSeconds/60; - int theSecondsLeft = theTimeLeftSeconds%60; - int theMinutesLimit = theTimeLimitSeconds/60; - int theSecondsLimit = theTimeLimitSeconds%60; - - // If we're in tournament mode or playing Combat, draw the timelimit - bool theTournyMode = (gHUD.GetServerVariableFloat(kvTournamentMode) > 0); - bool theDisplayTimeLimit = theTournyMode || gHUD.GetIsCombatMode(); - - string theGameTimeText; - if(LocalizeString(kGameTime, theGameTimeText)) - { - bool theTimeVisible = true; - - // Flash time when we're almost out of time - if((theMinutesLeft < 1) && this->GetGameStarted() && theDisplayTimeLimit) - { - float theTime = gHUD.GetTimeOfLastUpdate(); - float theTimeFraction = theTime - floor(theTime); - if(theTimeFraction < .5f) - { - theTimeVisible = false; - } - } - - // Game time - 12:43 - char theGameTimeCStr[256]; - sprintf(theGameTimeCStr, " - %02d:%02d", theMinutesElapsed, theSecondsElapsed); - theGameTimeText += string(theGameTimeCStr); - - if(theTimeVisible) - { - this->DrawHudString(theX, theY, ScreenWidth(), theGameTimeText.c_str(), theR, theG, theB); - } - - // Increment X so time limit is drawn properly - theX += this->GetHudStringWidth(theGameTimeText.c_str()); - } - - if(theDisplayTimeLimit) - { - string theTimeLimitString; - if(LocalizeString(kTimeLimit, theTimeLimitString)) - { - // Format: Time limit - 60:00 - char theTimeLimitCStr[256]; - sprintf(theTimeLimitCStr, " %s - %02d:%02d", theTimeLimitString.c_str(), theMinutesLimit, theSecondsLimit); - - string theTimeLimitText = string(theTimeLimitCStr); - - this->DrawHudString(theX, theY, ScreenWidth(), theTimeLimitText.c_str(), theR, theG, theB); - } - - if(gHUD.GetIsCombatMode()) - { - theY += this->GetHudStringHeight(); - - // Draw "attacking" or "defending" - AvHTeamNumber theTeamNumber = this->GetHUDTeam(); - string theDisplayString = ""; - if(theTeamNumber != TEAM_IND) - { - if(theTeamNumber == this->GetCombatAttackingTeamNumber()) - { - LocalizeString(kAttacking, theDisplayString); - } - else - { - LocalizeString(kDefending, theDisplayString); - } - - this->DrawHudString(theLeftEdge, theY, ScreenWidth(), theDisplayString.c_str(), theR, theG, theB); - } - } - } - } -} - -void AvHHud::DrawMouseCursor(int inBaseX, int inBaseY) -{ - if ( g_iVisibleMouse && !(this->GetInTopDownMode() && gEngfuncs.pDemoAPI->IsPlayingback()) ) - { - - HSPRITE theCursorSprite; - int theCursorFrame; - - GetCursor(theCursorSprite, theCursorFrame); - - if (theCursorSprite > 0) - { - float theGammaSlope = this->GetGammaSlope(); - ASSERT(theGammaSlope > 0.0f); - - /* - int theColorComponent = 255/theGammaSlope; - SPR_Set(this->mCursorSprite, theColorComponent, theColorComponent, theColorComponent); - - // Draw the logo at 20 fps - //SPR_DrawAdditive( 0, this->mMouseCursorX - inBaseX, this->mMouseCursorY - inBaseY, NULL ); - const int kMouseHotSpotX = -1; - const int kMouseHotSpotY = -1; - SPR_DrawHoles(this->mCurrentCursorFrame, this->mMouseCursorX - inBaseX - kMouseHotSpotX, this->mMouseCursorY - inBaseY - kMouseHotSpotY, NULL ); - */ - - // Draw the mouse cursor. - - const int kMouseHotSpotX = -1; - const int kMouseHotSpotY = -1; - - AvHSpriteBeginFrame(); - - AvHSpriteEnableVGUI(true); - AvHSpriteSetVGUIOffset(inBaseX, inBaseY); - - float x = this->mMouseCursorX - kMouseHotSpotX; - float y = this->mMouseCursorY - kMouseHotSpotY; - - float w = SPR_Width(theCursorSprite, theCursorFrame); - float h = SPR_Height(theCursorSprite, theCursorFrame); - - AvHSpriteSetRenderMode(kRenderTransAlpha); - AvHSpriteDraw(theCursorSprite, theCursorFrame, x, y, x + w, y + h, 0, 0, 1, 1); - - // Draw the marquee if it's visible. - - if (mSelectionBoxVisible) - { - - int sprite = Safe_SPR_Load(kWhiteSprite); - - int r, g, b; - GetPrimaryHudColor(r, g, b, true, false); - - AvHSpriteSetRenderMode(kRenderTransAdd); - AvHSpriteSetColor(r / 255.0, g / 255.0, b / 255.0, 0.3); - - AvHSpriteSetDrawMode(kSpriteDrawModeFilled); - AvHSpriteDraw(sprite, 0, mSelectionBoxX1 + 1, mSelectionBoxY1 + 1, mSelectionBoxX2 - 1, mSelectionBoxY2 - 1, 0, 0, 1, 1); - - AvHSpriteSetRenderMode(kRenderNormal); - AvHSpriteSetColor(1, 1, 1, 1); - - AvHSpriteSetDrawMode(kSpriteDrawModeBorder); - AvHSpriteDraw(sprite, 0, mSelectionBoxX1, mSelectionBoxY1, mSelectionBoxX2, mSelectionBoxY2, 0, 0, 1, 1); - - } - - AvHSpriteEndFrame(); - - } - } -} - - - -void AvHHud::DrawTopDownBG() -{ - // If we're in top down mode, draw a black background - float theDrawBackgroundHeight = -1;//cl_drawbg->value; - - // Draw the bottom plane at the map's min view height, unless overridden by cl_drawbg (this variable is for for testing purposes only) - if(theDrawBackgroundHeight == -1) - { - if(this->mMapExtents.GetDrawMapBG()) - { - theDrawBackgroundHeight = this->mMapExtents.GetMinViewHeight(); - } - } - - if(theDrawBackgroundHeight != -1) - { - if(this->mBackgroundSprite) - { - // Build three points on plane described by max world dimensions at the draw background height - Vector theUpperLeftWorldPos(-kMaxMapDimension, kMaxMapDimension, theDrawBackgroundHeight); - Vector theUpperRightWorldPos(kMaxMapDimension, kMaxMapDimension, theDrawBackgroundHeight); - Vector theLowerLeftWorldPos(-kMaxMapDimension, -kMaxMapDimension, theDrawBackgroundHeight); - - // Calculate plane info - float thePlaneD; - Vector thePlaneNormal; - CalculatePlaneInfo(theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePlaneD, thePlaneNormal); - - gEngfuncs.pTriAPI->RenderMode( kRenderNormal ); - gEngfuncs.pTriAPI->CullFace( TRI_NONE ); - gEngfuncs.pTriAPI->Brightness( 1 ); - - if(gEngfuncs.pTriAPI->SpriteTexture((struct model_s *)gEngfuncs.GetSpritePointer(this->mBackgroundSprite), 0)) - { - gEngfuncs.pTriAPI->Begin( TRI_TRIANGLE_STRIP ); - - gEngfuncs.pTriAPI->Color4f(1.0f, 1.0f, 1.0f, 1.0f); - - Vector thePoint; - - CalculatePointOnPlane(0, ScreenHeight(), GetViewOrigin(), thePlaneNormal, thePlaneD, thePoint); - gEngfuncs.pTriAPI->TexCoord2f(0, 1); - gEngfuncs.pTriAPI->Vertex3fv((float*)&thePoint); - - CalculatePointOnPlane(0, 0, GetViewOrigin(), thePlaneNormal, thePlaneD, thePoint); - gEngfuncs.pTriAPI->TexCoord2f(0, 0); - gEngfuncs.pTriAPI->Vertex3fv((float*)&thePoint); - - CalculatePointOnPlane(ScreenWidth(), ScreenHeight(), GetViewOrigin(), thePlaneNormal, thePlaneD, thePoint); - gEngfuncs.pTriAPI->TexCoord2f(1, 1); - gEngfuncs.pTriAPI->Vertex3fv((float*)&thePoint); - - CalculatePointOnPlane(ScreenWidth(), 0, GetViewOrigin(), thePlaneNormal, thePlaneD, thePoint); - gEngfuncs.pTriAPI->TexCoord2f(1, 0); - gEngfuncs.pTriAPI->Vertex3fv((float*)&thePoint); - - // Add in some buffer because of perspective -// pVector ver; -// ver.z = theDrawBackgroundHeight; -// -// // Lower left -// gEngfuncs.pTriAPI->TexCoord2f(0,1); -// ver.x = -kMaxMapDimension; -// ver.y = -kMaxMapDimension; -// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); -// -// // Upper left -// gEngfuncs.pTriAPI->TexCoord2f(0,0); -// ver.x = -kMaxMapDimension; -// ver.y = kMaxMapDimension; -// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); -// -// // Upper right -// gEngfuncs.pTriAPI->TexCoord2f(1,0); -// ver.x = kMaxMapDimension; -// ver.y = kMaxMapDimension; -// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); -// -// // Lower right -// gEngfuncs.pTriAPI->TexCoord2f(1,1); -// ver.x = kMaxMapDimension; -// ver.y = -kMaxMapDimension; -// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); - - gEngfuncs.pTriAPI->End(); - } - } - } -} - -void AvHHud::PostModelRender(char* inModelName) -{ - - // Get our view model name - cl_entity_t* theViewEntity = gEngfuncs.GetViewModel(); - if(theViewEntity) - { - // If this is our view model, that means we can now render our own stuff in screen space without z-buffering on top of everything else - if(theViewEntity->model) - { - if(!strcmp(theViewEntity->model->name, inModelName)) - { - this->RenderNoZBuffering(); - } - } - } - -} - -float AvHHud::GetHUDExperience() const -{ - vec3_t theVUser4; - theVUser4.z = 0.0f; - - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - if(theLocalPlayer) - { - theVUser4 = theLocalPlayer->curstate.vuser4; - } - - if(g_iUser1 == OBS_IN_EYE) - { - cl_entity_t* theEnt = gEngfuncs.GetEntityByIndex(g_iUser2); - if(theEnt) - { - theVUser4 = theEnt->curstate.vuser4; - } - } - - float theExperience = theVUser4.z/kNumericNetworkConstant; - - return theExperience; -} - -int AvHHud::GetHUDExperienceLevel() const -{ - float theExperience = this->GetHUDExperience(); - int theLevel = AvHPlayerUpgrade::GetPlayerLevel(theExperience); - return theLevel; -} - -float AvHHud::GetHUDHandicap() const -{ - float theHandicap = 100.0f; - - AvHTeamNumber theTeamNumber = this->GetHUDTeam(); - switch(theTeamNumber) - { - case TEAM_ONE: - theHandicap = this->GetServerVariableFloat(kvTeam1DamagePercent); - break; - - case TEAM_TWO: - theHandicap = this->GetServerVariableFloat(kvTeam2DamagePercent); - break; - case TEAM_THREE: - theHandicap = this->GetServerVariableFloat(kvTeam3DamagePercent); - break; - case TEAM_FOUR: - theHandicap = this->GetServerVariableFloat(kvTeam4DamagePercent); - break; - } - - return theHandicap; -} - -AvHUser3 AvHHud::GetHUDUser3() const -{ - AvHUser3 theUser3 = AVH_USER3_NONE; - - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - if(theLocalPlayer) - { - theUser3 = (AvHUser3)(theLocalPlayer->curstate.iuser3); - } - - if(g_iUser1 == OBS_IN_EYE) - { - cl_entity_t* theEnt = gEngfuncs.GetEntityByIndex(g_iUser2); - if(theEnt) - { - theUser3 = (AvHUser3)(theEnt->curstate.iuser3); - } - } - - return theUser3; -} - -AvHTeamNumber AvHHud::GetHUDTeam() const -{ - AvHTeamNumber theTeamNumber = TEAM_IND; - - cl_entity_s* thePlayer = this->GetVisiblePlayer(); - if(thePlayer) - { - theTeamNumber = AvHTeamNumber(thePlayer->curstate.team); - } - - return theTeamNumber; -} - -int AvHHud::GetHUDUpgrades() const -{ - int theUpgrades = 0; - - cl_entity_s* thePlayer = this->GetVisiblePlayer(); - if(thePlayer) - { - theUpgrades = thePlayer->curstate.iuser4; - } - - return theUpgrades; -} - -int AvHHud::GetHUDMaxArmor() const -{ - int theHUDUpgrades = this->GetHUDUpgrades(); - AvHUser3 theUser3 = this->GetHUDUser3(); - - int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(theHUDUpgrades, theUser3); - - return theMaxArmor; -} - -int AvHHud::GetHUDMaxHealth() const -{ - int theHUDUpgrades = this->GetHUDUpgrades(); - AvHUser3 theUser3 = this->GetHUDUser3(); - int theHUDExperienceLevel = this->GetHUDExperienceLevel(); - - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theHUDUpgrades, theUser3, theHUDExperienceLevel); - - return theMaxHealth; -} - -void AvHHud::GetPrimaryHudColor(int& outR, int& outG, int& outB, bool inIgnoreUpgrades, bool gammaCorrect) const -{ - if(this->GetIsMarine()) - { - UnpackRGB(outR, outG, outB, RGB_MARINE_BLUE); - } - else - { - // HUD turns green when we're frenzying - //if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_PRIMALSCREAM) && !inIgnoreUpgrades) - //{ - // UnpackRGB(outR, outG, outB, RGB_GREENISH); - //} - //else - //{ - UnpackRGB(outR, outG, outB, RGB_YELLOWISH); - //} - } - - if (gammaCorrect) - { - // Take into account current gamma? - float theGammaSlope = this->GetGammaSlope(); - outR /= theGammaSlope; - outG /= theGammaSlope; - outB /= theGammaSlope; - } - -} - -void AvHHud::HandleFog() -{ - float theFogColor[3]; - theFogColor[0] = this->mFogColor.x; - theFogColor[1] = this->mFogColor.y; - theFogColor[2] = this->mFogColor.z; - - gEngfuncs.pTriAPI->Fog(theFogColor, this->mFogStart, this->mFogEnd, this->mFogActive); -} - -void AvHHud::PreRenderFrame() -{ - bool theRenderForTopDown = this->mInTopDownMode /*&& !this->mIsRenderingSelectionView*/; - - if(!theRenderForTopDown) - { - this->HandleFog(); - } - else - { - this->DrawTopDownBG(); - - // Now draw commander HUD scaled - //void CreatePickingRay( int mousex, int mousey, Vector& outVecPickingRay ) -// static int theCommanderHUDSprite = 0; -// if(!theCommanderHUDSprite) -// { -// theCommanderHUDSprite = Safe_SPR_Load("sprites/.spr"); -// } -// -// if(theCommanderHUDSprite) -// { -// gEngfuncs.pTriAPI->RenderMode( kRenderNormal ); -// gEngfuncs.pTriAPI->CullFace( TRI_NONE ); -// gEngfuncs.pTriAPI->Brightness( 1 ); -// -// if(gEngfuncs.pTriAPI->SpriteTexture((struct model_s *)gEngfuncs.GetSpritePointer(theCommanderHUDSprite), 0)) -// { -// gEngfuncs.pTriAPI->Begin( TRI_QUADS ); -// -// gEngfuncs.pTriAPI->Color4f(1.0f, 1.0f, 1.0f, .5f); -// -// pVector ver; -// -// // Lower left -// const float kMaxMapDimension = 4096; -// //ver.z = -kMaxMapDimension; -// ver.z = theDrawBackgroundHeight; -// -// gEngfuncs.pTriAPI->TexCoord2f(0,1); -// ver.x = -kMaxMapDimension; -// ver.y = -kMaxMapDimension; -// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); -// -// // Upper left -// gEngfuncs.pTriAPI->TexCoord2f(0,0); -// ver.x = -kMaxMapDimension; -// ver.y = kMaxMapDimension; -// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); -// -// // Upper right -// gEngfuncs.pTriAPI->TexCoord2f(1,0); -// ver.x = kMaxMapDimension; -// ver.y = kMaxMapDimension; -// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); -// -// // Lower right -// gEngfuncs.pTriAPI->TexCoord2f(1,1); -// ver.x = kMaxMapDimension; -// ver.y = -kMaxMapDimension; -// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); -// -// gEngfuncs.pTriAPI->End(); -// } -// } - } - - AvHScriptManager::Instance()->DrawNormal(); -} - -void AvHHud::PostRenderFrame() -{ - // Restore player angles and view position -// v_origin = gPlayerOrigin; -// v_angles = gPlayerAngles; -} - -void AvHHud::DrawActionButtons() -{ - // Look up AvHActionButtons component - AvHActionButtons* theActionButtons = NULL; - if(this->GetManager().GetVGUIComponentNamed(kActionButtonsComponents, theActionButtons)) - { - int theNumCols = theActionButtons->GetNumCols(); - int theNumRows = theActionButtons->GetNumRows(); - - // Iterate through num cols - for(int theCol = 0; theCol < theNumCols; theCol++) - { - // Iterate through num rows - for(int theRow = 0; theRow < theNumRows; theRow++) - { - // Get current ActionButton - ActionButton* theActionButton = theActionButtons->GetActionButtonAtPos(theCol, theRow); - ASSERT(theActionButton); - - // Get message ID for button - AvHMessageID theMessageID = theActionButton->GetMessageID(); - - // Find the group that it belongs to (20, 30, 40, etc.) - int theMessageNumber = (int)theMessageID - (theMessageID % 10); - -// // Load sprite if not loaded -// bool theSpriteLoaded = (this->mActionButtonSprites[theMessageNumber] > 0); -// if(!theSpriteLoaded) -// { -// // Build sprite name for message ID -// char theMessageNumberString[16]; -// sprintf(theMessageNumberString, "%d", (int)theMessageNumber); -// //string theSpriteName = kTechTreeSpriteDirectory + string("/") + kTechTreeSpritePrefix + string(theMessageIDString) + string(".spr"); -// string theSpriteName = kTechTreeSpriteDirectory + string("/") + kTechTreeSpritePrefix + string(theMessageNumberString) + string("s.spr"); -// int theSpriteHandle = Safe_SPR_Load(theSpriteName.c_str()); -// -// // Sprite handle can be 0, as I don't have sprites for all tech yet -// this->mActionButtonSprites[theMessageNumber] = theSpriteHandle; -// } - - // Get pos and size for component - int thePosX, thePosY; - theActionButton->getPos(thePosX, thePosY); - - int theWidth, theHeight; - theActionButton->getSize(theWidth, theHeight); - - // Set sprite frame depending if button is available, active - bool theTechEnabled = theActionButton->GetTechEnabled(); - bool theMouseOver = theActionButton->GetMouseOver(); - bool theCostMet = theActionButton->GetCostMet(); - - // 0 = default - // 1 = highlighted - // 2 = dark - // 3 = red - int theFrame = 2; - - // If it's enabled, or if it's an icon in the "menu navigation" category, allow it to be highlighted - if(theTechEnabled || (theMessageNumber == 80)) - { - if(theCostMet) - { - if(theMouseOver) - { - theFrame = 1; - } - else - { - theFrame = 0; - } - } - else - { - theFrame = 3; - } - } - - // Also highlight menu category headings for open menus, and for active nodes just pressed - if(gCommanderHandler.GetDisplayMenuMessageID() == theMessageID) - { - theFrame = 1; - } - else - { - AvHMessageID theMessageJustActivated = MESSAGE_NULL; - if(gCommanderHandler.GetAndClearTechNodePressed(theMessageJustActivated, false)) - { - if(theMessageJustActivated == theMessageID) - { - if(theTechEnabled) - { - theFrame = 1; - } - } - } - } - - this->DrawTechTreeSprite(theMessageID, thePosX, thePosY, theWidth, theHeight, theFrame); - } - } - } -} - -int AvHHud::GetTechTreeSprite(AvHMessageID inMessageID) -{ - // Find the group that it belongs to (20, 30, 40, etc.) - int theMessageNumber = (int)inMessageID - (inMessageID % 10); - - // Load sprite if not loaded - bool theSpriteLoaded = (this->mActionButtonSprites[theMessageNumber] > 0); - if(!theSpriteLoaded && (theMessageNumber != 0)) - { - // Build sprite name for message ID - char theMessageNumberString[16]; - sprintf(theMessageNumberString, "%d", (int)theMessageNumber); - //string theSpriteName = kTechTreeSpriteDirectory + string("/") + kTechTreeSpritePrefix + string(theMessageIDString) + string(".spr"); - string theSpriteName = kTechTreeSpriteDirectory + string("/") + kTechTreeSpritePrefix + string(theMessageNumberString) + string("s.spr"); - int theSpriteHandle = Safe_SPR_Load(theSpriteName.c_str()); - - // Sprite handle can be 0, as I don't have sprites for all tech yet - this->mActionButtonSprites[theMessageNumber] = theSpriteHandle; - } - - // Fetch sprite handle - int theSpriteHandle = this->mActionButtonSprites[theMessageNumber]; - - return theSpriteHandle; -} - -void AvHHud::DrawTechTreeSprite(AvHMessageID inMessageID, int inPosX, int inPosY, int inWidth, int inHeight, int inFrame) -{ - if(inMessageID != MESSAGE_NULL) - { - // Check for alien sprites - bool theIsAlienSprite = false; - int theSpriteHandle = 0; - int theRenderMode = kRenderTransAlpha; // kRenderNormal - - switch(inMessageID) - { - case ALIEN_BUILD_DEFENSE_CHAMBER: - theIsAlienSprite = true; - inFrame = 0; - break; - case ALIEN_BUILD_OFFENSE_CHAMBER: - theIsAlienSprite = true; - inFrame = 1; - break; - case ALIEN_BUILD_MOVEMENT_CHAMBER: - theIsAlienSprite = true; - inFrame = 2; - break; - case ALIEN_BUILD_SENSORY_CHAMBER: - theIsAlienSprite = true; - inFrame = 3; - break; - case ALIEN_BUILD_RESOURCES: - theIsAlienSprite = true; - inFrame = 4; - break; - case ALIEN_BUILD_HIVE: - theIsAlienSprite = true; - inFrame = 5; - break; - } - - float theStartU = 0.0f; - float theStartV = 0.0f; - float theEndU = 1.0f; - float theEndV = 1.0f; - - if(theIsAlienSprite) - { - theRenderMode = kRenderTransAlpha; - theSpriteHandle = this->mAlienUIUpgradeCategories; - } - else - { - // Fetch sprite handle - theSpriteHandle = this->GetTechTreeSprite(inMessageID); - - int theIndex = inMessageID % 10; - int theXIndex = theIndex % 4; - int theYIndex = (theIndex / 4); - theStartU = theXIndex*.25f; - theStartV = theYIndex*.25f; - theEndU = (theXIndex+1)*.25f; - theEndV = (theYIndex+1)*.25f; - } - - if(theSpriteHandle > 0) - { - // Draw sprite scaled here - AvHSpriteSetRenderMode(theRenderMode); - AvHSpriteDraw(theSpriteHandle, inFrame, inPosX, inPosY, inPosX + inWidth, inPosY + inHeight, theStartU, theStartV, theEndU, theEndV); - } - } -} - -void AvHHud::DrawHotgroups() -{ - AvHMessageID theHotgroups[kNumHotkeyGroups] = {GROUP_SELECT_1, GROUP_SELECT_2, GROUP_SELECT_3, GROUP_SELECT_4, GROUP_SELECT_5}; - - // Run through list of hotgroups, drawing them if they exist - for(int i = 0; i < kNumHotkeyGroups; i++) - { - EntityListType& theHotgroup = this->mGroups[i]; - - if(theHotgroup.size() > 0) - { - // Get message ID to draw - AvHUser3 theGroupTypeUser3 = this->mGroupTypes[i]; - - // Default is marine face - AvHMessageID theHotgroupIcon = MENU_SOLDIER_FACE; - AvHSHUUser3ToMessageID(theGroupTypeUser3, theHotgroupIcon); - - // Build component name ("PendingImpulseXPanel", where X is the impulse) - AvHMessageID theHotgroupMessageID = theHotgroups[i]; - char theComponentName[256]; - sprintf(theComponentName, kPendingImpulseSpecifier, (int)theHotgroupMessageID); - - AvHTechImpulsePanel* theTechImpulsePanel = NULL; - if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) - { - int thePosX, thePosY; - theTechImpulsePanel->getPos(thePosX, thePosY); - - int theWidth, theHeight; - theTechImpulsePanel->getSize(theWidth, theHeight); - - // Highlight if mouse is in region or if it's our current selection - bool theIsHighlighted = this->GetIsMouseInRegion(thePosX, thePosY, theWidth, theHeight) || (this->mSelected == theHotgroup); - int theFrame = theIsHighlighted ? 1 : 0; - - int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; - if((this->mGroupAlerts[i] == ALERT_UNDER_ATTACK) && (theSecondOfLastUpdate % 2)) - { - // Play blinking red frame when being attacked - theFrame = 3; - } - - this->DrawTechTreeSprite(theHotgroupIcon, thePosX, thePosY, theWidth, theHeight, theFrame); - - theTechImpulsePanel->SetVisualNumber((int)theHotgroup.size()); - } - } - } -} - -// Draw pending requests -void AvHHud::DrawPendingRequests() -{ - // Run through list of requests and delete any entries with - PendingRequestListType::iterator theIterator; - for(theIterator = this->mPendingRequests.begin(); theIterator != this->mPendingRequests.end(); /* nothing */) - { - if(theIterator->second == 0) - { - PendingRequestListType::iterator theSavedIter = theIterator; - theSavedIter++; - this->mPendingRequests.erase(theIterator); - theIterator = theSavedIter; - } - else - { - theIterator++; - } - } - - int theNumRequestsToDraw = (int)this->mPendingRequests.size(); - int theCounter = 0; - for(theIterator = this->mPendingRequests.begin(); theIterator != this->mPendingRequests.end(); theIterator++) - { - // Draw each one above the minimap - AvHMessageID theMessageID = theIterator->first; - int theNumRequests = theIterator->second; - - // Build component name ("PendingImpulseXPanel", where X is the impulse) - char theComponentName[256]; - sprintf(theComponentName, kPendingImpulseSpecifier, (int)theMessageID); - - AvHTechImpulsePanel* theTechImpulsePanel = NULL; - if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) - { - // Get size of panel and draw tech sprite there - int thePosX, thePosY; - theTechImpulsePanel->getPos(thePosX, thePosY); - - int theWidth, theHeight; - theTechImpulsePanel->getSize(theWidth, theHeight); - - AvHMessageID theMessageToDraw = theMessageID; - switch(theMessageID) - { - case COMMANDER_NEXTIDLE: - theMessageToDraw = MENU_SOLDIER_FACE; - break; - case COMMANDER_NEXTAMMO: - theMessageToDraw = BUILD_AMMO; - break; - case COMMANDER_NEXTHEALTH: - theMessageToDraw = BUILD_HEALTH; - break; - } - - int theFrame = this->GetIsMouseInRegion(thePosX, thePosY, theWidth, theHeight) ? 1 : 0; - this->DrawTechTreeSprite(theMessageToDraw, thePosX, thePosY, theWidth, theHeight, theFrame); - - theTechImpulsePanel->SetVisualNumber(theNumRequests); - if(theNumRequests > 0) - { - theTechImpulsePanel->setVisible(true); - } - else - { - theTechImpulsePanel->setVisible(false); - } - - //this->DrawHUDNumber(thePosX + kTileWidth, thePosY + kTileHeight, DHN_2DIGITS, theNumRequests); - } - - // Draw indicator on sprite showing how many requests there are - theCounter++; - } -} - - -// tankefugl: 0000988 -void AvHHud::DrawBuildHealthEffectsForEntity(int inEntityIndex, float inAlpha) -// :tankefugl -{ - // Get entity - int theUser3 = 0; - int theUser4 = 0; - float theFuser1 = 0.0f; - int theEntityTeam = 0; - vec3_t theOrigin; - vec3_t theMins; - vec3_t theMaxs; - bool theContinue = false; - float theHealthPercentage = 0.0f; - double theDistanceToEntity = 0; - - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(inEntityIndex); - if(theEntity) - { - theUser3 = theEntity->curstate.iuser3; - theUser4 = theEntity->curstate.iuser4; - theFuser1 = theEntity->curstate.fuser1; - theEntityTeam = theEntity->curstate.team; - - //theOrigin = theEntity->curstate.origin; - theOrigin = AvHSHUGetRealLocation(theEntity->origin, theEntity->curstate.mins, theEntity->curstate.maxs); - if(theEntity->player) - { - // Subtract half-height to be at feet - float theHeight = theEntity->curstate.maxs[2] - theEntity->curstate.mins[2]; - theOrigin[2] -= theHeight/2.0f; - } - theDistanceToEntity = VectorDistance(gPredictedPlayerOrigin, theOrigin); - - theMins = theEntity->curstate.mins; - theMaxs = theEntity->curstate.maxs; - theHealthPercentage = theEntity->curstate.fuser2/kNormalizationNetworkFactor; - - theContinue = true; - } - - // Get local player - cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); - - bool theDrewBuildInProgress = false; - float theOversizeScalar = 1.0f; - bool theEntityIsPlayer = ((inEntityIndex > 0) && (inEntityIndex <= gEngfuncs.GetMaxClients())); - bool theIsOnOurTeam = (theEntityTeam == (int)this->GetHUDTeam()); - - if(AvHSHUGetDrawRingsForUser3((AvHUser3)theUser3, theOversizeScalar)) - { - if(theContinue && theLocalPlayer) - { - const kDrawEnemyBuildingDistance = 200; - - // Draw effects if we are in top-down mode OR - if( this->GetInTopDownMode() || - - // It's an unfriendly building that's very close OR - (!theEntityIsPlayer && (theDistanceToEntity < kDrawEnemyBuildingDistance)) || - - // It's a friendly entity and we're a builder OR - (theIsOnOurTeam && (this->GetHUDUser3() == AVH_USER3_ALIEN_PLAYER2)) || - - // welder/healing spray is selected - (this->mCurrentWeaponID == 18 || this->mCurrentWeaponID == 27) - - ) - { - - // If they're not on opposite teams - //if((theEntityTeam == theLocalPlayer->curstate.team) || (theEntityTeam == 0)) - //{ - // Get entity build/research state - bool theIsBuilding = false; - bool theIsResearching = false; - float theNormalizedPercentage = 0.0f; - - // Calculate percentage full - AvHSHUGetBuildResearchState(theUser3, theUser4, theFuser1, theIsBuilding, theIsResearching, theNormalizedPercentage); - - bool theDrawHealth = true; - int theSpriteToUse = this->GetIsAlien() ? this->mAlienHealthSprite : this->mMarineHealthSprite; - bool theDrawAsRecyling = (GetHasUpgrade(theUser4, MASK_RECYCLING) && theIsOnOurTeam); - - if((theIsBuilding && (GetHasUpgrade(theUser4, MASK_BUILDABLE))) || theDrawAsRecyling) - { - theSpriteToUse = this->GetIsAlien() ? this->mAlienBuildSprite : this->mMarineBuildSprite; - theDrawHealth = false; - - // Draw progress inverted, as we're "undoing" the structure - if(theDrawAsRecyling) - { - theNormalizedPercentage = (1.0f - theNormalizedPercentage); - } - } - else - { - theNormalizedPercentage = theHealthPercentage; - } - - if(theDrawHealth || (theEntityTeam == theLocalPlayer->curstate.team)) - { - // Draw it - int theNumFrames = SPR_Frames(theSpriteToUse); - ASSERT(theNumFrames > 0); - - int theCurrentFrame; - if ((theHealthPercentage < 1.0f) || theDrawAsRecyling) - { - theCurrentFrame = theNormalizedPercentage * (theNumFrames - 1); - // tankefugl: 0000893 - // quick hack to eliminate 1 red bar shown for dead players - if (theEntity->player) - theCurrentFrame = min(max(0, theCurrentFrame), theNumFrames - 1); - else - theCurrentFrame = min(max(1, theCurrentFrame), theNumFrames - 1); - // :tankefugl - } - else - { - theCurrentFrame = theNumFrames - 1; - } - - // Get real position of entity - vec3_t thePosition; - thePosition = AvHSHUGetRealLocation(theOrigin, theMins, theMaxs); - - float theMaxX = theMaxs[0] - theMins[0]; - float theMaxY = theMaxs[1] - theMins[1]; - - float theRadius = max(theMaxX, theMaxY)*theOversizeScalar; - - // Bring position off ground just a little to prevent z-fighting - thePosition.z += kZFightingOffset; - - //GetHasUpgrade(this->GetHUDUpgrades(), MASK_PARASITED) - - DrawSpriteOnGroundAtPoint(thePosition, theRadius, theSpriteToUse, kRenderTransAdd, theCurrentFrame, inAlpha); - - theDrewBuildInProgress = true; - } - } - } - } -} - -void AvHHud::DrawHUDNumber(int inX, int inY, int inFlags, int inNumber) -{ - // Draw number on HUD, in our HUD color - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, false, false); - - int theGammaSlope = this->GetGammaSlope(); - theR /= theGammaSlope; - theG /= theGammaSlope; - theB /= theGammaSlope; - - this->DrawHudNumber(inX, inY, inFlags, inNumber, theR, theG, theB); -} - -void AvHHud::DrawSelectionAndBuildEffects() -{ -// tankefugl: 0000988 - list theSelectedList; -// :tankefugl - - // Draw build effects - for(SelectionListType::iterator theSelectIter = this->mSelectionEffects.begin(); theSelectIter != this->mSelectionEffects.end(); theSelectIter++) - { - // Draw selection effect around the entity - int theEntIndex = theSelectIter->mEntIndex; - this->DrawBuildHealthEffectsForEntity(theEntIndex); - // tankefugl: 0000988 - theSelectedList.push_back(theEntIndex); - // :tankefugl - } - - bool theDrawBuildingEffect = false; - for(EntityListType::iterator theBuildingIter = this->mBuildingEffectsEntityList.begin(); theBuildingIter != this->mBuildingEffectsEntityList.end(); theBuildingIter++) - { - int theEntIndex = *theBuildingIter; - this->DrawBuildHealthEffectsForEntity(theEntIndex); - // tankefugl: 0000988 - theSelectedList.push_back(theEntIndex); - // :tankefugl - } - - // tankefugl: 0000988 & 0000991 - bool maintanceWeaponSelected = (this->mCurrentWeaponID == 18 || this->mCurrentWeaponID == 27); - bool isCommander = this->GetInTopDownMode(); - if (isCommander || maintanceWeaponSelected) { - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - gEngfuncs.pEventAPI->EV_PushPMStates(); - gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1); - - int localPlayerIndex = gEngfuncs.GetLocalPlayer()->index; - int currentteam = gEngfuncs.GetLocalPlayer()->curstate.team; - int maxclients = gEngfuncs.GetMaxClients(); - - physent_t* theEntity = NULL; - int theNumEnts = pmove->numphysent; - for (int i = 0; i < theNumEnts; i++) - { - theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(i); - if(theEntity) - { - if (localPlayerIndex != theEntity->info) - { - int theEntityIndex = theEntity->info; - list::iterator theSelectedIterator = find(theSelectedList.begin(), theSelectedList.end(), theEntityIndex); - if (theSelectedIterator == theSelectedList.end()) - { - bool theIsPlayer = ((theEntityIndex >= 1) && (theEntityIndex <= maxclients)); - bool theSameTeam = (theEntity->team == currentteam ); - - if (isCommander && (theIsPlayer || theSameTeam)) - { - this->DrawBuildHealthEffectsForEntity(theEntityIndex, 0.2); - } - else if (maintanceWeaponSelected && theSameTeam && !theIsPlayer) - { - if (AvHTraceLineAgainstWorld(gEngfuncs.GetLocalPlayer()->origin, theEntity->origin) == 1.0f) - { - this->DrawBuildHealthEffectsForEntity(theEntityIndex, 0.3); - } - } - } - } - } - } - gEngfuncs.pEventAPI->EV_PopPMStates(); - } - // :tankefugl -} - - -void AvHHud::Render() -{ - - if (!IEngineStudio.IsHardware()) - { - - // Don't show anything in software mode. - - const char* theMessage = "Software mode is not supported by Natural Selection"; - - int theWidth; - int theHeight; - - float gammaScale = 1.0f / GetGammaSlope(); - - gEngfuncs.pfnDrawSetTextColor(0, gammaScale, 0); - gEngfuncs.pfnDrawConsoleStringLen(theMessage, &theWidth, &theHeight); - gEngfuncs.pfnDrawConsoleString((ScreenWidth() - theWidth) / 2, (ScreenHeight() - theHeight) / 2, (char*)theMessage); - - return; - - } - - if (m_Spectator.IsInOverviewMode()) - { - AvHSpriteSetViewport(mSpecialViewport[0], mSpecialViewport[1], - mSpecialViewport[0] + mSpecialViewport[2], mSpecialViewport[1] + mSpecialViewport[3]); - } - - AvHSpriteBeginFrame(); - - if (mSteamUIActive) - { - // Use the old 2D method if we're in the Steam UI. - AvHSpriteEnableVGUI(false); - } - else - { - AvHSpriteEnableVGUI(true); - AvHSpriteSetVGUIOffset(0, 0); - } - -#ifdef DEBUG - if ( CVAR_GET_FLOAT( "hud_hideview" ) != 0 ) - { - - // Black out the screen - - int sprite = Safe_SPR_Load(kWhiteSprite); - AvHSpriteSetColor(0, 0, 0, 1); - AvHSpriteDraw(sprite, 0, 0, 0, ScreenWidth(), ScreenHeight(), 0, 0, 1, 1); - AvHSpriteSetColor(1, 1, 1, 1); - - } -#endif - - // Don't draw the HUD while being digested. - - if (GetIsBeingDigested()) - { - - if (this->mDigestingSprite) - { - AvHSpriteSetColor(1,1,1); - AvHSpriteSetRenderMode(kRenderNormal); - DrawWarpedOverlaySprite(mDigestingSprite, 4, 3, .02, .02, .3, .15); - } - - } - else - { - - if (this->GetHUDPlayMode() == PLAYMODE_PLAYING || - this->GetHUDPlayMode() == PLAYMODE_READYROOM) - { - - if (!mSteamUIActive) - { - this->DrawReticleInfo(); - this->DrawPlayerNames(); - this->DrawToolTips(); - this->DrawCenterText(); - } - - } - - if (this->GetHUDPlayMode() == PLAYMODE_PLAYING) - { - - RenderCommonUI(); - - if (GetIsMarine()) - { - if (GetInTopDownMode()) - { - RenderCommanderUI(); - } - else - { - RenderMarineUI(); - } - } - else if (GetIsAlien()) - { - RenderAlienUI(); - } - } - - } - - AvHSpriteEndFrame(); - AvHSpriteClearViewport(); - -} - -void AvHHud::RenderCommonUI() -{ - if (!mSteamUIActive) - { - - if (gHUD.GetServerVariableFloat("sv_cheats") != 0 && CVAR_GET_FLOAT("cl_showspeed") != 0) - { - - // Draw the speedometer. - - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, true, false); - - extern playermove_s* pmove; - - char buffer[1024]; - - sprintf(buffer, "Speed = %d", (int)Length(pmove->velocity)); - mFont.DrawString(10, 10, buffer, theR, theG, theB); - - float theGroundSpeed = sqrtf(pmove->velocity[0] * pmove->velocity[0] + pmove->velocity[1] * pmove->velocity[1]); - - sprintf(buffer, "Ground speed = %d", (int)theGroundSpeed); - mFont.DrawString(10, 12 + mFont.GetStringHeight(), buffer, theR, theG, theB); - - - } - - DrawInfoLocationText(); - DrawHUDStructureNotification(); - - this->DrawOrders(); - this->DrawHelpIcons(); - // tankefugl: 0000971 - this->DrawTeammateOrders(); - // tankefugl: 0000992 - this->DrawDisplayOrder(); - // :tankefugl - - if (this->GetIsCombatMode()) - { - // If we're in combat mode, draw our rank - const float kTitleYInset = (kPlayerStatusVerticalInset+5*kPlayerStatusStatusSpacing); - - string theTitleText = this->GetRankTitle(true); - - char theCharArray[512]; - sprintf(theCharArray, theTitleText.c_str()); - - // Draw it - - int theX = mViewport[0] + kPlayerStatusHorizontalCenteredInset*(mViewport[2] - mViewport[0]); - int theY = mViewport[1] + mViewport[3] - (1-kTitleYInset)*ScreenHeight(); - - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, true, false); - - this->DrawHudStringCentered(theX, theY, ScreenWidth(), theCharArray, theR, theG, theB); - } - - // If we're parasited, draw a message as such - if (this->GetIsAlive()) - { - string theText; - char theCharArray[512]; - - int theR, theG, theB; - //UnpackRGB(theR, theG, theB, RGB_YELLOWISH); - this->GetPrimaryHudColor(theR, theG, theB, true, false); - - int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; - - // Draw blinking primal scream message - if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_BUFFED) && (theSecondOfLastUpdate % 2)) - { - if(this->GetIsAlien()) - { - LocalizeString(kPrimalScreamed, theText); - } - else if(this->GetIsMarine()) - { - LocalizeString(kCatalysted, theText); - } - - // Draw it - sprintf(theCharArray, "%s", theText.c_str()); - this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), kPlayerStatusVerticalInset*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); - } - - // Draw parasited indicator - if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_PARASITED)) - { - LocalizeString(kParasited, theText); - sprintf(theCharArray, "%s", theText.c_str()); - - // Draw it - this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); - } - - // Draw umbraed indicator - if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_UMBRA)) - { - LocalizeString(kUmbraed, theText); - sprintf(theCharArray, "%s", theText.c_str()); - - // Draw it - this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+2*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); - } - - // Draw blinking "webbed" message - if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_ENSNARED) && (theSecondOfLastUpdate % 2)) - { - LocalizeString(kWebbed, theText); - sprintf(theCharArray, "%s", theText.c_str()); - - // Draw it - this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+3*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); - } - - // Draw "stunned" message (it's so fast, try not blinking it) - if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_PLAYER_STUNNED) /*&& (theSecondOfLastUpdate % 2)*/) - { - LocalizeString(kStunned, theText); - sprintf(theCharArray, "%s", theText.c_str()); - - // Draw it - this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+4*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); - } - - // Draw "being digested" or "digesting" message - if(this->GetIsBeingDigested() && (theSecondOfLastUpdate % 2)) - { - LocalizeString(kDigested, theText); - sprintf(theCharArray, "%s", theText.c_str()); - - // Draw it - this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+5*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); - } - else if(this->GetIsDigesting() && (theSecondOfLastUpdate % 2)) - { - // Look up player in progress entity - if((this->mProgressBarEntityIndex >= 1) && (this->mProgressBarEntityIndex <= gEngfuncs.GetMaxClients())) - { - hud_player_info_t thePlayerInfo; - gEngfuncs.pfnGetPlayerInfo(this->mProgressBarEntityIndex, &thePlayerInfo); - - char* thePlayerName = thePlayerInfo.name; - if(thePlayerName) - { - LocalizeString(kDigestingPlayer, theText); - sprintf(theCharArray, theText.c_str(), thePlayerName); - } - } - - // Draw it - this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+5*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); - } - - // If we are in a squad, draw it on our HUD - if(this->mCurrentSquad > 0) - { - string theSquadIndicator; - if(LocalizeString(kSquadIndicator, theSquadIndicator)) - { - sprintf(theCharArray, theSquadIndicator.c_str(), this->mCurrentSquad); - - // Draw it - this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+6*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); - } - } - } - - // Draw the numerical effects. - - if(this->GetIsAlive()) - { - for(NumericalInfoEffectListType::iterator theIter = this->mNumericalInfoEffects.begin(); theIter != this->mNumericalInfoEffects.end(); theIter++) - { - // Draw it - Vector theScreenPos; - float theWorldPosition[3]; - theIter->GetPosition(theWorldPosition); - if(AvHCUWorldToScreen(theWorldPosition, (float*)&theScreenPos)) - { - float theNormX = theScreenPos.x/ScreenWidth(); - float theNormY = theScreenPos.y/ScreenHeight(); - if(!this->GetInTopDownMode() || !this->GetIsRegionBlockedByUI(theNormX, theNormY)) - { - float theNumber = theIter->GetNumber(); - string thePrependString("+"); - - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, true, false); - if(theNumber < 0) - { - // Red, minus for negatives - theR = 255; - theG = 0; - theB = 0; - - // The "-" is prepended by MakeStringFromInt when negative - thePrependString = string(""); - } - - const char* thePostPendStringToTranslate = NULL; - string thePostPendString; - - switch(theIter->GetEventType()) - { - case kNumericalInfoResourcesEvent: - thePostPendStringToTranslate = kNumericalEventResources; - break; - case kNumericalInfoHealthEvent: - thePostPendStringToTranslate = kNumericalEventHealth; - break; - case kNumericalInfoResourcesDonatedEvent: - thePostPendStringToTranslate = kNumericalEventResourcesDonated; - break; - case kNumericalInfoAmmoEvent: - thePostPendStringToTranslate = kNumericalEventAmmo; - break; - } - - if(thePostPendStringToTranslate) - { - LocalizeString(thePostPendStringToTranslate, thePostPendString); - } - - // Don't draw 0, so we can have other messages - string theNumberString; - if(theNumber != 0) - { - theNumberString = thePrependString + MakeStringFromFloat(theNumber) + " "; - } - - string theDisplayString = theNumberString + thePostPendString; - - char theCharBuffer[512]; - sprintf(theCharBuffer, "%s", theDisplayString.c_str()); - - this->DrawHudStringCentered(theScreenPos.x, theScreenPos.y, ScreenWidth(), theCharBuffer, theR, theG, theB); - } - } - } - } - - } - - // Draw the combat HUD. - - if (this->GetIsCombatMode()) - { - // Now draw our current experience level, so people know how close they are to the next level - // Load alien resource and energy sprites - string theSpriteName = UINameToSprite(kCombatExperienceSprite, ScreenWidth()); - int theExperienceSprite = Safe_SPR_Load(theSpriteName.c_str()); - - if(theExperienceSprite) - { - const float kNormalizedWidth = .1f; - const float kNormalizedYInset = .96f; - const float kNormalizedHeight = .025f; - const int kBaseIndex = this->GetIsMarine() ? 2 : 0; - - // Draw full background - const int kXStart = mViewport[0] + (.5f - kNormalizedWidth/2.0f)*(mViewport[2] - mViewport[0]); - const int kYStart = mViewport[1] + mViewport[3] - (1 - kNormalizedYInset)*ScreenHeight(); - - - AvHSpriteSetColor(1,1,1); - AvHSpriteSetRenderMode(kRenderTransAlpha); - - AvHSpriteDraw(theExperienceSprite, kBaseIndex + 1, kXStart, kYStart, kXStart + kNormalizedWidth*ScreenWidth(), kYStart + kNormalizedHeight*ScreenHeight(), 0, 0, 1, 1); - - // Draw overlay showing amount of experience - float thePercentToNextLevel = AvHPlayerUpgrade::GetPercentToNextLevel(this->GetHUDExperience()); - if((thePercentToNextLevel >= 0.0f) && (thePercentToNextLevel <= 1.0f)) - { - AvHSpriteDraw(theExperienceSprite, kBaseIndex, kXStart, kYStart, kXStart + thePercentToNextLevel*kNormalizedWidth*ScreenWidth(), kYStart + kNormalizedHeight*ScreenHeight(), 0, 0, thePercentToNextLevel, 1.0f); - } - } - - if(this->GetIsAlive(false)) - { - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, false, false); - this->mCombatUpgradeMenu.SetR(theR); - this->mCombatUpgradeMenu.SetG(theG); - this->mCombatUpgradeMenu.SetB(theB); - - this->mCombatUpgradeMenu.Draw(); - } - } - - // Draw top down help - if(this->GetInTopDownMode()) - { - this->mTopDownActionButtonHelp.Draw(); - } - -} - -void AvHHud::RenderMiniMap(int inX, int inY, int inWidth, int inHeight) -{ - - AvHOverviewMap& theOverviewMap = gHUD.GetOverviewMap(); - - AvHOverviewMap::DrawInfo theDrawInfo; - - theDrawInfo.mX = inX; - theDrawInfo.mY = inY; - theDrawInfo.mWidth = inWidth; - theDrawInfo.mHeight = inHeight; - theDrawInfo.mFullScreen = false; - - float worldViewWidth = 800.0f; - - float aspectRatio = (float)(theDrawInfo.mHeight) / theDrawInfo.mWidth; - - float thePlayerX; - float thePlayerY; - - theOverviewMap.GetWorldPosition(thePlayerX, thePlayerY); - - theDrawInfo.mViewWorldMinX = thePlayerX - worldViewWidth; - theDrawInfo.mViewWorldMinY = thePlayerY - worldViewWidth * aspectRatio; - theDrawInfo.mViewWorldMaxX = thePlayerX + worldViewWidth; - theDrawInfo.mViewWorldMaxY = thePlayerY + worldViewWidth * aspectRatio; - - theOverviewMap.Draw(theDrawInfo); - -} - -void AvHHud::RenderMarineUI() -{ - - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, false, false); - - - if (mMarineTopSprite && (this->mMapMode == MAP_MODE_NS)) - { - - float theX; - float theY; - float theWidth = .75f*ScreenWidth(); - float theHeight = ((float)256/768)*ScreenHeight(); - - AvHSpriteSetRenderMode(kRenderTransAdd); - AvHSpriteSetColor(1,1,1); - AvHSpriteDrawTiles(mMarineTopSprite, 3, 1, mViewport[2] - theWidth + mViewport[0], - mViewport[1], mViewport[2] + mViewport[0], mViewport[1] + theHeight, 0, 0, 1, 1); - - // Draw the minimap. - - int theMiniMapX = mViewport[2] - 0.21f * ScreenWidth() + mViewport[0]; - int theMiniMapY = mViewport[1] + 0.013 * ScreenHeight(); - int theMiniMapWidth = 0.200 * ScreenWidth(); - int theMiniMapHeight = 0.202 * ScreenHeight(); - - RenderMiniMap(theMiniMapX, theMiniMapY, theMiniMapWidth, theMiniMapHeight); - - // Draw the resource label. - - theHeight = ScreenHeight() * 0.038; - - std::string theResourceText; - char theResourceBuffer[64]; - - LocalizeString(kMarineResourcePrefix, theResourceText); - _snprintf(theResourceBuffer, 64, "%s %d", theResourceText.c_str(), this->mVisualResources); - - theX = mViewport[0] + 0.3 * ScreenWidth(); - theY = mViewport[1] + 0.007 * ScreenHeight() + (theHeight - GetSmallFont().GetStringHeight()) / 2; - - GetSmallFont().DrawString(theX, theY, theResourceBuffer, theR, theG, theB); - - // Draw the commander status label. - - std::string theCommanderName; - bool theCommanderNameVisible = true; - - if (!GetCommanderLabelText(theCommanderName)) - { - // Blink if there is no commander. - if ((int)this->mTimeOfLastUpdate % 2) - { - theCommanderNameVisible = false; - } - } - - if (theCommanderNameVisible) - { - - theX = mViewport[0] + 0.50 * ScreenWidth(); - theY = mViewport[1] + 0.006 * ScreenHeight() + (theHeight - GetSmallFont().GetStringHeight()) / 2; - - GetSmallFont().DrawString(theX, theY, theCommanderName.c_str(), theR, theG, theB); - - } - - } - - AvHSpriteEnableClippingRect(false); - - // TODO: Draw various marine upgrades (assumes 256x256 sprite, with each upgrade being 64x64, listed right to left, top to bottom, armor upgrades first, - // weapon upgrades next, then motion-tracking, then jetpacks, then exoskeleton) - - // Do we have a jetpack? - if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_UPGRADE_7)) - { - // Draw jetpack energy indicator - if(this->mMarineUIJetpackSprite) - { - float theFactor = 1 - pmove->fuser3/kNormalizationNetworkFactor; - int theFrame = 0; - - string theOutputMessage; - if(LocalizeString(kJetpackEnergyHUDText, theOutputMessage)) - { - - float theX = .9f*ScreenWidth(); - float theY = .8f*ScreenHeight(); - float theWidth = .02f*ScreenWidth(); - float theHeight = .1f*ScreenHeight(); - - AvHSpriteSetRenderMode(kRenderTransAdd); - AvHSpriteSetColor(1,1,1); - - AvHSpriteDraw(mMarineUIJetpackSprite, 0, theX, theY, theX + theWidth, theY + theHeight * theFactor, 0, 0, 1, theFactor); - AvHSpriteDraw(mMarineUIJetpackSprite, 1, theX, theY + theHeight * theFactor, theX + theWidth, theY + theHeight, 0, theFactor, 1, 1); - - } - } - } - - - const int kNumUpgradesToDraw = 4; - AvHUpgradeMask kUpgradeToMaskTable[kNumUpgradesToDraw] = {MASK_UPGRADE_4, MASK_UPGRADE_1, MASK_UPGRADE_8, MASK_UPGRADE_7}; - - const float kAspectRatio = ScreenWidth()/ScreenHeight(); - - const float kNormalizedSpacing = 0.01f; - const int kBaseRightOffset = kNormalizedSpacing*ScreenWidth(); - const int kVerticalUpgradeSpacing = kNormalizedSpacing*kAspectRatio*ScreenHeight(); - int theUpgradeVar = this->GetHUDUpgrades(); - const int kUpgradeFrame = 0; - const float kUpgradeSize = 0.05; - int theUpgradeWidth = kUpgradeSize*ScreenWidth(); - int theUpgradeHeight = kUpgradeSize*kAspectRatio*ScreenHeight(); - - for(int i = 0; i < kNumUpgradesToDraw; i++) - { - AvHUpgradeMask theUpgradeMask = kUpgradeToMaskTable[i]; - - // Draw highest ammo upgrade level - if(theUpgradeMask == MASK_UPGRADE_4) - { - if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_5)) - { - theUpgradeMask = MASK_UPGRADE_5; - - if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_6)) - { - theUpgradeMask = MASK_UPGRADE_6; - } - } - } - - // Draw highest armor level - if(theUpgradeMask == MASK_UPGRADE_1) - { - if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_2)) - { - theUpgradeMask = MASK_UPGRADE_2; - - if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_3)) - { - theUpgradeMask = MASK_UPGRADE_3; - } - } - } - - // Draw heavy armor instead of jetpacks if they have it - if(theUpgradeMask == MASK_UPGRADE_7) - { - if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_13)) - { - theUpgradeMask = MASK_UPGRADE_13; - } - } - - if(GetHasUpgrade(theUpgradeVar, theUpgradeMask)) - { - int theFrame = 0; - - switch(theUpgradeMask) - { - case MASK_UPGRADE_4: - theFrame = 0; - break; - case MASK_UPGRADE_5: - theFrame = 1; - break; - case MASK_UPGRADE_6: - theFrame = 2; - break; - case MASK_UPGRADE_1: - theFrame = 3; - break; - case MASK_UPGRADE_2: - theFrame = 4; - break; - case MASK_UPGRADE_3: - theFrame = 5; - break; - case MASK_UPGRADE_8: - theFrame = 6; - break; - case MASK_UPGRADE_7: - theFrame = 7; - break; - case MASK_UPGRADE_13: - theFrame = 8; - break; - } - - float theStartU = (theFrame % 4)*.25f; - float theStartV = (theFrame / 4)*.333f; - float theEndU = theStartU + .25f; - float theEndV = theStartV + .333f; - - const int kBaseX = ScreenWidth() - .05*ScreenWidth(); - const int kBaseY = .75*ScreenHeight(); - const int kIconWidth = .05*ScreenWidth(); - const int kIconHeight = .05*ScreenHeight(); - - float x1 = kBaseX; - float y1 = kBaseY + i*kIconHeight; - float x2 = x1 + kIconWidth; - float y2 = y1 + kIconHeight; - - AvHSpriteSetRenderMode(kRenderTransAdd); - AvHSpriteEnableClippingRect(false); - AvHSpriteSetColor(1, 1, 1, this->GetGammaSlope()); - AvHSpriteDraw(mMarineUpgradesSprite, 0, x1, y1, x2, y2, theStartU, theStartV, theEndU, theEndV); - - } - } - -} - -void AvHHud::RenderCommanderUI() -{ - - RenderStructureRanges(); - - // Draw command button - if(this->mCommandButtonSprite) - { - // Animate the button unless the mouse is over it (?) - int kNumFrames = 16; // Last frame is highlighted, first frame seems off - const float kFramesPerSecond = 10; - const float kCycleTime = (1.0f/kFramesPerSecond)*kNumFrames; - - float theNumCycles = this->mTimeOfLastUpdate/kCycleTime; - int theFrame = 1 + (theNumCycles - (int)theNumCycles)*kNumFrames; - - const int kStartX = 890; - int theX = (kStartX/1024.0f)*ScreenWidth(); - int theY = (525/768.0f)*ScreenHeight(); - - float theWidth = ((1024 - kStartX)/1024.0f)*ScreenWidth(); - float theHeight = (38/768.0f)*ScreenHeight(); - - float x1 = theX; - float y1 = theY; - float x2 = x1 + theWidth; - float y2 = y1 + theHeight; - - AvHSpriteSetRenderMode(kRenderTransAlpha); - AvHSpriteDraw(mCommandButtonSprite, theFrame, x1, y2, x2, y2, 0, 0, 1, 1); - - } - - // Draw "select all" button - if(this->mSelectAllSprite) - { - AvHTechImpulsePanel* theTechImpulsePanel = NULL; - if(this->GetManager().GetVGUIComponentNamed(kSelectAllImpulsePanel, theTechImpulsePanel)) - { - if(theTechImpulsePanel->isVisible()) - { - int theX, theY; - theTechImpulsePanel->getPos(theX, theY); - - int theWidth, theHeight; - theTechImpulsePanel->getSize(theWidth, theHeight); - - // Draw unhighlighted, highlighted, or active - int theFrame = 0; - - // If mouse is over, use frame 1 - if(this->GetIsMouseInRegion(theX, theY, theWidth, theHeight)) - { - theFrame = 1; - } - - // If all troops selected, use frame 2 - if((this->mSelected.size()) > 0 && (this->mSelected == this->mSelectAllGroup)) - { - theFrame = 2; - } - - AvHSpriteSetRenderMode(kRenderTransAlpha); - AvHSpriteDraw(mSelectAllSprite, theFrame, theX, theY, theX + theWidth, theY + theHeight, 0, 0, 1, 1); - - } - } - } - - // Draw blinking "under attack" indicator - if(this->mCommandStatusSprite) - { - int theFrame = 0; - - int theX = (725/1024.0f)*ScreenWidth(); - int theY = (702/768.0f)*ScreenHeight(); - - float theWidth = (61.5f/1024.0f)*ScreenWidth(); - float theHeight = (66/768.0f)*ScreenHeight(); - - // Blink button when an alert is set! - if(this->mBlinkingAlertType) - { - int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; - if(theSecondOfLastUpdate % 2) - { - theFrame = this->mBlinkingAlertType; - } - } - - AvHSpriteSetRenderMode(kRenderTransAlpha); - AvHSpriteDraw(mCommandStatusSprite, theFrame, theX, theY, theX + theWidth, theY + theHeight, 0, 0, 1, 1); - - } - - if(this->mTopDownTopSprite) - { - float theHeight = ((float)128/768)*ScreenHeight(); - AvHSpriteDrawTiles(mTopDownTopSprite, 4, 1, 0, 0, ScreenWidth(), theHeight, 0, 0, 1, 1); - } - - if(this->mTopDownBottomSprite) - { - // The artwork is three 128s high - float theHeight = ((float)256/768)*ScreenHeight(); - AvHSpriteSetRenderMode(kRenderTransAlpha); - AvHSpriteDrawTiles(mTopDownBottomSprite, 4, 1, 0, ScreenHeight()-theHeight, ScreenWidth(), ScreenHeight(), 0, 0, 1, 1); - } - - // Draw scaled sprites for all ActionButtons - this->DrawActionButtons(); - - // Draw hotgroups - this->DrawHotgroups(); - - // Draw pending requests - this->DrawPendingRequests(); - - // Draw logout button - if(this->mLogoutSprite) - { - int theFrame = 0; - - int theX = .785*ScreenWidth(); - int theY = .013*ScreenHeight(); - - float theWidth = .197f*ScreenWidth(); - float theHeight = .083f*ScreenHeight(); - - // Detect mouseover - if(this->GetIsMouseInRegion(theX, theY, theWidth, theHeight)) - { - theFrame = 1; - } - - AvHSpriteSetRenderMode(kRenderTransAlpha); - AvHSpriteDraw(mLogoutSprite, theFrame, theX, theY, theX + theWidth, theY + theHeight, 0, 0, 1, 1); - - } - -} - -void AvHHud::RenderStructureRanges() -{ - - float theRangeR = 0; - float theRangeG = 1; - float theRangeB = 0; - float theRangeA = 0.2f; - - // Draw range for current ghost building, if applicable - EntityListType theEntities; - IntList theDistanceRequirements; - float theZAdjustment = 0.0f; - bool theSnapToLocation = false; - bool theDrewGhostRegions = false; - - AvHSHUGetBuildRegions(this->mGhostBuilding, theEntities, theDistanceRequirements, theZAdjustment, theSnapToLocation); - - // For each entity to draw - int theDistanceCounter = 0; - for(EntityListType::iterator theRangeIter = theEntities.begin(); theRangeIter != theEntities.end(); theRangeIter++, theDistanceCounter++) - { - //cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(*theRangeIter); - physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theRangeIter); - if(theEntity) - { - vec3_t thePosition; - thePosition = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs); - - //int theSprite = (theEntity->iuser3 == AVH_USER3_SIEGETURRET) ? this->mSiegeTurretSprite : this->mBuildCircleSprite; - int theSprite = this->mBuildCircleSprite; - - int theDistanceRequirement = theDistanceRequirements[theDistanceCounter]; - RenderStructureRange(thePosition, theDistanceRequirement, theSprite, kRenderTransAdd, 0, theRangeR, theRangeG, theRangeB, theRangeA); - - theDrewGhostRegions = true; - } - } - - // Draw selection as well - for(EntityListType::iterator theSelectedIter = this->mSelected.begin(); theSelectedIter != this->mSelected.end(); theSelectedIter++) - { - cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(*theSelectedIter); - //physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theSelectedIter); - if(theEntity) - { - vec3_t thePosition; - thePosition = AvHSHUGetRealLocation(theEntity->curstate.origin, theEntity->curstate.mins, theEntity->curstate.maxs); - - AvHUser3 theUser3 = (AvHUser3)(theEntity->curstate.iuser3); - int theRange = AvHSHUGetDrawRangeForUser3(theUser3); - if(theRange > 0) - { - // Don't draw structures that are recycling and therefore inactive - if(!GetHasUpgrade(theEntity->curstate.iuser4, MASK_RECYCLING)) - { - //int theSprite = (theEntity->curstate.iuser3 == AVH_USER3_SIEGETURRET) ? this->mSiegeTurretSprite : this->mBuildCircleSprite; - int theSprite = this->mBuildCircleSprite; - RenderStructureRange(thePosition, theRange, theSprite, kRenderTransAdd, 0, theRangeR, theRangeG, theRangeB, theRangeA); - } - } - } - } - - // Draw the minimum build radius violations. - - EntityListType theBuildViolations; - AvHSHUGetMinBuildRadiusViolations(mGhostBuilding, mGhostWorldLocation, theBuildViolations); - - - - for (EntityListType::iterator theBuildViolationsIter = theBuildViolations.begin(); theBuildViolationsIter != theBuildViolations.end(); ++theBuildViolationsIter) - { - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - - // Store off the old count - gEngfuncs.pEventAPI->EV_PushPMStates(); - - // Now add in all of the players. - gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); - - physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theBuildViolationsIter); - - gEngfuncs.pEventAPI->EV_PopPMStates(); - - if(theEntity) - { - vec3_t thePosition; - thePosition = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs); - - AvHUser3 theUser3 = (AvHUser3)(theEntity->iuser3); - - vec3_t theMinSize, theMaxSize; - - AvHSHUGetSizeForUser3(theUser3, theMinSize, theMaxSize); - float theMaxRadius2 = max(max(theMinSize.x, theMaxSize.x), max(theMinSize.y, theMaxSize.y)); - - int theSprite = this->mBuildCircleSprite; - // joev: 0000291 - // It's possible to place "on" marines if you're offset a little from center. This code and - // associated changes above and in AvHSharedUtil.cpp is to enforce a build distance around marines, - // in the same way as structures, to prevent this exploit. - float theMinMarineBuildDistance; - if (theUser3 == AVH_USER3_MARINE_PLAYER) { - theMinMarineBuildDistance = BALANCE_VAR(kMinMarinePlayerBuildDistance); - } - else - { - theMinMarineBuildDistance = BALANCE_VAR(kMinMarineBuildDistance); - } - // :joev - RenderStructureRange(thePosition, theMinMarineBuildDistance + theMaxRadius2, theSprite, kRenderTransAdd, 0, 1, 0, 0, 0.3f); - - } - } -} - -void AvHHud::RenderStructureRange(vec3_t inOrigin, int inRadius, HSPRITE inSprite, int inRenderMode, int inFrame, float inR, float inG, float inB, float inAlpha) -{ - - vec3_t w1; - - w1[0] = inOrigin[0] - inRadius; - w1[1] = inOrigin[1] - inRadius; - w1[2] = inOrigin[2]; - - vec3_t s1; - gEngfuncs.pTriAPI->WorldToScreen(w1, s1); - - vec3_t w2; - - w2[0] = inOrigin[0] + inRadius; - w2[1] = inOrigin[1] + inRadius; - w2[2] = inOrigin[2]; - - vec3_t s2; - gEngfuncs.pTriAPI->WorldToScreen(w2, s2); - - float x1 = XPROJECT(s1[0]); - float y1 = YPROJECT(s1[1]); - float x2 = XPROJECT(s2[0]); - float y2 = YPROJECT(s2[1]); - - AvHSpriteSetColor(inR, inG, inB, inAlpha); - AvHSpriteSetRenderMode(inRenderMode); - AvHSpriteDraw(inSprite, inFrame, x1, y1, x2, y2, 0, 0, 1, 1); - AvHSpriteSetColor(1, 1, 1, 1); - -} - -void AvHHud::RenderAlienUI() -{ - - const float kAspectRatio = ScreenWidth()/ScreenHeight(); - - // Draw the embryo overlay if the player is gestating. - - if (GetHUDUser3() == AVH_USER3_ALIEN_EMBRYO) - { - if (mMembraneSprite) - { - AvHSpriteSetRenderMode(kRenderTransAdd); - AvHSpriteSetColor(1,1,1); - DrawWarpedOverlaySprite(mMembraneSprite, 4, 3, .007 * 2, .002 * 2, .15, .3); - } - return; - } - - AvHSpriteSetRenderMode(kRenderTransAlpha); - - int theWidth = kResourceEnergyBarWidth*ScreenWidth(); - int theHeight = kResourceEnergyBarHeight*ScreenHeight(); - - int theX = mViewport[0]; - int theY = mViewport[3] - theHeight + mViewport[1]; - - // Draw resource text label. - - if (!mSteamUIActive) - { - - const float kTextInset = kResourceEnergyBarWidth*.5f; - const int kNumericYOffset = 1.5*this->GetHudStringHeight(); - - // tankefugl: 0000989 - // moved resource label a bit down - //int theResourceLabelX = mViewport[0] + kTextInset*ScreenWidth(); - //int theResourceLabelY = theY - + .05f * ScreenHeight(); - int theResourceLabelX = 10; - int theResourceLabelY = .68f * ScreenHeight(); - // :tankefugl - - if(this->mMapMode == MAP_MODE_NS) - { - string theLocalizedResourceDescription; - if(LocalizeString(kAlienResourcePrefix, theLocalizedResourceDescription)) - { - int theStringWidth = this->GetHudStringWidth(theLocalizedResourceDescription.c_str()); - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, false, false); - this->DrawHudString(theResourceLabelX, theResourceLabelY, ScreenWidth(), theLocalizedResourceDescription.c_str(), theR, theG, theB); - } - - // Draw amount we have - char theBuffer[128]; - sprintf(theBuffer, "(%d/%d)", this->mVisualResources, this->GetMaxAlienResources()); - this->DrawTranslatedString(theResourceLabelX, theResourceLabelY + kNumericYOffset, theBuffer); - } - } - - // Draw energy bar. - - if (mAlienUIEnergySprite) - { - theX = mViewport[2] - theWidth + mViewport[0]; - - float theFactor = 1 - this->mVisualEnergyLevel; - - AvHSpriteSetColor(1,1,1); - AvHSpriteSetRenderMode(kRenderTransTexture); - - AvHSpriteDraw(mAlienUIEnergySprite, 0, theX, theY, theX + theWidth, theY + theHeight * theFactor, 0, 0, 1, theFactor); - AvHSpriteDraw(mAlienUIEnergySprite, 1, theX, theY + theHeight * theFactor, theX + theWidth, theY + theHeight, 0, theFactor, 1, 1); - - } - - // Draw hive indicators. - - if (mHiveInfoSprite) // && (this->mMapMode == MAP_MODE_NS)) // removed check to enable hive with health to be shown in combat - { - - int theHiveIndex = 0; - - for (HiveInfoListType::iterator theIter = this->mHiveInfoList.begin(); theIter != this->mHiveInfoList.end(); theIter++, theHiveIndex++) - { - - // For this hive, figure out which frame to draw - - int theFrame = theIter->mStatus; - - // If under attack, make it blink red - int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; - if(theIter->mUnderAttack && (theSecondOfLastUpdate % 2)) - { - theFrame = kHiveInfoStatusUnderAttack; - } - - int theHiveWidth = kHiveNormScreenWidth*ScreenWidth(); - - // Draw hive - //float theNormX = theHiveWidth; - float theNormY = kHiveNormScreenY + kHiveNormScreenVerticalSpacing*theHiveIndex; - - //DrawScaledHUDSprite(this->mHiveInfoSprite, kRenderTransAlpha, 1, theNormX*ScreenWidth, theNormY*ScreenHeight(), kHiveNormScreenWidth*ScreenWidth, kHiveNormScreenHeight*ScreenHeight(), theFrame); - - float x1 = mViewport[0] + mViewport[2] - theHiveWidth; - float y1 = mViewport[1] + theNormY*ScreenHeight(); - float x2 = x1 + theHiveWidth; - float y2 = y1 + kHiveNormScreenHeight*ScreenHeight(); - - AvHSpriteSetColor(1,1,1); - AvHSpriteSetRenderMode(kRenderTransTexture); - AvHSpriteDraw(mHiveInfoSprite, theFrame, x1, y1, x2, y2, 0, 0, 1, 1); - -// AvHSpriteSetColor(1,1,1); -// AvHSpriteSetRenderMode(kRenderTransTexture); -// AvHSpriteDraw(mHiveHealthSprite, 0, x1, y1, x1+100, y1+100, 0, 0, 1, 1); - - - // use the hive sprite height for the hivehealth sprite height and width - float theHealthSpriteHeight = (float)(kHiveNormScreenHeight) *ScreenHeight(); - float theHealthSpriteWidth = theHealthSpriteHeight; - - AvHSpriteSetColor(1,1,1); - AvHSpriteSetRenderMode(kRenderTransTexture); - - // adjust the position for the sprite -- to the left - float w1 = mViewport[0] + mViewport[2] - (kHiveNormScreenWidth + 0.02f)*ScreenWidth(); - - AvHSpriteDraw(mHiveHealthSprite, 0, w1, y1, w1 + theHealthSpriteWidth, y1 + theHealthSpriteHeight, 0, 0, 1, 1); - - if (theIter->mStatus > 0) { - if (theIter->mHealthPercentage < 100) { - AvHSpriteDraw(mHiveHealthSprite, 0, w1, y1, w1 + theHealthSpriteWidth, y1 + theHealthSpriteHeight, 0, 0, 1, 1); - AvHSpriteDraw(mHiveHealthSprite, 1, - w1, y1 + theHealthSpriteHeight - theHealthSpriteHeight * ((float)(theIter->mHealthPercentage) * 0.92f / 100), - w1 + theHealthSpriteWidth, y1 + theHealthSpriteHeight, - 0, 1 - ((float)(theIter->mHealthPercentage) * 0.92f / 100), - 1, 1); - } else { - AvHSpriteDraw(mHiveHealthSprite, 1, w1, y1, w1 + theHealthSpriteWidth, y1 + theHealthSpriteHeight, 0, 0, 1, 1); - } - } - - - - // Draw technology it's supporting - AvHMessageID theTechnology = (AvHMessageID)theIter->mTechnology; - theFrame = -1; - switch(theTechnology) - { - case ALIEN_BUILD_DEFENSE_CHAMBER: - theFrame = 0; - break; - case ALIEN_BUILD_SENSORY_CHAMBER: - theFrame = 3; - break; - case ALIEN_BUILD_MOVEMENT_CHAMBER: - theFrame = 2; - break; - } - - // Draw it inset - if(theFrame >= 0) - { - - float x1 = mViewport[0] + mViewport[2] - theHiveWidth + (kHiveTechnologyInsetXScalar*kHiveNormScreenWidth)*ScreenWidth(); - float y1 = mViewport[1] + (theNormY + kHiveTechnologyInsetYScalar*kHiveNormScreenHeight)*ScreenHeight(); - float x2 = x1 + kHiveTechnologyInsetWidthScalar*kHiveNormScreenWidth*ScreenWidth(); - float y2 = y1 + kHiveTechnologyInsetHeightScalar*kHiveNormScreenHeight*ScreenHeight(); - - AvHSpriteDraw(mAlienUIUpgradeCategories, theFrame, x1, y1, x2, y2, 0, 0, 1, 1); - - } - - } - - } - - // Draw the upgrades. - - const int kNumberXInset = 2; - const int kNumberYInset = 2; - - const int kBaseYOffset = .1*ScreenHeight(); - - // Now draw alien upgrades - const float kNormalizedSpacing = 0.01f; - const int kBaseRightOffset = kNormalizedSpacing*ScreenWidth(); - const int kVerticalUpgradeSpacing = kNormalizedSpacing*kAspectRatio*ScreenHeight(); - int theUpgradeVar = this->GetHUDUpgrades(); - const int kUpgradeFrame = 0; - const float kUpgradeSize = 0.05; - int theUpgradeWidth = kUpgradeSize*ScreenWidth(); - int theUpgradeHeight = kUpgradeSize*kAspectRatio*ScreenHeight(); - - // In Combat mode, we can have multiple upgrades in each category - int theNumDrawnInCategory[ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE + 1]; - memset(theNumDrawnInCategory, 0, sizeof(int)*(ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE + 1)); - - AvHUpgradeMask theUpgradeMasks[kNumAlienUpgrades] = {MASK_UPGRADE_1, MASK_UPGRADE_2, MASK_UPGRADE_3, MASK_NONE, MASK_NONE, MASK_NONE, MASK_UPGRADE_4, MASK_UPGRADE_5, MASK_UPGRADE_6, MASK_UPGRADE_7, MASK_UPGRADE_8, MASK_UPGRADE_9}; - for(int i = 0; i < kNumAlienUpgrades; i++) - { - AvHUpgradeMask theUpgradeMask = theUpgradeMasks[i]; - AvHAlienUpgradeCategory theCategory = ALIEN_UPGRADE_CATEGORY_INVALID; - AvHGetAlienUpgradeCategoryFromMask(theUpgradeMask, theCategory); - - int theCategoryNumber = (int)theCategory; - //int theRowNumber = i % (kNumAlienUpgrades/kNumAlienUpgradeCategories); - - // Quick hack: treat defense as offense for drawing purposes, because offense has been (temporarily?) removed - int theCategoryNumberOffset = (int)(((theCategory == ALIEN_UPGRADE_CATEGORY_DEFENSE) ? ALIEN_UPGRADE_CATEGORY_OFFENSE : theCategory)); - int theX = ScreenWidth() - theUpgradeWidth - kBaseRightOffset; - - // Inset drawing of multiple upgrades in same category (needed for Combat mode) - theX -= (theNumDrawnInCategory[theCategory]*theUpgradeWidth); - - int theEnergyHeight = kResourceEnergyBarHeight*ScreenHeight(); - - int theY = (3*kHiveNormScreenVerticalSpacing)*ScreenHeight() + kVerticalUpgradeSpacing + theCategoryNumberOffset*(kVerticalUpgradeSpacing + theUpgradeHeight); - - if(GetHasUpgrade(theUpgradeVar, theUpgradeMask)) - { - theNumDrawnInCategory[theCategory]++; - - int theLevelOfUpgrade = AvHGetAlienUpgradeLevel(theUpgradeVar, theUpgradeMask); - for(int theLevel = 0; theLevel < theLevelOfUpgrade; theLevel++) - { - // Draw them slightly overlapping - const float kOffset = .01f; - - float x1 = theX - theLevel*(kOffset*ScreenWidth()); - float y1 = theY - theLevel*(kOffset*ScreenHeight()); - float x2 = x1 + theUpgradeWidth; - float y2 = y1 + theUpgradeHeight; - - AvHSpriteDraw(mAlienUIUpgrades, i, x1, y1, x2, y2, 0, 0, 1, 1); - } - } - else - { - if(this->GetGameStarted() && !this->GetIsCombatMode()) - { - // If upgrade is pending, draw it blinking - for(int i = 0; i < this->mNumUpgradesAvailable; i++) - { - if(this->mCurrentUpgradeCategory[i] == theCategory) - { - int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; - if(theSecondOfLastUpdate % 2) - { - int theFrame = theCategory-1; - - float x1 = theX; - float y1 = theY; - float x2 = x1 + theUpgradeWidth; - float y2 = y1 + theUpgradeHeight; - - AvHSpriteDraw(mAlienUIUpgradeCategories, theFrame, x1, y1, x2, y2, 0, 0, 1, 1); - } - break; - } - } - } - } - } - - - if(this->GetIsAlien() && (this->GetHUDTeam() != TEAM_IND)) - { - bool theIsGestating = (this->GetHUDUser3() == AVH_USER3_ALIEN_EMBRYO); - bool theIsBeingDigested = this->GetIsBeingDigested(); - - this->mSelectedNodeResourceCost = -1; - - // Find the blip nearest our view reticle - int theNearestBlip = -1; - float theDotProductOfClosestBlip = -1; - - // Get view vector - Vector theViewVector; - vec3_t theForward, theRight, theUp; - AngleVectors(v_angles, theForward, theRight, theUp); - VectorNormalize(theForward); - - for(int theBlip = 0; theBlip < this->mFriendlyBlips.mNumBlips; theBlip++) - { - // Get vector to current blip - Vector theVectorToBlip; - VectorSubtract(this->mFriendlyBlips.mBlipPositions[theBlip], gPredictedPlayerOrigin, theVectorToBlip); - VectorNormalize(theVectorToBlip); - - // Dot them - float theDotProduct = DotProduct(theForward, theVectorToBlip); - - // Is dot product closer to 1 then best? - if(theDotProduct > theDotProductOfClosestBlip) - { - // Save new blip and dot product - theNearestBlip = theBlip; - theDotProductOfClosestBlip = theDotProduct; - } - } - - // Draw information about our friendly blips - theBlip = theNearestBlip; - if(theNearestBlip >= 0) - { - ASSERT(theNearestBlip < this->mFriendlyBlips.mNumBlips); - - // Draw location when player under attack - int theBlipStatus = this->mFriendlyBlips.mBlipStatus[theBlip]; - //if((theBlipStatus == kVulnerableFriendlyBlipStatus) || ((theBlipStatus >= kSayingOne) && (theBlipStatus <= kSayingSix))) - //{ - // Get location - string theLocationName = this->GetNameOfLocation(this->mFriendlyBlips.mBlipPositions[theBlip]); - - // Get name of entity - string theBlipName; - - int theBlipInfo = this->mFriendlyBlips.mBlipInfo[theBlip]; - if(theBlipInfo <= kBaseBlipInfoOffset) - { - hud_player_info_t thePlayerInfo; - gEngfuncs.pfnGetPlayerInfo(theBlipInfo, &thePlayerInfo); - if(thePlayerInfo.name) - { - theBlipName = thePlayerInfo.name; - } - } - else - { - AvHUser3 theUser3 = AvHUser3(theBlipInfo - kBaseBlipInfoOffset); - this->GetTranslatedUser3Name(theUser3, theBlipName); - } - - string theBlipStatusText; - char theBlipStatusString[512]; - sprintf(theBlipStatusString, "#%s%d", kBlipStatusPrefix, theBlipStatus); - LocalizeString(theBlipStatusString, theBlipStatusText); - - Vector theScreenPos; - Vector theMessageWorldPos = this->mFriendlyBlips.mBlipPositions[theBlip]; - const float kSpacingScalar = 1.2f; - theMessageWorldPos.z -= (kWorldBlipScale/2.0f)*kSpacingScalar; - - if(AvHCUWorldToScreen(theMessageWorldPos, (float*)&theScreenPos)) - { - if((theBlipName != "") && (theBlipStatusText != "") && (theLocationName != "")) - { - // "MonsieurEvil is under attack" - // "Resource tower is under attack" - char theFirstLine[512]; - sprintf(theFirstLine, "%s %s\n", theBlipName.c_str(), theBlipStatusText.c_str()); - this->DrawTranslatedString(theScreenPos[0], theScreenPos[1], theFirstLine, true, true); - - char theLocationNameCStr[512]; - strcpy(theLocationNameCStr, theLocationName.c_str()); - this->DrawTranslatedString(theScreenPos[0], theScreenPos[1] + .022f*ScreenHeight(), theLocationNameCStr, true, true, true); - } - } - - //} - } - - // Draw hive locations under each hive indicator - if(!theIsGestating && !theIsBeingDigested && (this->mMapMode == MAP_MODE_NS)) - { - int theHiveIndex = 0; - for(HiveInfoListType::iterator theIter = this->mHiveInfoList.begin(); theIter != this->mHiveInfoList.end(); theIter++, theHiveIndex++) - { - vec3_t theHivePosition(theIter->mPosX, theIter->mPosY, theIter->mPosZ); - string theLocationName = this->GetNameOfLocation(theHivePosition); - - int theHiveWidth = kHiveNormScreenWidth*ScreenWidth(); - - int theScreenPosX = mViewport[0] + mViewport[2] - theHiveWidth; - int theScreenPosY = mViewport[1] + (kHiveNormScreenY + theHiveIndex*kHiveNormScreenVerticalSpacing + kHiveNormScreenHeight)*ScreenHeight(); - - string theTranslatedLocationName; - LocalizeString(theLocationName.c_str(), theTranslatedLocationName); - - AvHCUTrimExtraneousLocationText(theTranslatedLocationName); - - int theR, theG, theB; - this->GetPrimaryHudColor(theR, theG, theB, false, false); - - // Draw text in greyscale if hive isn't building or active - if(theIter->mStatus == kHiveInfoStatusUnbuilt) - { - theR = theG = theB = 100; - } - - this->DrawHudStringReverse(mViewport[0] + mViewport[2] - 5, theScreenPosY, ScreenWidth(), (char*)theTranslatedLocationName.c_str(), theR, theG, theB); - } - } - } - - -} - -void AvHHud::DrawWarpedOverlaySprite(int spriteHandle, int numXFrames, int numYFrames, float inWarpXAmount, float inWarpYAmount, float inWarpXSpeed, float inWarpYSpeed) -{ - - float dx = ScreenWidth(); - float dy = ScreenHeight(); - - float theCurrentTime = gHUD.GetTimeOfLastUpdate(); - float theNormXAmount = theCurrentTime*inWarpXSpeed - (int)(theCurrentTime*inWarpXSpeed); // Get fractional part of second - float theNormYAmount = theCurrentTime*inWarpYSpeed - (int)(theCurrentTime*inWarpYSpeed); - float theSinusoidalNormXAmount = cos(theNormXAmount*2.0f*M_PI); - float theSinusoidalNormYAmount = sin(theNormYAmount*2.0f*M_PI); - float theXAmount = theSinusoidalNormXAmount*inWarpXAmount; - float theYAmount = theSinusoidalNormYAmount*inWarpYAmount; - - for (int frameY = 0; frameY < numYFrames; ++frameY) - { - for (int frameX = 0; frameX < numXFrames; ++frameX) - { - - float theWarpXStartAmount = 0; - float theWarpYStartAmount = 0; - float theWarpXEndAmount = 0; - float theWarpYEndAmount = 0; - - int frame = frameX + frameY * numXFrames; - - float pw = SPR_Width(spriteHandle, frame); - float ph = SPR_Height(spriteHandle, frame); - - float fx1 = ((float)(frameX)) / numXFrames; - float fy1 = ((float)(frameY)) / numYFrames; - - float fx2 = ((float)(frameX + 1)) / numXFrames; - float fy2 = ((float)(frameY + 1)) / numYFrames; - - if(frameX > 0) - { - theWarpXStartAmount = theXAmount*ScreenWidth(); - } - if(frameX < numXFrames - 1) - { - theWarpXEndAmount = theXAmount*ScreenWidth(); - } - if(frameY > 0) - { - theWarpYStartAmount = theYAmount*ScreenHeight(); - } - if(frameY < numYFrames - 1) - { - theWarpYEndAmount = theYAmount*ScreenHeight(); - } - - AvHSpriteDraw(spriteHandle, frame, - dx * fx1 + theWarpXStartAmount, - dy * fy1 + theWarpYStartAmount, - dx * fx2 + theWarpXEndAmount, - dy * fy2 + theWarpYEndAmount, - 1 / pw, 1 / ph, 1 - 1 / pw, 1 - 1 / ph); - - } - } - -} - -void AvHHud::RenderNoZBuffering() -{ - - if (mSteamUIActive) - { - // Hack since the HUD doesn't get drawn while the Steam UI is open. - Render(); - } - - // Don't draw this stuff while being digested - if(this->GetIsBeingDigested() || gParticleEditorHandler.GetInEditMode()) - { - return; - } - - // Temporary, so we can make movies without having the UI flit around (known HL playback bug) - if(/*gEngfuncs.pDemoAPI->IsPlayingback() ||*/ gViewPort->IsOptionsMenuVisible()) - { - return; - } - - const int theDistance = 50; - - const float kAspectRatio = ScreenWidth()/ScreenHeight(); - - float m_vRenderOrigin[3]; - pVector m_vUp; - pVector m_vRight; - pVector m_vNormal; - - //vec3_t theViewAngles; - //gEngfuncs.GetViewAngles((float*)theViewAngles) - //gEngfuncs.GetView - - IEngineStudio.GetViewInfo( m_vRenderOrigin, (float*)&m_vUp, (float*)&m_vRight, (float*)&m_vNormal ); - - //float m_fSoftwareXScale, m_fSoftwareYScale; - //IEngineStudio.GetAliasScale( &m_fSoftwareXScale, &m_fSoftwareYScale ); - - //vec3_t theViewHeight; - //gEngfuncs.pEventAPI->EV_LocalPlayerViewheight(theViewHeight); - - // pVector p; - // p.x = m_vRenderOrigin[0] + m_vNormal.x*theDistance; - // p.y = m_vRenderOrigin[1] + m_vNormal.y*theDistance; - // p.z = m_vRenderOrigin[2] + m_vNormal.z*theDistance; - - // pVector p; - // p.x = m_vRenderOrigin[0]; - // p.y = m_vRenderOrigin[1]; - // p.z = m_vRenderOrigin[2]; - // p.x = v_origin[0]; - // p.y = v_origin[1]; - // p.z = v_origin[2]; - - // Allow scripts to perform render - AvHScriptManager::Instance()->DrawNoZBuffering(); - -// if(cl_particleinfo->value) -// { -// // Draw particles here for debugging -// pVector theRealView; -// vec3_t angles, up, right, forward; -// gEngfuncs.pfnAngleVectors(pmove->angles, forward, right, up); -// -// theRealView.x = forward[0]; -// theRealView.y = forward[1]; -// theRealView.z = forward[2]; -// -// AvHParticleSystemManager::Instance()->Draw(theRealView); -// } - - //if(this->mMappingTechSprite && !this->mIsRenderingSelectionView && !this->mGameStarted) - //{ - // float theXPercentage = 256/1024.0f; - // float theYPercentage = 64/768.0f; - // DrawScaledHUDSprite(this->mMappingTechSprite, kRenderTransColor, 1, 0, ScreenHeight/2, theXPercentage*ScreenWidth, theYPercentage*ScreenHeight()); - //} - - // Draw blips, orders, selection and build effects for spectators and demo watchers too - int theUpgradeVar = this->GetHUDUpgrades(); - bool theHasHiveSightUpgrade = true;//GetHasUpgrade(theUpgradeVar, MASK_ALIEN_UPGRADE_11); - - if(this->GetIsAlien()) - { - if(theHasHiveSightUpgrade) - { - // Draw friendly blips - this->mFriendlyBlips.Draw(m_vNormal, kFriendlyBlipStatus); - } - } - - // Draw enemy blips - this->mEnemyBlips.Draw(m_vNormal, kEnemyBlipStatus); - - //this->DrawOrders(true); - this->DrawSelectionAndBuildEffects(); - //this->DrawHelpIcons(); - -} - -void AvHHud::VidInit(void) -{ - UIHud::VidInit(); - - mOverviewMap.VidInit(); - - int theScreenWidth = ScreenWidth(); - string theSpriteName; - - // theSpriteName = UINameToSprite(kEggSprite, theScreenWidth); - // this->mAlienUIEggSprite = Safe_SPR_Load(theSpriteName.c_str()); - - // theSpriteName = UINameToSprite(kHiveSprite, theScreenWidth); - // this->mAlienUIHiveSprite = Safe_SPR_Load(theSpriteName.c_str()); - - int i = 0; - // for(i = 0; i < kNumAlienLifeforms; i++) - // { - // char theBaseName[128]; - // sprintf(theBaseName, "%s%d", kLifeformSprite, i+1); - // string theSpriteName = "sprites/level1_hud.spr";//UINameToSprite(theBaseName, theScreenWidth, true); - // this->mAlienUILifeforms[i] = Safe_SPR_Load(theSpriteName.c_str()); - // } - - char theBaseName[128]; - sprintf(theBaseName, "%s", kAlienUpgradeSprite); - theSpriteName = UINameToSprite(theBaseName, theScreenWidth); - this->mAlienUIUpgrades = Safe_SPR_Load(theSpriteName.c_str()); - - sprintf(theBaseName, "%s", kAlienUpgradeCategory); - theSpriteName = UINameToSprite(theBaseName, theScreenWidth); - this->mAlienUIUpgradeCategories = Safe_SPR_Load(theSpriteName.c_str()); - - // Load jetpack sprite - theSpriteName = UINameToSprite(kJetpackSprite, theScreenWidth); - this->mMarineUIJetpackSprite = Safe_SPR_Load(theSpriteName.c_str()); - - // Load alien energy sprite - theSpriteName = UINameToSprite(kAlienEnergySprite, theScreenWidth); - this->mAlienUIEnergySprite = Safe_SPR_Load(theSpriteName.c_str()); - - // Load background for topdown mode - this->mBackgroundSprite = Safe_SPR_Load(kTopDownBGSprite); - - // Load HUD - this->mTopDownTopSprite = Safe_SPR_Load(kTopDownTopHUDSprite); - this->mTopDownBottomSprite = Safe_SPR_Load(kTopDownBottomHUDSprite); - this->mMarineTopSprite = Safe_SPR_Load(kMarineTopHUDSprite); - - this->mLogoutSprite = Safe_SPR_Load(kLogoutSprite); - this->mCommandButtonSprite = Safe_SPR_Load(kCommandButtonSprite); - this->mCommandStatusSprite = Safe_SPR_Load(kCommandStatusSprite); - this->mSelectAllSprite = Safe_SPR_Load(kSelectAllSprite); - - //this->mTopDownBottomSprite = Safe_SPR_Load("sprites/distorttest.spr"); - //this->mTopDownBottomSprite = Safe_SPR_Load("sprites/ns.spr"); - //this->mTopDownBottomSprite = Safe_SPR_Load("sprites/distorttest.spr"); - - // Load overlays - this->mMembraneSprite = Safe_SPR_Load(kMembraneSprite); - this->mDigestingSprite = Safe_SPR_Load(kDigestingSprite); - - // Load order sprite - theSpriteName = UINameToSprite(kOrdersSprite, theScreenWidth); - this->mOrderSprite = Safe_SPR_Load(theSpriteName.c_str()); - this->mHiveInfoSprite = Safe_SPR_Load(kHiveInfoSprite); - this->mHiveHealthSprite = Safe_SPR_Load(kHiveHealthSprite); - - // Load cursor sprite - this->mMarineCursor = Safe_SPR_Load(kCursorsSprite); - this->mAlienCursor = Safe_SPR_Load(kAlienCursorSprite); - this->mMarineOrderIndicator = Safe_SPR_Load(kMarineOrderSprite); - this->mMarineUpgradesSprite = Safe_SPR_Load(kMarineUpgradesSprite); - - //this->mMappingTechSprite = Safe_SPR_Load("sprites/ns.spr"); - - this->mAlienBuildSprite = Safe_SPR_Load(kAlienBuildSprite); - this->mMarineBuildSprite = Safe_SPR_Load(kMarineBuildSprite); - - this->mAlienHealthSprite = Safe_SPR_Load(kAlienHealthSprite); - this->mMarineHealthSprite = Safe_SPR_Load(kMarineHealthSprite); - - this->mBuildCircleSprite = Safe_SPR_Load(kBuildCircleSprite); - //this->mSiegeTurretSprite = Safe_SPR_Load(kSiegeTurretSprite); - - this->mActionButtonSprites.clear(); - //this->mHelpSprites.clear(); - - string theIconName = string(kHelpIconPrefix) + ".spr"; - this->mHelpSprite = Safe_SPR_Load(theIconName.c_str()); - - // tankefugl: 0000971 - this->mTeammateOrderSprite = Safe_SPR_Load(kTeammateOrderSprite); - // :tankefugl - - this->mEnemyBlips.VidInit(); - this->mFriendlyBlips.VidInit(); -} \ No newline at end of file +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: All graphical HUD operations +// +// $Workfile: AvHHudRender.cpp $ +// $Date: 2002/10/24 21:31:13 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHHudRender.cpp,v $ +// Revision 1.25 2002/10/24 21:31:13 Flayra +// - Removed some parts of energy drawing (unneeded clutter) +// - New marine HUD artwork (mainhud.spr) +// - Reworked upgrade drawing to only draw one of a type of upgrade (this was the last thing for v1.0, so it's a bit yucky) +// +// Revision 1.24 2002/10/18 22:20:14 Flayra +// - Fixed alien mass crash when a player left the server +// +// Revision 1.23 2002/10/16 00:59:33 Flayra +// - Added titles for umbra and primal scream +// - Don't draw building circles for entities on the other team (not even for commander) +// - Tried drawing building circles for ghost buildings, but it was confusing +// +// Revision 1.22 2002/10/03 18:56:10 Flayra +// - Moved alien energy to fuser3 +// - Changed limits for energy and resources +// - Draw order icons centered around order position +// - Don't draw health rings for opposing teams +// +// Revision 1.21 2002/09/25 20:48:37 Flayra +// - Allow more UI to draw when gestating +// - Only draw text for blip closest to reticle +// - Don't draw stuff when dead +// - Changed order blinking +// +// Revision 1.20 2002/09/23 22:19:58 Flayra +// - Added "paralyzed" indicator +// - HUD element repositioning and refactoring +// - Added alien build circles +// - Added visible motion-tracking sprite to marine HUD +// - Removed special siege sprite +// +// Revision 1.19 2002/09/09 19:57:33 Flayra +// - Fixed black edges in D3D +// - Added blinking "webbed" indicator +// - Refactored UI constants +// - Fixed help icons +// - Added hive info indicator +// - Draw more info as spectator +// +// Revision 1.18 2002/08/31 18:01:01 Flayra +// - Work at VALVe +// +// Revision 1.17 2002/08/16 02:38:44 Flayra +// - Draw "webbed" message +// - Draw health for buildings and players +// - Removed old overwatch code +// +// Revision 1.16 2002/08/09 01:03:36 Flayra +// - Started refactoring for moving variable sprite hole drawing into TriAPI +// +// Revision 1.15 2002/08/02 21:56:54 Flayra +// - Added reticle help, new tooltip system and much nicer order drawing! Also changed jetpack label to use font, and refactored some sprite names. +// +// Revision 1.14 2002/07/26 23:05:06 Flayra +// - Generate numerical feedback for damage events +// +// Revision 1.13 2002/07/23 17:09:41 Flayra +// - Add ability to centered, translated strings, visually-smooth energy level, new hive sight info, draw parasited message, draw marine upgrades, new method of drawing alien upgrades (overlapping and offset) +// +// Revision 1.12 2002/07/10 14:42:26 Flayra +// - Removed cl_particleinfo drawing differences +// +// Revision 1.11 2002/07/08 17:07:56 Flayra +// - Started to add display of marine upgrade sprite, fixed bug where building indicators aren't displayed after a map change, info_location drawing changes, primal scream color tweak, removed old hive drawing code +// +// Revision 1.10 2002/07/01 21:36:23 Flayra +// - Added primal scream effect, added building ranges for ghost buildings, removed outdated code, removed mapping build sprite, call vidinit() on hive sight sprites +// +// Revision 1.9 2002/06/25 18:03:09 Flayra +// - Added info_locations, removed old weapon help system, added smooth resource swelling, lots of alien UI usability changes, fixed problem with ghost building +// +// Revision 1.8 2002/06/10 19:57:01 Flayra +// - Allow drawing just a portion of a texture when scaling it, draw team hierarchy for soldiers +// +// Revision 1.7 2002/06/03 16:49:20 Flayra +// - Help sprites moved into one animated sprite +// +// Revision 1.6 2002/05/28 17:49:06 Flayra +// - Hive sight sprite changes +// +// Revision 1.5 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHHud.h" +#include "cl_dll/hud.h" +#include "cl_dll/cl_util.h" +#include "mod/AvHConstants.h" +#include "mod/AvHClientVariables.h" +#include "mod/AvHSpecials.h" +#include "common/cl_entity.h" +#include "mod/AvHTitles.h" +#include "pm_shared/pm_debug.h" +#include "util/MathUtil.h" +#include "common/r_efx.h" +#include "cl_dll/eventscripts.h" +#include "mod/AvHSprites.h" +#include "ui/UIUtil.h" +#include "types.h" +#include +#include "common/com_model.h" +#include "cl_dll/studio_util.h" +#include "cl_dll/r_studioint.h" +#include "mod/AvHMiniMap.h" +#include "mod/AvHActionButtons.h" +#include "util/STLUtil.h" +#include "mod/AvHSharedUtil.h" +#include "common/event_api.h" +#include "mod/AvHScriptManager.h" +#include +#include +#include "mod/AvHParticleSystemManager.h" +#include "mod/AvHTeamHierarchy.h" +#include "mod/AvHClientUtil.h" +#include "mod/AvHTooltip.h" +#include "cl_dll/demo.h" +#include "common/demo_api.h" +#include "mod/AvHHudConstants.h" +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHCommanderModeHandler.h" +#include "common/ref_params.h" +#include "mod/AvHTechImpulsePanel.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHSpriteAPI.h" +#include "mod/AvHParticleEditorHandler.h" +#include "mod/AvHAlienAbilityConstants.h" +#include +#include "common/entity_types.h" + +void IN_GetMousePos( int *mx, int *my ); + +// Externs +extern int g_iVisibleMouse; +extern playermove_t* pmove; +extern engine_studio_api_t IEngineStudio; +extern "C" Vector gPredictedPlayerOrigin; +extern vec3_t v_origin; +extern vec3_t v_angles; + +//extern vec3_t gPlayerOrigin; +//extern vec3_t gPlayerAngles; +extern ref_params_s* pDemoRefParams; +extern vec3_t gPlaybackViewOrigin; +extern AvHCommanderModeHandler gCommanderHandler; +extern AvHParticleEditorHandler gParticleEditorHandler; +float kD3DErrorValue = 0.01f; + + +vec3_t GetViewOrigin() +{ + vec3_t theOrigin = v_origin; + + //cl_entity_t* theViewEntity = gEngfuncs.GetLocalPlayer();//gEngfuncs.GetViewModel(); + //if(theViewEntity && /*pDemoRefParams &&*/ gEngfuncs.pDemoAPI->IsPlayingback()) + //{ + // //theOrigin = pDemoRefParams->vieworg; + // theOrigin = theViewEntity->origin; + //} + + if(gEngfuncs.pDemoAPI->IsPlayingback()) + { + theOrigin = gPlaybackViewOrigin; + } + + return theOrigin; + // return v_origin; +} + +void BuildLerpedPoint(float inXPercentage, float inYPercentage, const Vector& inUpperLeft, const Vector& inUpperRight, const Vector& inLowerLeft, Vector& outPoint) +{ + ASSERT(inXPercentage >= 0.0f); + ASSERT(inXPercentage <= 1.0f); + ASSERT(inYPercentage >= 0.0f); + ASSERT(inYPercentage <= 1.0f); + + Vector theUpperLeftToUpperRight; + VectorSubtract(inUpperRight, inUpperLeft, theUpperLeftToUpperRight); + + Vector theUpperLeftToLowerLeft; + VectorSubtract(inLowerLeft, inUpperLeft, theUpperLeftToLowerLeft); + + Vector theXComp; + VectorScale(theUpperLeftToUpperRight, inXPercentage, theXComp); + + Vector theYComp; + VectorScale(theUpperLeftToLowerLeft, inYPercentage, theYComp); + + outPoint = inUpperLeft + theXComp + theYComp; + + //float theXPercentage = (outPoint.x - inUpperLeft.x)/(theXComp.x + theYComp.x); + //float theYPercentage = (outPoint.y - inUpperLeft.y)/(theXComp.y + theYComp.y); +} + +void CalculatePlaneInfo(const Vector& inUpperLeft, const Vector& inUpperRight, const Vector& inLowerLeft, float& outD, Vector& outPlaneNormal) +{ + // Cross two vectors for plane normal + Vector theUpperRightToUpperLeft; + VectorSubtract(inUpperLeft, inUpperRight, theUpperRightToUpperLeft); + theUpperRightToUpperLeft = theUpperRightToUpperLeft.Normalize(); + + Vector theUpperLeftToLowerLeft; + VectorSubtract(inLowerLeft, inUpperLeft, theUpperLeftToLowerLeft); + theUpperLeftToLowerLeft = theUpperLeftToLowerLeft.Normalize(); + + // Calculate plane normal + CrossProduct(theUpperRightToUpperLeft, theUpperLeftToLowerLeft, outPlaneNormal); + + // Plug in one of the points for D (Ax + By + Cz = -D) + outD = -(outPlaneNormal.x*inUpperLeft.x + outPlaneNormal.y*inUpperLeft.y + outPlaneNormal.z*inUpperLeft.z); +} + +void CalculatePointOnPlane(int inXPos, int inYPos, const Vector& inOrigin, const Vector& inPlaneNormal, float inPlaneD, Vector& outPoint) +{ + Vector theRay; + CreatePickingRay(inXPos, inYPos, theRay); + + // Solve for parametric t + float thePlaneA = inPlaneNormal.x; + float thePlaneB = inPlaneNormal.y; + float thePlaneC = inPlaneNormal.z; + + float theT = -(thePlaneA*inOrigin.x + thePlaneB*inOrigin.y + thePlaneC*inOrigin.z + inPlaneD)/(thePlaneA*theRay.x + thePlaneB*theRay.y + thePlaneC*theRay.z); + + // Now we have t, solve for the endpoint + outPoint.x = inOrigin.x + theT*theRay.x; + outPoint.y = inOrigin.y + theT*theRay.y; + outPoint.z = inOrigin.z + theT*theRay.z; +} + +void ProjectPointFromViewOrigin(int inDistanceToProject, int inScreenX, int inScreenY, Vector& outResult) +{ + Vector theRay; + CreatePickingRay(inScreenX, inScreenY, theRay); + + // project default distance along picking ray + VectorMA(GetViewOrigin(), inDistanceToProject, theRay, outResult); +} + +void AvHHud::DrawTranslatedString(int inX, int inY, const char* inStringToTranslate, bool inCentered, bool inIgnoreUpgrades, bool inTrimExtraInfo, float alpha) +{ + // Translate + string theTranslatedText; + LocalizeString(inStringToTranslate, theTranslatedText); + if(theTranslatedText[0] == '#') + { + theTranslatedText = theTranslatedText.substr(1, theTranslatedText.size()); + } + + if(inTrimExtraInfo) + { + AvHCUTrimExtraneousLocationText(theTranslatedText); + } + + // Draw it + if(theTranslatedText != "") + { + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, inIgnoreUpgrades, false); + + char theCharBuffer[512]; + sprintf(theCharBuffer, "%s", theTranslatedText.c_str()); + + theR *= alpha; + theB *= alpha; + theG *= alpha; + + if(inCentered) + { + this->DrawHudStringCentered(inX, inY, ScreenWidth(), theCharBuffer, theR, theG, theB); + } + else + { + this->DrawHudString(inX, inY, ScreenWidth(), theCharBuffer, theR, theG, theB); + } + } +} + +bool gWarpHUDSprites = false; +float gWarpXAmount = 0.0f; +float gWarpYAmount = 0.0f; +float gWarpXSpeed = 0.0f; +float gWarpYSpeed = 0.0f; + +void SetWarpHUDSprites(bool inMode, float inWarpXAmount = 0.0f, float inWarpYAmount = 0.0f, float inWarpXSpeed = 0.0f, float inWarpYSpeed = 0.0f) +{ + gWarpHUDSprites = inMode; + gWarpXAmount = inWarpXAmount; + gWarpYAmount = inWarpYAmount; + gWarpXSpeed = inWarpXSpeed; + gWarpYSpeed = inWarpYSpeed; +} + +void DrawScaledHUDSprite(int inSpriteHandle, int inMode, int inRowsInSprite = 1, int inX = 0, int inY = 0, int inWidth = ScreenWidth(), int inHeight = ScreenHeight(), int inForceSpriteFrame = -1, float inStartU = 0.0f, float inStartV = 0.0f, float inEndU = 1.0f, float inEndV = 1.0f, float inRotateUVRadians = 0.0f, bool inUVWrapsOverFrames = false) +{ + // Count number of frames + int theNumFrames = SPR_Frames(inSpriteHandle); + + //int theSpriteWidth = SPR_Width(inSpriteHandle, 0); + //int theSpriteHeight = SPR_Height(inSpriteHandle, 0); + //float theAspectRatio = theSpriteWidth/theSpriteHeight; + + // ASSERT that the the number of rows makes sense for the number of frames + ASSERT(theNumFrames > 0); + float theFloatNumCols = (float)theNumFrames/inRowsInSprite; + int theNumCols = (int)theFloatNumCols; + ASSERT(theNumCols == theFloatNumCols); + + //int theNumRows = theNumFrames/theNumCols; + int theNumRows = inRowsInSprite; + + // Allow scaling of one frame, without tiling + if(inForceSpriteFrame != -1) + { + theNumRows = theNumCols = 1; + } + + // Make sure coords are bounded to allowable ranges + + inStartU = min(max(inStartU, kD3DErrorValue), 1.0f - kD3DErrorValue); + inStartV = min(max(inStartV, kD3DErrorValue), 1.0f - kD3DErrorValue); + inEndU = min(max(inEndU, kD3DErrorValue), 1.0f - kD3DErrorValue); + inEndV = min(max(inEndV, kD3DErrorValue), 1.0f - kD3DErrorValue); + + if(inRotateUVRadians != 0.0f) + { + // Rotate all the UV coords + vec3_t theAngles(0.0f, 0.0f, inRotateUVRadians); + float theMatrix[3][4]; + AngleMatrix(theAngles, theMatrix); + + float theRotatedValues[3]; + + float theStartValues[3] = {inStartU, inStartV, 0.0f}; + VectorRotate(theStartValues, theMatrix, theRotatedValues); + inStartU = theRotatedValues[0]; + inStartV = theRotatedValues[1]; + + float theEndValues[3] = {inEndU, inEndV, 0.0f}; + VectorRotate(theEndValues, theMatrix, theRotatedValues); + inEndU = theRotatedValues[0]; + inEndV = theRotatedValues[1]; + } + + // Calculate width and height of each quad + int theQuadScreenWidth = inWidth/theNumCols; + int theQuadScreenHeight = inHeight/theNumRows; + + //Vector thePickRay; + //int theHalfWidth = ScreenWidth/2; + //int theHalfHeight = ScreenHeight/2; + //CreatePickingRay(theHalfWidth, theHalfHeight, thePickRay); + + //char gDebugMessage[256]; + //sprintf(gDebugMessage, "(%d, %d): %f, %f, %f", theHalfWidth, theHalfHeight, thePickRay.x, thePickRay.y, thePickRay.z); + //CenterPrint(gDebugMessage); + + //float theRenderOrigin[3]; + //pVector theUp; + //pVector theRight; + //pVector theNormal; + //IEngineStudio.GetViewInfo(theRenderOrigin, (float*)&theUp, (float*)&theRight, (float*)&theNormal); + + //Demo_WriteVector(TYPE_VIEWANGLES, v_angles); + //Demo_WriteVector(TYPE_VIEWORIGIN, v_origin); + + vec3_t theRenderOrigin; + VectorCopy(GetViewOrigin(), theRenderOrigin); + + vec3_t theForward, theRight, theUp; + AngleVectors(v_angles, theForward, theRight, theUp); + + Vector theRenderOriginVector; + theRenderOriginVector.x = theRenderOrigin[0]; + theRenderOriginVector.y = theRenderOrigin[1]; + theRenderOriginVector.z = theRenderOrigin[2]; + + // This is the smallest value that displays and it still clips with the view model a little + const int kDistanceToProject = 10; + + // create picking ray for upper left of quad + Vector theUpperLeftRay; + //CreatePickingRay(inX, inY, theUpperLeftRay); + CreatePickingRay(0, 0, theUpperLeftRay); + + // create picking ray for lower left of quad + Vector theLowerLeftRay; + //CreatePickingRay(inX, inY+inHeight, theLowerLeftRay); + CreatePickingRay(0, ScreenHeight(), theLowerLeftRay); + + // create picking ray for upper right of quad + Vector theUpperRightRay; + //CreatePickingRay(inX+inWidth, inY, theUpperRightRay); + CreatePickingRay(ScreenWidth(), 0, theUpperRightRay); + + // project default distance along picking ray + Vector theUpperLeftWorldPos; + VectorMA(theRenderOrigin, kDistanceToProject, theUpperLeftRay, theUpperLeftWorldPos); + + Vector theUpperRightWorldPos; + VectorMA(theRenderOrigin, kDistanceToProject, theUpperRightRay, theUpperRightWorldPos); + + Vector theLowerLeftWorldPos; + VectorMA(theRenderOrigin, kDistanceToProject, theLowerLeftRay, theLowerLeftWorldPos); + + // Create formula for plane + float thePlaneD; + Vector thePlaneNormal; + CalculatePlaneInfo(theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePlaneD, thePlaneNormal); + + // loop through screen height + int theCurrentFrame = 0; + + // Allow drawing of just one frame + if(inForceSpriteFrame != -1) + { + theCurrentFrame = inForceSpriteFrame; + } + + for(int i = 0; i < theNumRows; i++) + { + // loop through screen width + for(int j = 0; j < theNumCols; j++) + { + // draw quad + gEngfuncs.pTriAPI->RenderMode(inMode); + gEngfuncs.pTriAPI->CullFace(TRI_NONE); + //gEngfuncs.pTriAPI->Brightness(1); + + if(gEngfuncs.pTriAPI->SpriteTexture((struct model_s*)gEngfuncs.GetSpritePointer(inSpriteHandle), theCurrentFrame)) + { + gEngfuncs.pTriAPI->Begin( TRI_TRIANGLE_STRIP ); + //gEngfuncs.pTriAPI->Color4f(1.0f, 1.0f, 1.0f, 1.0f); + + bool theIsFirstRow = (i == 0); + bool theIsFirstCol = (j == 0); + bool theIsLastRow = (i == (theNumRows-1)); + bool theIsLastCol = (j == (theNumCols-1)); + + float theStartU = inStartU; + float theStartV = inStartV; + float theEndU = inEndU; + float theEndV = inEndV; + + if(inUVWrapsOverFrames) + { + if((theNumCols > 1) && !theIsFirstCol) + { + theStartU = 0.0f; + } + if((theNumRows > 1) && !theIsFirstRow) + { + theStartV = 0.0f; + } + if((theNumCols > 1) && !theIsLastCol) + { + theEndU = 1.0f; + } + if((theNumRows > 1) && !theIsLastRow) + { + theEndV = 1.0f; + } + } + + // Optionally warp XY coords using current time + int theWarpXStartAmount = 0; + int theWarpYStartAmount = 0; + int theWarpXEndAmount = 0; + int theWarpYEndAmount = 0; + + if(gWarpHUDSprites) + { + float theCurrentTime = gHUD.GetTimeOfLastUpdate(); + float theNormXAmount = theCurrentTime*gWarpXSpeed - (int)(theCurrentTime*gWarpXSpeed); // Get fractional part of second + float theNormYAmount = theCurrentTime*gWarpYSpeed - (int)(theCurrentTime*gWarpYSpeed); + float theSinusoidalNormXAmount = cos(theNormXAmount*2.0f*M_PI); + float theSinusoidalNormYAmount = sin(theNormYAmount*2.0f*M_PI); + float theXAmount = theSinusoidalNormXAmount*gWarpXAmount;// - gWarpUAmount/2.0f; + float theYAmount = theSinusoidalNormYAmount*gWarpYAmount;// - gWarpVAmount/2.0f; + + if(!theIsFirstCol) + { + theWarpXStartAmount = theXAmount*ScreenWidth(); + } + if(!theIsLastCol) + { + theWarpXEndAmount = theXAmount*ScreenWidth(); + } + if(!theIsFirstRow) + { + theWarpYStartAmount = theYAmount*ScreenHeight(); + } + if(!theIsLastRow) + { + theWarpYEndAmount = theYAmount*ScreenHeight(); + } + } + + // Compensate for gamma + float theGammaSlope = gHUD.GetGammaSlope(); + float theColorComponent = 1.0f/theGammaSlope; + gEngfuncs.pTriAPI->Color4f(theColorComponent, theColorComponent, theColorComponent, 1.0f); + + Vector thePoint; + + //float theMinXPercentage = (float)j/theNumCols; + //float theMaxXPercentage = (float)(j+1)/theNumCols; + + //float theMinYPercentage = (float)i/theNumRows; + //float theMaxYPercentage = (float)(i+1)/theNumRows; + + int theMinXPos = inX + ((float)j/theNumCols)*inWidth + theWarpXStartAmount; + int theMinYPos = inY + ((float)i/theNumRows)*inHeight + theWarpYStartAmount; + + int theMaxXPos = inX + ((float)(j+1)/theNumCols)*inWidth + theWarpXEndAmount; + int theMaxYPos = inY + ((float)(i+1)/theNumRows)*inHeight + theWarpYEndAmount; + + // Lower left + Vector thePointOne; + //BuildLerpedPoint(theMinXPercentage, theMaxYPercentage, theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePointOne); + CalculatePointOnPlane(theMinXPos, theMaxYPos, theRenderOriginVector, thePlaneNormal, thePlaneD, thePointOne); + float theU = theStartU; + float theV = theEndV; + gEngfuncs.pTriAPI->TexCoord2f(theU, theV);// 0,1 + gEngfuncs.pTriAPI->Vertex3fv((float*)&thePointOne); + + // Upper left + Vector thePointTwo; + //BuildLerpedPoint(theMinXPercentage, theMinYPercentage, theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePointTwo); + CalculatePointOnPlane(theMinXPos, theMinYPos, theRenderOriginVector, thePlaneNormal, thePlaneD, thePointTwo); + theU = theStartU; + theV = theStartV; + //gEngfuncs.pTriAPI->TexCoord2f(inStartU, inStartV); // 0,0 + gEngfuncs.pTriAPI->TexCoord2f(theU, theV);// 0,1 + gEngfuncs.pTriAPI->Vertex3fv((float*)&thePointTwo); + + // Lower right + Vector thePointFour; + //BuildLerpedPoint(theMaxXPercentage, theMaxYPercentage, theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePointFour); + CalculatePointOnPlane(theMaxXPos, theMaxYPos, theRenderOriginVector, thePlaneNormal, thePlaneD, thePointFour); + theU = theEndU; + theV = theEndV; + //gEngfuncs.pTriAPI->TexCoord2f(inEndU, inEndV);// 1,1 + gEngfuncs.pTriAPI->TexCoord2f(theU, theV);// 0,1 + gEngfuncs.pTriAPI->Vertex3fv((float*)&thePointFour); + + // Upper right + Vector thePointThree; + //BuildLerpedPoint(theMaxXPercentage, theMinYPercentage, theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePointThree); + CalculatePointOnPlane(theMaxXPos, theMinYPos, theRenderOriginVector, thePlaneNormal, thePlaneD, thePointThree); + theU = theEndU; + theV = theStartV; + //gEngfuncs.pTriAPI->TexCoord2f(inEndU, inStartV); // 1,0 + gEngfuncs.pTriAPI->TexCoord2f(theU, theV);// 0,1 + gEngfuncs.pTriAPI->Vertex3fv((float*)&thePointThree); + + gEngfuncs.pTriAPI->End(); + } + + // Increment frame + theCurrentFrame++; + + // Allow drawing of just one frame + if(inForceSpriteFrame != -1) + { + theCurrentFrame = inForceSpriteFrame; + } + + // increment current x and y + //theCurrentScreenX += theQuadScreenWidth; + } + //theCurrentScreenX = 0; + //theCurrentScreenY += theQuadScreenHeight; + } +} + +void DrawVariableScaledHUDSprite(float inFactor, int inSpriteHandle, int inMode, int inX, int inY, int inWidth, int inHeight) +{ + // Draw as two scaled sprites, one for the level and one for the "empty" level + // Assumes that sprite has two frames, with the empty level being frame 0 and the full frame being frame 1 + int theWidth = inWidth; + float theStartU = 0.0f; + float theEndU = 1.0f; + + int theHeight = inFactor*inHeight; + float theStartV = 1.0f - inFactor; + float theEndV = 1.0f; + int theX = inX; + int theY = inY + inHeight - theHeight; + DrawScaledHUDSprite(inSpriteHandle, inMode, 1, theX, theY, theWidth, theHeight, 1, theStartU, theStartV, theEndU, theEndV); + + // Draw background + theHeight = (1.0f - inFactor)*inHeight; + theY = inY; + theStartV = 0.0f; + theEndV = 1.0f - inFactor; + DrawScaledHUDSprite(inSpriteHandle, inMode, 1, theX, theY, theWidth, theHeight, 0, theStartU, theStartV, theEndU, theEndV); +} + +void DrawSpriteOnGroundAtPoint(vec3_t inOrigin, int inRadius, HSPRITE inSprite, int inRenderMode = kRenderNormal, int inFrame = 0, float inAlpha = 1.0f) +{ + if(gEngfuncs.pTriAPI->SpriteTexture((struct model_s *)gEngfuncs.GetSpritePointer(inSprite), inFrame)) + { + gEngfuncs.pTriAPI->CullFace(TRI_NONE); + + gEngfuncs.pTriAPI->RenderMode(inRenderMode); + + //gEngfuncs.pTriAPI->RenderMode( kRenderTransTexture ); + //gEngfuncs.pTriAPI->Color4f(inR, inG, inB, inA); + + gEngfuncs.pTriAPI->Begin(TRI_TRIANGLE_STRIP); + + // Draw one quad + vec3_t thePoint = inOrigin; + + float theGammaSlope = gHUD.GetGammaSlope(); + ASSERT(theGammaSlope > 0.0f); + float theColorComponent = 1.0f/theGammaSlope; + + gEngfuncs.pTriAPI->Color4f(theColorComponent, theColorComponent, theColorComponent, inAlpha); + gEngfuncs.pTriAPI->Brightness(1.6f); + + thePoint[0] = inOrigin[0] - inRadius; + thePoint[1] = inOrigin[1] - inRadius; + gEngfuncs.pTriAPI->TexCoord2f(0, 0); + gEngfuncs.pTriAPI->Vertex3f(thePoint[0], thePoint[1], thePoint[2]); + + thePoint[0] = inOrigin[0] - inRadius; + thePoint[1] = inOrigin[1] + inRadius; + gEngfuncs.pTriAPI->TexCoord2f(0, 1); + gEngfuncs.pTriAPI->Vertex3f(thePoint[0], thePoint[1], thePoint[2]); + + thePoint[0] = inOrigin[0] + inRadius; + thePoint[1] = inOrigin[1] - inRadius; + gEngfuncs.pTriAPI->TexCoord2f(1, 0); + gEngfuncs.pTriAPI->Vertex3f(thePoint[0], thePoint[1], thePoint[2]); + + thePoint[0] = inOrigin[0] + inRadius; + thePoint[1] = inOrigin[1] + inRadius; + gEngfuncs.pTriAPI->TexCoord2f(1, 1); + gEngfuncs.pTriAPI->Vertex3f(thePoint[0], thePoint[1], thePoint[2]); + + gEngfuncs.pTriAPI->End(); + gEngfuncs.pTriAPI->RenderMode( kRenderNormal ); + } +} + +void AvHHud::DrawPlayerNames() +{ + bool inReadyRoom = false;//(this->GetPlayMode() == PLAYMODE_READYROOM); + bool inTopDownMode = this->GetInTopDownMode(); + + if(inTopDownMode || inReadyRoom /*&& !gEngfuncs.pDemoAPI->IsPlayingback()*/) + { + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + if(theLocalPlayer) + { + int theLocalPlayerIndex = theLocalPlayer->index; + + // Loop through all players + for(int i = 1; i <= kMaxPlayers; i++) + { + if((i != theLocalPlayerIndex) && AvHCUGetIsEntityInPVSAndVisible(i)) + { + cl_entity_s* theCurrentPlayer = gEngfuncs.GetEntityByIndex(i); + bool theDrawEntity = (inReadyRoom || (theCurrentPlayer && theCurrentPlayer->player && (AvHTeamNumber(theCurrentPlayer->curstate.team) == this->GetHUDTeam()) && (i != theLocalPlayerIndex)) ); + if(theDrawEntity) + { + string theEntityName; + bool theIsEnemy; + if(this->GetEntityInfoString(i, theEntityName, theIsEnemy)) + { + vec3_t theEntityOrigin; + VectorCopy(theCurrentPlayer->curstate.origin, theEntityOrigin); + theEntityOrigin.z += AvHCUGetIconHeightForPlayer((AvHUser3)theCurrentPlayer->curstate.iuser3); + + // If they are on screen + Vector theScreenPos; + if(AvHCUWorldToScreen(theEntityOrigin, (float*)&theScreenPos)) + { + // Set color + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, true, false); + + // If selected, draw in different color + if(inTopDownMode) + { + bool theIsSelected = (std::find(this->mSelected.begin(), this->mSelected.end(), i) != this->mSelected.end()); + if(theIsSelected) + { + // Selected color + UnpackRGB(theR, theG, theB, RGB_MARINE_SELECTED); + + if(GetHasUpgrade(theCurrentPlayer->curstate.iuser4, MASK_PARASITED)) + { + string thePrePendString; + LocalizeString(kParasited, thePrePendString); + theEntityName = string(theEntityName + " (" + thePrePendString + ")"); + + // Set color to parasited color + UnpackRGB(theR, theG, theB, RGB_MARINE_PARASITED); + } + } + } + + // Set text color draw in different color + this->mTopDownPlayerNameMessage.SetRGB(theR, theG, theB); + this->mTopDownPlayerNameMessage.SetIgnoreFadeForLifetime(true); + + // Set the message info and draw it + this->mTopDownPlayerNameMessage.SetText(theEntityName); + + // Set position + Vector theNormPos; + float theNormX = theScreenPos.x/ScreenWidth(); + + int theBoxHeight = this->mTopDownPlayerNameMessage.GetScreenHeight(); + float theNormY = (theScreenPos.y - theBoxHeight)/ScreenHeight(); + + if((inTopDownMode && !this->GetIsRegionBlockedByUI(theNormX, theNormY)) || inReadyRoom) + { + this->mTopDownPlayerNameMessage.SetNormalizedScreenX(theNormX); + this->mTopDownPlayerNameMessage.SetNormalizedScreenY(theNormY); + this->mTopDownPlayerNameMessage.SetCentered(true); + this->mTopDownPlayerNameMessage.SetNormalizedMaxWidth(kReticleMaxWidth); + this->mTopDownPlayerNameMessage.Draw(); + } + } + } + } + } + } + } + } +} + +//bool AvHHud::ChopStringOfMaxScreenWidth(int inMaxScreenWidth, string& ioBaseString, string& outChoppedString) +//{ +// // Loop backwards through the string, until we get a string that fits in this screen width +// int theCurrentLength = ioBaseString.length(); +// int theMaxLength = ioBaseString.length(); +// bool theSuccess = false; +// +// while(!theSuccess) +// { +// string theCurrentString = ioBaseString.substr(0, theCurrentLength); +// int theCurrentStringScreenWidth = this->GetHudStringWidth(theCurrentString.c_str()); +// if(theCurrentStringScreenWidth <= inMaxScreenWidth) +// { +// // Look for a word to break the line +// while((theCurrentLength > 0) && !theSuccess) +// { +// char theCurrentChar = ioBaseString[theCurrentLength-1]; +// if((theCurrentChar == ' ') || (theCurrentLength == theMaxLength)) +// { +// outChoppedString = ioBaseString.substr(0, theCurrentLength); +// ioBaseString = ioBaseString.substr(theCurrentLength, ioBaseString.length() - theCurrentLength); +// theSuccess = true; +// break; +// } +// else +// { +// theCurrentLength--; +// } +// } +// } +// else +// { +// theCurrentLength--; +// } +// } +// +// return theSuccess; +//} + +void AvHHud::DrawReticleInfo() +{ + this->mReticleMessage.Draw(); + + // if(this->mReticleInfoText != "") + // { + // const float kMaxWidth = .3f; + // int kMaxScreenWidth = kMaxWidth*ScreenWidth; + // + // StringList theStringList; + // string theHelpString = this->mReticleInfoText; + // + // do + // { + // string theNewString; + // if(ChopStringOfMaxScreenWidth(kMaxScreenWidth, theHelpString, theNewString)) + // { + // theStringList.push_back(theNewString); + // } + // else + // { + // theHelpString = ""; + // } + // } + // while(theHelpString != ""); + // + // // For each line, if the line contains any special markers, move them to their own lines + // + // // Compute max width of all the strings, add some extra for a frame + // int theBoxWidth = 0; + // StringList::iterator theStringListIter; + // for(theStringListIter = theStringList.begin(); theStringListIter != theStringList.end(); theStringListIter++) + // { + // int theCurrentScreenWidth = this->GetHudStringWidth(theStringListIter->c_str()); + // theBoxWidth = max(theBoxWidth, theCurrentScreenWidth); + // } + // int theLineHSpacing = .01f*ScreenWidth; + // theBoxWidth += 2*theLineHSpacing; + // + // // Compute max height needed to contain all the strings, add some extra for a frame + // int theLineVSpacing = .01f*ScreenHeight(); + // int theLineHeight = this->GetHudStringHeight(); + // int theBoxHeight = 2*theLineVSpacing + (theStringList.size()*theLineHeight); + // + // int theFillStartX = this->mReticleInfoScreenX; + // int theFillStartY = this->mReticleInfoScreenY; + // + // theFillStartX -= theBoxWidth/2; + // theFillStartY -= theBoxHeight/2; + // + // // Draw nice border and shaded background + // const float kReticleInfoMaxAlpha = 25; + // float theNormalizedAlpha = this->mReticleInfoColorA/255; + // int theAlphaComponent = theNormalizedAlpha*kReticleInfoMaxAlpha; + // + // FillRGBA(theFillStartX, theFillStartY, theBoxWidth, theBoxHeight, this->mReticleInfoColorR, this->mReticleInfoColorG, this->mReticleInfoColorB, theAlphaComponent); + // vguiSimpleBox(theFillStartX, theFillStartY, theFillStartX + theBoxWidth, theFillStartY + theBoxHeight, this->mReticleInfoColorR, this->mReticleInfoColorG, this->mReticleInfoColorB, theAlphaComponent); + // + // // Now draw each line, non-centered, left-aligned + // int theLineNumber = 0; + // for(theStringListIter = theStringList.begin(); theStringListIter != theStringList.end(); theStringListIter++) + // { + // int theR = this->mReticleInfoColorR; + // int theG = this->mReticleInfoColorG; + // int theB = this->mReticleInfoColorB; + // + // // If the line starts with a marker, draw it in a special color + // //string theDamageMarker(kDamageMarker); + // //if(theStringListIter->substr(0, theDamageMarker.length()) == theDamageMarker) + // //{ + // // // Draw string in yellow + // // theR = theG = 255; + // // theB = 25; + // //} + // + // int theBaseY = theFillStartY + theLineVSpacing + theLineNumber*theLineHeight; + // + // // Draw message (DrawHudStringCentered only centers in x) + // this->DrawHudString(theFillStartX + theLineHSpacing, theBaseY /*- theLineHeight/2*/, ScreenWidth, theStringListIter->c_str(), theR*theNormalizedAlpha, theG*theNormalizedAlpha, theB*theNormalizedAlpha); + // + // theLineNumber++; + // } + // } +} + +void AvHHud::DrawToolTips() +{ + if(!gEngfuncs.pDemoAPI->IsPlayingback()) + { + this->mHelpMessage.Draw(); + + // Draw each one + for(AvHTooltipListType::iterator theIter = this->mTooltips.begin(); theIter != this->mTooltips.end(); theIter++) + { + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, true, false); + theIter->SetRGB(theR, theG, theB); + theIter->Draw(); + } + } +} + +void AvHHud::DrawWorldSprite(int inSpriteHandle, int inRenderMode, vec3_t inWorldPosition, int inFrame, float inWorldSize, float inAlpha) +// : added inAlpha +{ + vec3_t theUpperLeft; + vec3_t theLowerRight; + + vec3_t theForward, theRight, theUp; + AngleVectors(v_angles, theForward, theRight, theUp); + + vec3_t theToUpperLeft; + VectorAdd(-theRight, theUp, theToUpperLeft); + VectorNormalize(theToUpperLeft); + + VectorMA(inWorldPosition, inWorldSize, theToUpperLeft, theUpperLeft); + + vec3_t theToLowerRight; + VectorAdd(theRight, -theUp, theToLowerRight); + VectorNormalize(theToLowerRight); + + VectorMA(inWorldPosition, inWorldSize, theToLowerRight, theLowerRight); + + vec3_t theScreenUpperLeft; + vec3_t theScreenLowerRight; + + // World to screen returns true if the world pos is behind the viewer + if(!gEngfuncs.pTriAPI->WorldToScreen((float*)theUpperLeft, (float*)theScreenUpperLeft)) + { + if(!gEngfuncs.pTriAPI->WorldToScreen((float*)theLowerRight, (float*)theScreenLowerRight)) + { + // If the sprite is behind you, push it to the bottom or top of the screen +// cl_entity_t* theLocalPlayer = gEngfuncs.GetLocalPlayer(); +// ASSERT(theLocalPlayer); +// +// vec3_t theDirectionToOrder; +// VectorSubtract(inWorldPosition, theLocalPlayer->origin, theDirectionToOrder); + + // float theDotProduct = DotProduct(theDirectionToOrder, theLocalPlayer->angles); + // if(theDotProduct < 0) + // { + // if(theWorldPos.z < theLocalPlayer->origin.z) + // { + // theY = theScreenHeight - theSpriteHeight - theScreenBorder; + // } + // else + // { + // theY = theScreenBorder; + // } + + // vec3_t theCrossProduct; + // theCrossProduct = CrossProduct(theLocalPlayer->angles, theDirectionToOrder); + // + // // It's to our right + // if(theCrossProduct.z > 0) + // { + // theX = theScreenWidth - theSpriteWidth - theScreenBorder; + // } + // else + // { + // theX = theScreenBorder; + // } + // } + +// float theDistanceToLocation = (float)VectorDistance(inWorldPosition, theLocalPlayer->origin); +// const theMaxDistance = 1500; +// float theEffectiveDistance = min(theDistanceToLocation, theMaxDistance); +// const int theMinColorComponent = 100; +// int theColorComponent = max(theMinColorComponent, 255 - ((theEffectiveDistance/theMaxDistance)*255)); + + //SPR_Set(inSpriteHandle, theColorComponent, theColorComponent, theColorComponent); + ////SPR_DrawHoles((int)0, theX, theY, NULL); + //if(inRenderMode == kRenderNormal) + //{ + // SPR_Draw(inFrame, theX, theY, NULL); + //} + //else if(inRenderMode == kRenderTransAdd) + //{ + // SPR_DrawAdditive(inFrame, theX, theY, NULL); + //} + + float theScreenX = XPROJECT(theScreenUpperLeft.x); + float theScreenY = YPROJECT(theScreenUpperLeft.y); + float theWidth = XPROJECT(theScreenLowerRight.x) - theScreenX; + float theHeight = YPROJECT(theScreenLowerRight.y) - theScreenY; + + //DrawScaledHUDSprite(inSpriteHandle, inRenderMode, 1, theScreenX, theScreenY, theWidth, theHeight, inFrame); + + AvHSpriteSetColor(1, 1, 1, inAlpha); + AvHSpriteSetRenderMode(inRenderMode); + AvHSpriteDraw(inSpriteHandle, inFrame, theScreenX, theScreenY, theScreenX + theWidth, theScreenY + theHeight, 0, 0, 1, 1); + + + + } + } +} + +void AvHHud::DrawOrderIcon(const AvHOrder& inOrder) +{ + if(this->mOrderSprite) + { + int theNumFrames = SPR_Frames(this->mOrderSprite); + int theCurrentFrame = this->GetFrameForOrderType(inOrder.GetOrderType()); + + if((theCurrentFrame >= 0) && (theCurrentFrame < theNumFrames)) + { + vec3_t theWorldPos; + inOrder.GetLocation(theWorldPos); + if ( inOrder.GetOrderType() == ORDERTYPET_ATTACK ) { + theWorldPos[2]+=kAttackOrderZOffset; + } + + // Draw icon above pos, text below + theWorldPos.z += BALANCE_VAR(kOrderIconDrawSize); + + this->DrawWorldSprite(this->mOrderSprite, kRenderTransAdd, theWorldPos, theCurrentFrame, BALANCE_VAR(kOrderIconDrawSize)); + + // If the order is our own order, draw the order indicator around it + if((this->GetHUDPlayMode() == PLAYMODE_PLAYING) && this->GetIsMarine() && !this->GetInTopDownMode()) + { + this->DrawWorldSprite(this->mMarineOrderIndicator, kRenderTransAdd, theWorldPos, 0, BALANCE_VAR(kOrderIconDrawSize)); + //DrawScaledHUDSprite(theSpriteHandle, kRenderNormal, 1, thePosX, thePosY, theWidth, theHeight, theFrame, theStartU, theStartV, theEndU, theEndV); + + } + + vec3_t orderDir; + inOrder.GetLocation(orderDir); + this->GetOrderDirection(orderDir, 2); + } + } +} + +void AvHHud::DrawOrderText(const AvHOrder& inOrder) +{ + int theIndex = (int)(inOrder.GetOrderType()); + + // Now draw text describing the waypoint + string theTitle; + sprintf(theTitle, "Order%d", theIndex); + + string theLocalizedTitle(" "); + LocalizeString(theTitle.c_str(), theLocalizedTitle); + + if((theIndex == ORDERTYPET_BUILD) || (theIndex == ORDERTYPET_GUARD) || (theIndex == ORDERTYPET_GET)) + { + AvHUser3 theUser3 = inOrder.GetTargetUser3Type(); + string theUser3Name; + if(this->GetTranslatedUser3Name(theUser3, theUser3Name)) + { + // "Get %s" -> "Get heavy machine gun" + // "Guard %s -> "Guard soldier" + // "Build %s" -> "Build sentry turret" + string theTitleWithTarget; + sprintf(theTitleWithTarget, theLocalizedTitle.c_str(), theUser3Name.c_str()); + theLocalizedTitle = theTitleWithTarget; + } + } + + vec3_t theOrderLocation; + inOrder.GetLocation(theOrderLocation); + + // Because the commander may not have information about the players heading to this waypoint (outside of his PVS), we + // can't draw a range for the commander + string theRangeDisplayString; + if(!this->GetInTopDownMode()) + { + float theDistanceToWaypoint = VectorDistance(gPredictedPlayerOrigin, theOrderLocation); + int theVisibleDistance = max(1, (int)theDistanceToWaypoint/100); + + string theVisibleUnits; + if(LocalizeString("Range", theVisibleUnits)) + { + sprintf(theRangeDisplayString, theVisibleUnits.c_str(), theVisibleDistance); + } + } + + string theLocationOfOrder; + theLocationOfOrder = this->GetNameOfLocation(theOrderLocation); + + // It's OK if this fails, as only official maps will have these translated + string theTranslatedLocation = theLocationOfOrder; + LocalizeString(theLocationOfOrder.c_str(), theTranslatedLocation); + + // : 0000992 + string theFirstLine = theLocalizedTitle; + if(theRangeDisplayString != "") + { + theFirstLine += string(" : ") + theRangeDisplayString; + } + // : + + Vector theScreenPos; + if(AvHCUWorldToScreen((float*)theOrderLocation, (float*)&theScreenPos)) + { + float theNormX = theScreenPos.x/ScreenWidth(); + float theNormY = theScreenPos.y/ScreenHeight(); + + if(!this->GetIsRegionBlockedByUI(theNormX, theNormY)) + { + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, false, false); + + string theFirstLine = theLocalizedTitle; + if(theRangeDisplayString != "") + { + theFirstLine += string(" : ") + theRangeDisplayString; + } + + // Draw order (icon above world position, text below it) + int theBaseX = theScreenPos.x; + int theBaseY = theScreenPos.y; + int theStringHeight = this->GetHudStringHeight(); + this->DrawHudStringCentered(theBaseX, theBaseY + theStringHeight, ScreenWidth(), theFirstLine.c_str(), theR, theG, theB); + + // Draw location below it + this->DrawHudStringCentered(theBaseX, theBaseY + 2*theStringHeight, ScreenWidth(), theTranslatedLocation.c_str(), theR, theG, theB); + } + } + // : 0000992 + if (this->mDisplayOrderType == 2) + { + // this->mDisplayOrderText1 = "The commander issued an order:"; + this->mDisplayOrderText1 = theFirstLine.c_str(); + this->mDisplayOrderText2 = theTranslatedLocation.c_str(); + } + // : +} + +// : +#define CENTER_TEXT_LENGTH 10 +#define CENTER_TEXT_FADEOUT 2 +void AvHHud::DrawCenterText() +{ + if ((this->mCenterTextTime > -1) && (this->mTimeOfLastUpdate < this->mCenterTextTime + CENTER_TEXT_LENGTH + CENTER_TEXT_FADEOUT)) + { + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, false, false); + + if (this->mTimeOfLastUpdate > this->mCenterTextTime + CENTER_TEXT_LENGTH) + { + float fraction = this->mTimeOfLastUpdate - (this->mCenterTextTime + CENTER_TEXT_LENGTH); + fraction = 1 - fraction / CENTER_TEXT_FADEOUT; + theR *= fraction; + theG *= fraction; + theB *= fraction; + } + + int posX = 0.5 * ScreenWidth() - this->mFont.GetStringWidth(this->mCenterText.c_str()) / 2; + int posY = 0.4 * ScreenHeight(); + + this->mFont.DrawString(posX, posY, this->mCenterText.c_str(), theR, theG, theB); + } +} +// : + +// : 0000992 +void AvHHud::SetDisplayOrder(int inOrderType, int inOrderIndex, string inText1, string inText2, string inText3) +{ + this->mDisplayOrderTime = this->mTimeOfLastUpdate; + this->mDisplayOrderType = inOrderType; + this->mDisplayOrderIndex = inOrderIndex; + this->mDisplayOrderText1 = inText1; + this->mDisplayOrderText2 = inText2; + this->mDisplayOrderText3 = inText3; +} + +void AvHHud::DrawDisplayOrder() +{ + const float flashLength = 1.0f; + const float fadeLimit = 6.0f; + const float fadeEnd = 2.0f; + + if ((this->mDisplayOrderType > 0) && (this->mDisplayOrderTime + fadeLimit + fadeEnd) > this->mTimeOfLastUpdate && (this->GetInTopDownMode() == false)) + { + float theFade = 1.0f; + if ((this->mDisplayOrderTime + fadeLimit) < this->mTimeOfLastUpdate) + { + theFade = 1.0f - (this->mTimeOfLastUpdate - (this->mDisplayOrderTime + fadeLimit)) / fadeEnd; + if(theFade < 0.0f) + { + this->mDisplayOrderType = 0; + return; + } + } + + // flash the icon for the first second + if ((this->mDisplayOrderTime + flashLength) > this->mTimeOfLastUpdate) + { + if (((int)((this->mTimeOfLastUpdate - this->mDisplayOrderTime) * 8)) % 2) + { + theFade = 0.0f; + } + } + + // draw the panel +// int sprite = Safe_SPR_Load(kWhiteSprite); + + int r, g, b; + GetPrimaryHudColor(r, g, b, true, false); + + int theStringHeight = this->GetHudStringHeight(); + + float mIconX1 = 0.47f * ScreenWidth(); + float mIconY1 = 0.10f * ScreenHeight(); + float mIconX2 = mIconX1 + 0.06f * ScreenWidth(); + float mIconY2 = mIconY1 + 0.06f * ScreenWidth(); + float mLeftX = mIconX1 - 0.06f * ScreenWidth(); + float mRightX = mIconX2 + 0.06f * ScreenWidth(); + + float mTextX1 = 0.50f * ScreenWidth(); + + AvHSpriteSetRenderMode(kRenderTransAdd); + AvHSpriteSetDrawMode(kSpriteDrawModeFilled); + AvHSpriteSetColor(1, 1, 1, 1 * theFade); + + int theTeamAdd = 0; + if (this->GetIsAlien()) + theTeamAdd = 2; + + if (this->mDisplayOrderDirection == 1) + AvHSpriteDraw(this->mTeammateOrderSprite, TEAMMATE_MARINE_LEFT_ARROW + theTeamAdd, + mLeftX, mIconY1, mIconX1, mIconY2, 0, 0, 1, 1); // Left + else if (this->mDisplayOrderDirection == 2) + AvHSpriteDraw(this->mTeammateOrderSprite, TEAMMATE_MARINE_RIGHT_ARROW + theTeamAdd, + mIconX2, mIconY1, mRightX, mIconY2, 0, 0, 1, 1); // Right + + if (this->mDisplayOrderType == 1) + { + AvHSpriteDraw(this->mTeammateOrderSprite, this->mDisplayOrderIndex + 8, mIconX1, mIconY1, mIconX2, mIconY2, 0, 0, 1, 1); + this->DrawHudStringCentered(mTextX1, mIconY2, ScreenWidth(), this->mDisplayOrderText1.c_str(), r, g, b); + } + else if (this->mDisplayOrderType == 2) + { + AvHSpriteDraw(this->mOrderSprite, this->mDisplayOrderIndex, mIconX1, mIconY1, mIconX2, mIconY2, 0, 0, 1, 1); + this->DrawHudStringCentered(mTextX1, mIconY2, ScreenWidth(), this->mDisplayOrderText1.c_str(), r, g, b); + this->DrawHudStringCentered(mTextX1, mIconY2 + theStringHeight, ScreenWidth(), this->mDisplayOrderText2.c_str(), r, g, b); + } + +// float mTextX1 = mIconX2 + 0.02 * ScreenWidth(); +// this->DrawHudString(mTextX1, mIconY1, ScreenWidth(), this->mDisplayOrderText1.c_str(), r, g, b); +// this->DrawHudString(mTextX1, mIconY1 + theStringHeight, ScreenWidth(), this->mDisplayOrderText2.c_str(), r, g, b); +// this->DrawHudString(mTextX1, mIconY1 + theStringHeight * 2, ScreenWidth(), this->mDisplayOrderText3.c_str(), r, g, b); + } +} +// : + +// : 0000971 +void AvHHud::GetOrderDirection(vec3_t inTarget, int inOrderType) +{ + if (this->mDisplayOrderType == inOrderType) + { + // find left/right/none direction for the order popup notificator + vec3_t theForward, theRight, theUp, theDir; + AngleVectors(v_angles, theForward, theRight, theUp); + VectorSubtract(inTarget, v_origin, theDir); + theForward[2] = theDir[2] = 0; + VectorNormalize(theForward); + VectorNormalize(theDir); + + this->mDisplayOrderDirection = 0; + // if it is too close to screen center, ignore it + if (DotProduct(theForward, theDir) < 0.9f) + { + // Determine left and right + vec3_t theCrossProd = CrossProduct(theForward, theDir); + if (theCrossProd[2] > 0.0f) + this->mDisplayOrderDirection = 1; // Left + else if (theCrossProd[2] < 0.0f) + this->mDisplayOrderDirection = 2; // Right + } + } +} + +void AvHHud::DrawTeammateOrders() +{ + TeammateOrderListType::iterator toErase = NULL; + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + + const float flashLength = 1.0f; + const float fadeLimit = 6.0f; + const float fadeEnd = 2.0f; + + for(TeammateOrderListType::iterator theIter = this->mTeammateOrder.begin(); theIter != this->mTeammateOrder.end(); theIter++) + { + float lastOrder = 0; + TeammateOrderType theOrder = (*theIter).second; + int theEntIndex = (*theIter).first; + float theFade = 1.0f; + + // remove the order if it has expired + if((theOrder.second + fadeEnd + fadeLimit) < this->mTimeOfLastUpdate) + { + toErase = theIter; + continue; + } + // draw the order fading away + else if((theOrder.second + fadeLimit) < this->mTimeOfLastUpdate) + { + theFade = 1.0f - (this->mTimeOfLastUpdate - (theOrder.second + fadeLimit)) / fadeEnd; + if(theFade < 0.0f) + theFade = 0.0f; + } + // else, draw the order normally + + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(theEntIndex); + if (theEntity && (theEntIndex < MAX_PLAYERS && theEntIndex >= 0) && (theEntity->index != theLocalPlayer->index)) + { + if (AvHTraceLineAgainstWorld(theLocalPlayer->origin, theEntity->origin) == 1.0f) + { + vec3_t theVec; + VectorCopy(theEntity->origin, theVec); + theVec[2] += AvHCUGetIconHeightForPlayer((AvHUser3)theEntity->curstate.iuser3); + this->DrawWorldSprite(this->mTeammateOrderSprite, kRenderTransAdd, theVec, theOrder.first, kHelpIconDrawSize, theFade); + + if (lastOrder < theOrder.second) + { + lastOrder = theOrder.second; + this->GetOrderDirection(theVec, 1); + } + } + } + } + + if (toErase != NULL) + this->mTeammateOrder.erase(toErase); + + // flash target player + if (((this->mCurrentOrderTime + flashLength) > this->mTimeOfLastUpdate) && (this->mCurrentOrderTarget > 0)) + { + if (((int)((this->mTimeOfLastUpdate - (this->mCurrentOrderTime + flashLength)) * 8)) % 2) + { + cl_entity_s* theTargetEntity = gEngfuncs.GetEntityByIndex(this->mCurrentOrderTarget); + + vec3_t theVec; + VectorCopy(theTargetEntity->origin, theVec); + theVec[2] += AvHCUGetIconHeightForPlayer((AvHUser3)theTargetEntity->curstate.iuser3); + this->DrawWorldSprite(this->mTeammateOrderSprite, kRenderTransAdd, theVec, this->mCurrentOrderType, kHelpIconDrawSize, 1.0f); + + } + } + + +} +// : + +void AvHHud::DrawOrders() +{ + if(1/*!this->mIsRenderingSelectionView*/) + { + // Draw them blinking for soldiers, but always for commanders + float theFractionalLastUpdate = this->mTimeOfLastUpdate - (int)this->mTimeOfLastUpdate; + if((theFractionalLastUpdate > .25f) || (this->GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER)) + { + OrderListType theOrders = this->GetOrderList(); + + EntityListType theDrawPlayerList = this->GetDrawPlayerOrders(); + + // Run through the order list type + for(OrderListType::iterator theIter = theOrders.begin(); theIter != theOrders.end(); theIter++) + { + // For each one, if the order is for a player in the theDrawPlayerList, draw it + vec3_t theOrderLocation; + theIter->GetLocation(theOrderLocation); + + if(theIter->GetOrderTargetType() == ORDERTARGETTYPE_TARGET) + { + int theTargetIndex = theIter->GetTargetIndex(); + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(theTargetIndex); + if(theEntity) + { + //: dont follow if they are cloaked, leave the waypoint active so they have a clue where they may be at, the wp should snap back to the baddy + //once they are spotted again. + + if(theEntity->curstate.rendermode != kRenderTransTexture && theEntity->curstate.renderamt > 128) + VectorCopy(theEntity->origin, theOrderLocation); + } + } + + // 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::iterator theSearchIter = std::find(theDrawPlayerList.begin(), theDrawPlayerList.end(), theReceiverPlayer); + if(theSearchIter != theDrawPlayerList.end() && *theSearchIter == theReceiverPlayer) + { + //gEngfuncs.pEfxAPI->R_ParticleLine((float*)theEntity->origin, (float*)theOrderLocation, 0, 255, 0, .05f); + theDrawWaypoint = true; + } + if(theDrawWaypoint) + { + this->DrawOrderIcon(*theIter); + this->DrawOrderText(*theIter); + } + } + } + } +} + +int AvHHud::GetHelpIconFrameFromUser3(AvHUser3 inUser3) +{ + int theFrame = -1; + + switch(inUser3) + { + case AVH_USER3_WELD: + theFrame = 0; + break; + case AVH_USER3_MARINEITEM: + theFrame = 1; + break; + case AVH_USER3_HIVE: + theFrame = 2; + break; + //case AVH_USER3_USEABLE: + // theFrame = 3; + // break; + case AVH_USER3_COMMANDER_STATION: + theFrame = 4; + break; + //case AVH_USER3_BREAKABLE: + // theFrame = 5; + // break; + } + + return theFrame; +} + +HSPRITE AvHHud::GetHelpSprite() const +{ + return this->mHelpSprite; +} + +void AvHHud::DrawHelpIcons() +{ + // Lookup table + + // Only draw if enabled + //if(gEngfuncs.pfnGetCvarFloat(kvAutoHelp)) + //{ + // Iterate through help icons, drawing each one if we have an sprite for it + for(HelpIconListType::iterator theIter = this->mHelpIcons.begin(); theIter != this->mHelpIcons.end(); theIter++) + { + int theUser3 = theIter->second; + int theFrame = GetHelpIconFrameFromUser3(AvHUser3(theUser3)); + + // Lookup sprite to see if it's loaded +// if(this->mHelpSprites[theUser3] == 0) +// { +// string theIconName = string(kHelpIconPrefix) + MakeStringFromInt(theUser3) + ".spr"; +// this->mHelpSprites[theUser3] = Safe_SPR_Load(theIconName.c_str()); +// } +// +// int theSpriteHandle = this->mHelpSprites[theUser3]; +// if(theSpriteHandle > 0) +// { +// // Draw icon at entity center +// this->DrawWorldSprite(theSpriteHandle, kRenderTransAdd, theIter->first, 0); +// } + + if((theFrame >= 0) && this->mHelpSprite) + { + this->DrawWorldSprite(this->mHelpSprite, kRenderTransAdd, theIter->first, theFrame, kHelpIconDrawSize); + } + } + //} +} + +// inDrawMode determines if we're drawing text or sprites +void AvHHud::DrawHUDStructureNotification() +{ + const float kHUDStructureNotificationStartX = .02f; + const float kHUDStructureNotificationStartY = .11f; + const float kHUDStructureNotificationIconWidth = .03f; + const float kHUDStructureNotificationIconHeight = kHUDStructureNotificationIconWidth*1.333f; + const float kHUDStructureNotificationIconHorizontalSpacing = .01f; + const float kHUDStructureNotificationIconVerticalSpacing = kHUDStructureNotificationIconHorizontalSpacing*1.333f; + const float kHUDStructureNotificationMaxTextWidth = .2f; + + // Draw them all in order + if(this->GetIsAlive() && CVAR_GET_FLOAT(kvBuildMessages)) + { + // Get starting coords + float theCurrentX = kHUDStructureNotificationStartX; + float theCurrentY = kHUDStructureNotificationStartY; + + + 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(); + this->DrawTranslatedString(theStartX, theCurrentY*ScreenHeight(), theLocationName.c_str(), false, true); + } + + // Increment coords + theCurrentY += (kHUDStructureNotificationIconHeight + kHUDStructureNotificationIconVerticalSpacing); + } + + } +} + +void AvHHud::DrawInfoLocationText() +{ + string theTimeLeftText; + + // Get drawing color and position + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, false, false); + + // Get position of health and draw to the right of it (must be left-justified) + int theX = 0; + int theY = 0; + if(this->GetInTopDownMode()) + { + const float kLeftInset = .4f; + theX = mViewport[0] + kLeftInset*ScreenWidth(); + theY = mViewport[1] + mViewport[3] - (1-.78f)*ScreenHeight(); + } + // Draw info location text the same for aliens and marines + else if(this->GetHUDUser3() != AVH_USER3_ALIEN_EMBRYO) + { + const float kLeftInset = .08f; + theX = mViewport[0] + kLeftInset*ScreenWidth(); + theY = mViewport[1] + mViewport[3] - (1-.88f)*ScreenHeight(); + } + int theLeftEdge = theX; + + // Draw location text, translation if possible + string theLocalizedLocationText; + if(this->mLocationText != "") + { + LocalizeString(this->mLocationText.c_str(), theLocalizedLocationText); + if(theLocalizedLocationText[0] == '#') + { + theLocalizedLocationText = theLocalizedLocationText.substr(1, theLocalizedLocationText.size()); + } + + // Draw handicap text as well + int theHandicap = (int)this->GetHUDHandicap(); + if(theHandicap < 100) + { + // draw "(handicap 70%)" + string theHandicapString; + if(LocalizeString(kHandicap, theHandicapString)) + { + char theFormattedHandicapString[256]; + sprintf(theFormattedHandicapString, " (%d%% %s)", theHandicap, theHandicapString.c_str()); + theLocalizedLocationText += string(theFormattedHandicapString); + } + } + + // Draw info location + if(theLocalizedLocationText != "") + { + char theCharArray[512]; + sprintf(theCharArray, "%s", theLocalizedLocationText.c_str()); + + this->DrawHudString(theX, theY, ScreenWidth(), theCharArray, theR, theG, theB); + } + } + + // Don't draw time when playing back, it isn't right and not worth breaking backward-compatibility over. + // TODO: Draw time next time demo version changes + if(!gEngfuncs.pDemoAPI->IsPlayingback() && this->GetHUDUser3() != AVH_USER3_ALIEN_EMBRYO) + { + // Whether we draw first line or not, increment for second line below + theY += this->GetHudStringHeight(); + + // Optionally draw time left below it + int theTimeLimitSeconds = (int)this->GetGameTimeLimit(); + int theTimeElapsed = this->GetGameTime(); + int theMinutesElapsed = theTimeElapsed/60; + int theSecondsElapsed = theTimeElapsed%60; + int theTimeLeftSeconds = theTimeLimitSeconds - theTimeElapsed; + int theMinutesLeft = theTimeLeftSeconds/60; + int theSecondsLeft = theTimeLeftSeconds%60; + int theMinutesLimit = theTimeLimitSeconds/60; + int theSecondsLimit = theTimeLimitSeconds%60; + + // If we're in tournament mode or playing Combat, draw the timelimit + bool theTournyMode = (gHUD.GetServerVariableFloat(kvTournamentMode) > 0); + bool theDisplayTimeLimit = theTournyMode || gHUD.GetIsCombatMode(); + + string theGameTimeText; + if(LocalizeString(kGameTime, theGameTimeText)) + { + bool theTimeVisible = true; + + // Flash time when we're almost out of time + if((theMinutesLeft < 1) && this->GetGameStarted() && theDisplayTimeLimit) + { + float theTime = gHUD.GetTimeOfLastUpdate(); + float theTimeFraction = theTime - floor(theTime); + if(theTimeFraction < .5f) + { + theTimeVisible = false; + } + } + + // Game time - 12:43 + char theGameTimeCStr[256]; + sprintf(theGameTimeCStr, " - %02d:%02d", theMinutesElapsed, theSecondsElapsed); + theGameTimeText += string(theGameTimeCStr); + + if(theTimeVisible) + { + this->DrawHudString(theX, theY, ScreenWidth(), theGameTimeText.c_str(), theR, theG, theB); + } + + // Increment X so time limit is drawn properly + theX += this->GetHudStringWidth(theGameTimeText.c_str()); + } + + if(theDisplayTimeLimit) + { + string theTimeLimitString; + if(LocalizeString(kTimeLimit, theTimeLimitString)) + { + // Format: Time limit - 60:00 + char theTimeLimitCStr[256]; + sprintf(theTimeLimitCStr, " %s - %02d:%02d", theTimeLimitString.c_str(), theMinutesLimit, theSecondsLimit); + + string theTimeLimitText = string(theTimeLimitCStr); + + this->DrawHudString(theX, theY, ScreenWidth(), theTimeLimitText.c_str(), theR, theG, theB); + } + + if(gHUD.GetIsCombatMode()) + { + theY += this->GetHudStringHeight(); + + // Draw "attacking" or "defending" + AvHTeamNumber theTeamNumber = this->GetHUDTeam(); + string theDisplayString = ""; + if(theTeamNumber != TEAM_IND) + { + if(theTeamNumber == this->GetCombatAttackingTeamNumber()) + { + LocalizeString(kAttacking, theDisplayString); + } + else + { + LocalizeString(kDefending, theDisplayString); + } + + this->DrawHudString(theLeftEdge, theY, ScreenWidth(), theDisplayString.c_str(), theR, theG, theB); + } + } + } + } +} + +void AvHHud::DrawMouseCursor(int inBaseX, int inBaseY) +{ + if ( g_iVisibleMouse && !(this->GetInTopDownMode() && gEngfuncs.pDemoAPI->IsPlayingback()) ) + { + + HSPRITE theCursorSprite; + int theCursorFrame; + + GetCursor(theCursorSprite, theCursorFrame); + + if (theCursorSprite > 0) + { + float theGammaSlope = this->GetGammaSlope(); + ASSERT(theGammaSlope > 0.0f); + + /* + int theColorComponent = 255/theGammaSlope; + SPR_Set(this->mCursorSprite, theColorComponent, theColorComponent, theColorComponent); + + // Draw the logo at 20 fps + //SPR_DrawAdditive( 0, this->mMouseCursorX - inBaseX, this->mMouseCursorY - inBaseY, NULL ); + const int kMouseHotSpotX = -1; + const int kMouseHotSpotY = -1; + SPR_DrawHoles(this->mCurrentCursorFrame, this->mMouseCursorX - inBaseX - kMouseHotSpotX, this->mMouseCursorY - inBaseY - kMouseHotSpotY, NULL ); + */ + + // Draw the mouse cursor. + + const int kMouseHotSpotX = -1; + const int kMouseHotSpotY = -1; + + AvHSpriteBeginFrame(); + + AvHSpriteEnableVGUI(true); + AvHSpriteSetVGUIOffset(inBaseX, inBaseY); + + float x = this->mMouseCursorX - kMouseHotSpotX; + float y = this->mMouseCursorY - kMouseHotSpotY; + + float w = SPR_Width(theCursorSprite, theCursorFrame); + float h = SPR_Height(theCursorSprite, theCursorFrame); + + AvHSpriteSetRenderMode(kRenderTransAlpha); + AvHSpriteDraw(theCursorSprite, theCursorFrame, x, y, x + w, y + h, 0, 0, 1, 1); + + // Draw the marquee if it's visible. + + if (mSelectionBoxVisible) + { + + int sprite = Safe_SPR_Load(kWhiteSprite); + + int r, g, b; + GetPrimaryHudColor(r, g, b, true, false); + + AvHSpriteSetRenderMode(kRenderTransAdd); + AvHSpriteSetColor(r / 255.0, g / 255.0, b / 255.0, 0.3); + + AvHSpriteSetDrawMode(kSpriteDrawModeFilled); + AvHSpriteDraw(sprite, 0, mSelectionBoxX1 + 1, mSelectionBoxY1 + 1, mSelectionBoxX2 - 1, mSelectionBoxY2 - 1, 0, 0, 1, 1); + + AvHSpriteSetRenderMode(kRenderNormal); + AvHSpriteSetColor(1, 1, 1, 1); + + AvHSpriteSetDrawMode(kSpriteDrawModeBorder); + AvHSpriteDraw(sprite, 0, mSelectionBoxX1, mSelectionBoxY1, mSelectionBoxX2, mSelectionBoxY2, 0, 0, 1, 1); + + } + + AvHSpriteEndFrame(); + + } + } +} + + + +void AvHHud::DrawTopDownBG() +{ + // If we're in top down mode, draw a black background + float theDrawBackgroundHeight = -1;//cl_drawbg->value; + + // Draw the bottom plane at the map's min view height, unless overridden by cl_drawbg (this variable is for for testing purposes only) + if(theDrawBackgroundHeight == -1) + { + if(this->mMapExtents.GetDrawMapBG()) + { + theDrawBackgroundHeight = this->mMapExtents.GetMinViewHeight(); + } + } + + if(theDrawBackgroundHeight != -1) + { + if(this->mBackgroundSprite) + { + // Build three points on plane described by max world dimensions at the draw background height + Vector theUpperLeftWorldPos(-kMaxMapDimension, kMaxMapDimension, theDrawBackgroundHeight); + Vector theUpperRightWorldPos(kMaxMapDimension, kMaxMapDimension, theDrawBackgroundHeight); + Vector theLowerLeftWorldPos(-kMaxMapDimension, -kMaxMapDimension, theDrawBackgroundHeight); + + // Calculate plane info + float thePlaneD; + Vector thePlaneNormal; + CalculatePlaneInfo(theUpperLeftWorldPos, theUpperRightWorldPos, theLowerLeftWorldPos, thePlaneD, thePlaneNormal); + + gEngfuncs.pTriAPI->RenderMode( kRenderNormal ); + gEngfuncs.pTriAPI->CullFace( TRI_NONE ); + gEngfuncs.pTriAPI->Brightness( 1 ); + + if(gEngfuncs.pTriAPI->SpriteTexture((struct model_s *)gEngfuncs.GetSpritePointer(this->mBackgroundSprite), 0)) + { + gEngfuncs.pTriAPI->Begin( TRI_TRIANGLE_STRIP ); + + gEngfuncs.pTriAPI->Color4f(1.0f, 1.0f, 1.0f, 1.0f); + + Vector thePoint; + + CalculatePointOnPlane(0, ScreenHeight(), GetViewOrigin(), thePlaneNormal, thePlaneD, thePoint); + gEngfuncs.pTriAPI->TexCoord2f(0, 1); + gEngfuncs.pTriAPI->Vertex3fv((float*)&thePoint); + + CalculatePointOnPlane(0, 0, GetViewOrigin(), thePlaneNormal, thePlaneD, thePoint); + gEngfuncs.pTriAPI->TexCoord2f(0, 0); + gEngfuncs.pTriAPI->Vertex3fv((float*)&thePoint); + + CalculatePointOnPlane(ScreenWidth(), ScreenHeight(), GetViewOrigin(), thePlaneNormal, thePlaneD, thePoint); + gEngfuncs.pTriAPI->TexCoord2f(1, 1); + gEngfuncs.pTriAPI->Vertex3fv((float*)&thePoint); + + CalculatePointOnPlane(ScreenWidth(), 0, GetViewOrigin(), thePlaneNormal, thePlaneD, thePoint); + gEngfuncs.pTriAPI->TexCoord2f(1, 0); + gEngfuncs.pTriAPI->Vertex3fv((float*)&thePoint); + + // Add in some buffer because of perspective +// pVector ver; +// ver.z = theDrawBackgroundHeight; +// +// // Lower left +// gEngfuncs.pTriAPI->TexCoord2f(0,1); +// ver.x = -kMaxMapDimension; +// ver.y = -kMaxMapDimension; +// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); +// +// // Upper left +// gEngfuncs.pTriAPI->TexCoord2f(0,0); +// ver.x = -kMaxMapDimension; +// ver.y = kMaxMapDimension; +// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); +// +// // Upper right +// gEngfuncs.pTriAPI->TexCoord2f(1,0); +// ver.x = kMaxMapDimension; +// ver.y = kMaxMapDimension; +// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); +// +// // Lower right +// gEngfuncs.pTriAPI->TexCoord2f(1,1); +// ver.x = kMaxMapDimension; +// ver.y = -kMaxMapDimension; +// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); + + gEngfuncs.pTriAPI->End(); + } + } + } +} + +void AvHHud::PostModelRender(char* inModelName) +{ + + // Get our view model name + cl_entity_t* theViewEntity = gEngfuncs.GetViewModel(); + if(theViewEntity) + { + // If this is our view model, that means we can now render our own stuff in screen space without z-buffering on top of everything else + if(theViewEntity->model) + { + if(!strcmp(theViewEntity->model->name, inModelName)) + { + this->RenderNoZBuffering(); + } + } + } + +} + +float AvHHud::GetHUDExperience() const +{ + vec3_t theVUser4; + theVUser4.z = 0.0f; + + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + if(theLocalPlayer) + { + theVUser4 = theLocalPlayer->curstate.vuser4; + } + + if(g_iUser1 == OBS_IN_EYE) + { + cl_entity_t* theEnt = gEngfuncs.GetEntityByIndex(g_iUser2); + if(theEnt) + { + theVUser4 = theEnt->curstate.vuser4; + } + } + + float theExperience = theVUser4.z/kNumericNetworkConstant; + + return theExperience; +} + +int AvHHud::GetHUDExperienceLevel() const +{ + float theExperience = this->GetHUDExperience(); + int theLevel = AvHPlayerUpgrade::GetPlayerLevel(theExperience); + return theLevel; +} + +float AvHHud::GetHUDHandicap() const +{ + float theHandicap = 100.0f; + + AvHTeamNumber theTeamNumber = this->GetHUDTeam(); + switch(theTeamNumber) + { + case TEAM_ONE: + theHandicap = this->GetServerVariableFloat(kvTeam1DamagePercent); + break; + + case TEAM_TWO: + theHandicap = this->GetServerVariableFloat(kvTeam2DamagePercent); + break; + case TEAM_THREE: + theHandicap = this->GetServerVariableFloat(kvTeam3DamagePercent); + break; + case TEAM_FOUR: + theHandicap = this->GetServerVariableFloat(kvTeam4DamagePercent); + break; + } + + return theHandicap; +} + +AvHUser3 AvHHud::GetHUDUser3() const +{ + AvHUser3 theUser3 = AVH_USER3_NONE; + + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + if(theLocalPlayer) + { + theUser3 = (AvHUser3)(theLocalPlayer->curstate.iuser3); + } + + if(g_iUser1 == OBS_IN_EYE) + { + cl_entity_t* theEnt = gEngfuncs.GetEntityByIndex(g_iUser2); + if(theEnt) + { + theUser3 = (AvHUser3)(theEnt->curstate.iuser3); + } + } + + return theUser3; +} + +AvHTeamNumber AvHHud::GetHUDTeam() const +{ + AvHTeamNumber theTeamNumber = TEAM_IND; + + cl_entity_s* thePlayer = this->GetVisiblePlayer(); + if(thePlayer) + { + theTeamNumber = AvHTeamNumber(thePlayer->curstate.team); + } + + return theTeamNumber; +} + +int AvHHud::GetHUDUpgrades() const +{ + int theUpgrades = 0; + + cl_entity_s* thePlayer = this->GetVisiblePlayer(); + if(thePlayer) + { + theUpgrades = thePlayer->curstate.iuser4; + } + + return theUpgrades; +} + +int AvHHud::GetHUDMaxArmor() const +{ + int theHUDUpgrades = this->GetHUDUpgrades(); + AvHUser3 theUser3 = this->GetHUDUser3(); + + int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(theHUDUpgrades, theUser3); + + return theMaxArmor; +} + +int AvHHud::GetHUDMaxHealth() const +{ + int theHUDUpgrades = this->GetHUDUpgrades(); + AvHUser3 theUser3 = this->GetHUDUser3(); + int theHUDExperienceLevel = this->GetHUDExperienceLevel(); + + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theHUDUpgrades, theUser3, theHUDExperienceLevel); + + return theMaxHealth; +} + +void AvHHud::GetPrimaryHudColor(int& outR, int& outG, int& outB, bool inIgnoreUpgrades, bool gammaCorrect) const +{ + if(this->GetIsMarine()) + { + UnpackRGB(outR, outG, outB, RGB_MARINE_BLUE); + } + else + { + // HUD turns green when we're frenzying + //if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_PRIMALSCREAM) && !inIgnoreUpgrades) + //{ + // UnpackRGB(outR, outG, outB, RGB_GREENISH); + //} + //else + //{ + UnpackRGB(outR, outG, outB, RGB_YELLOWISH); + //} + } + + if (gammaCorrect) + { + // Take into account current gamma? + float theGammaSlope = this->GetGammaSlope(); + outR /= theGammaSlope; + outG /= theGammaSlope; + outB /= theGammaSlope; + } + +} + +void AvHHud::HandleFog() +{ + float theFogColor[3]; + theFogColor[0] = this->mFogColor.x; + theFogColor[1] = this->mFogColor.y; + theFogColor[2] = this->mFogColor.z; + + gEngfuncs.pTriAPI->Fog(theFogColor, this->mFogStart, this->mFogEnd, this->mFogActive); +} + +void AvHHud::PreRenderFrame() +{ + bool theRenderForTopDown = this->mInTopDownMode /*&& !this->mIsRenderingSelectionView*/; + + if(!theRenderForTopDown) + { + this->HandleFog(); + } + else + { + this->DrawTopDownBG(); + + // Now draw commander HUD scaled + //void CreatePickingRay( int mousex, int mousey, Vector& outVecPickingRay ) +// static int theCommanderHUDSprite = 0; +// if(!theCommanderHUDSprite) +// { +// theCommanderHUDSprite = Safe_SPR_Load("sprites/.spr"); +// } +// +// if(theCommanderHUDSprite) +// { +// gEngfuncs.pTriAPI->RenderMode( kRenderNormal ); +// gEngfuncs.pTriAPI->CullFace( TRI_NONE ); +// gEngfuncs.pTriAPI->Brightness( 1 ); +// +// if(gEngfuncs.pTriAPI->SpriteTexture((struct model_s *)gEngfuncs.GetSpritePointer(theCommanderHUDSprite), 0)) +// { +// gEngfuncs.pTriAPI->Begin( TRI_QUADS ); +// +// gEngfuncs.pTriAPI->Color4f(1.0f, 1.0f, 1.0f, .5f); +// +// pVector ver; +// +// // Lower left +// const float kMaxMapDimension = 4096; +// //ver.z = -kMaxMapDimension; +// ver.z = theDrawBackgroundHeight; +// +// gEngfuncs.pTriAPI->TexCoord2f(0,1); +// ver.x = -kMaxMapDimension; +// ver.y = -kMaxMapDimension; +// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); +// +// // Upper left +// gEngfuncs.pTriAPI->TexCoord2f(0,0); +// ver.x = -kMaxMapDimension; +// ver.y = kMaxMapDimension; +// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); +// +// // Upper right +// gEngfuncs.pTriAPI->TexCoord2f(1,0); +// ver.x = kMaxMapDimension; +// ver.y = kMaxMapDimension; +// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); +// +// // Lower right +// gEngfuncs.pTriAPI->TexCoord2f(1,1); +// ver.x = kMaxMapDimension; +// ver.y = -kMaxMapDimension; +// gEngfuncs.pTriAPI->Vertex3fv((float*)&ver); +// +// gEngfuncs.pTriAPI->End(); +// } +// } + } + + AvHScriptManager::Instance()->DrawNormal(); +} + +void AvHHud::PostRenderFrame() +{ + // Restore player angles and view position +// v_origin = gPlayerOrigin; +// v_angles = gPlayerAngles; +} + +void AvHHud::DrawActionButtons() +{ + // Look up AvHActionButtons component + AvHActionButtons* theActionButtons = NULL; + if(this->GetManager().GetVGUIComponentNamed(kActionButtonsComponents, theActionButtons)) + { + int theNumCols = theActionButtons->GetNumCols(); + int theNumRows = theActionButtons->GetNumRows(); + + // Iterate through num cols + for(int theCol = 0; theCol < theNumCols; theCol++) + { + // Iterate through num rows + for(int theRow = 0; theRow < theNumRows; theRow++) + { + // Get current ActionButton + ActionButton* theActionButton = theActionButtons->GetActionButtonAtPos(theCol, theRow); + ASSERT(theActionButton); + + // Get message ID for button + AvHMessageID theMessageID = theActionButton->GetMessageID(); + + // Find the group that it belongs to (20, 30, 40, etc.) + int theMessageNumber = (int)theMessageID - (theMessageID % 10); + +// // Load sprite if not loaded +// bool theSpriteLoaded = (this->mActionButtonSprites[theMessageNumber] > 0); +// if(!theSpriteLoaded) +// { +// // Build sprite name for message ID +// char theMessageNumberString[16]; +// sprintf(theMessageNumberString, "%d", (int)theMessageNumber); +// //string theSpriteName = kTechTreeSpriteDirectory + string("/") + kTechTreeSpritePrefix + string(theMessageIDString) + string(".spr"); +// string theSpriteName = kTechTreeSpriteDirectory + string("/") + kTechTreeSpritePrefix + string(theMessageNumberString) + string("s.spr"); +// int theSpriteHandle = Safe_SPR_Load(theSpriteName.c_str()); +// +// // Sprite handle can be 0, as I don't have sprites for all tech yet +// this->mActionButtonSprites[theMessageNumber] = theSpriteHandle; +// } + + // Get pos and size for component + int thePosX, thePosY; + theActionButton->getPos(thePosX, thePosY); + + int theWidth, theHeight; + theActionButton->getSize(theWidth, theHeight); + + // Set sprite frame depending if button is available, active + bool theTechEnabled = theActionButton->GetTechEnabled(); + bool theMouseOver = theActionButton->GetMouseOver(); + bool theCostMet = theActionButton->GetCostMet(); + + // 0 = default + // 1 = highlighted + // 2 = dark + // 3 = red + int theFrame = 2; + + // If it's enabled, or if it's an icon in the "menu navigation" category, allow it to be highlighted + if(theTechEnabled || (theMessageNumber == 80)) + { + if(theCostMet) + { + if(theMouseOver) + { + theFrame = 1; + } + else + { + theFrame = 0; + } + } + else + { + theFrame = 3; + } + } + + // Also highlight menu category headings for open menus, and for active nodes just pressed + if(gCommanderHandler.GetDisplayMenuMessageID() == theMessageID) + { + theFrame = 1; + } + else + { + AvHMessageID theMessageJustActivated = MESSAGE_NULL; + if(gCommanderHandler.GetAndClearTechNodePressed(theMessageJustActivated, false)) + { + if(theMessageJustActivated == theMessageID) + { + if(theTechEnabled) + { + theFrame = 1; + } + } + } + } + + this->DrawTechTreeSprite(theMessageID, thePosX, thePosY, theWidth, theHeight, theFrame); + } + } + } +} + +int AvHHud::GetTechTreeSprite(AvHMessageID inMessageID) +{ + // Find the group that it belongs to (20, 30, 40, etc.) + int theMessageNumber = (int)inMessageID - (inMessageID % 10); + + // Load sprite if not loaded + bool theSpriteLoaded = (this->mActionButtonSprites[theMessageNumber] > 0); + if(!theSpriteLoaded && (theMessageNumber != 0)) + { + // Build sprite name for message ID + char theMessageNumberString[16]; + sprintf(theMessageNumberString, "%d", (int)theMessageNumber); + //string theSpriteName = kTechTreeSpriteDirectory + string("/") + kTechTreeSpritePrefix + string(theMessageIDString) + string(".spr"); + string theSpriteName = kTechTreeSpriteDirectory + string("/") + kTechTreeSpritePrefix + string(theMessageNumberString) + string("s.spr"); + int theSpriteHandle = Safe_SPR_Load(theSpriteName.c_str()); + + // Sprite handle can be 0, as I don't have sprites for all tech yet + this->mActionButtonSprites[theMessageNumber] = theSpriteHandle; + } + + // Fetch sprite handle + int theSpriteHandle = this->mActionButtonSprites[theMessageNumber]; + + return theSpriteHandle; +} + +void AvHHud::DrawTechTreeSprite(AvHMessageID inMessageID, int inPosX, int inPosY, int inWidth, int inHeight, int inFrame) +{ + if(inMessageID != MESSAGE_NULL) + { + // Check for alien sprites + bool theIsAlienSprite = false; + int theSpriteHandle = 0; + int theRenderMode = kRenderTransAlpha; // kRenderNormal + + switch(inMessageID) + { + case ALIEN_BUILD_DEFENSE_CHAMBER: + theIsAlienSprite = true; + inFrame = 0; + break; + case ALIEN_BUILD_OFFENSE_CHAMBER: + theIsAlienSprite = true; + inFrame = 1; + break; + case ALIEN_BUILD_MOVEMENT_CHAMBER: + theIsAlienSprite = true; + inFrame = 2; + break; + case ALIEN_BUILD_SENSORY_CHAMBER: + theIsAlienSprite = true; + inFrame = 3; + break; + case ALIEN_BUILD_RESOURCES: + theIsAlienSprite = true; + inFrame = 4; + break; + case ALIEN_BUILD_HIVE: + theIsAlienSprite = true; + inFrame = 5; + break; + } + + float theStartU = 0.0f; + float theStartV = 0.0f; + float theEndU = 1.0f; + float theEndV = 1.0f; + + if(theIsAlienSprite) + { + theRenderMode = kRenderTransAlpha; + theSpriteHandle = this->mAlienUIUpgradeCategories; + } + else + { + // Fetch sprite handle + theSpriteHandle = this->GetTechTreeSprite(inMessageID); + + int theIndex = inMessageID % 10; + int theXIndex = theIndex % 4; + int theYIndex = (theIndex / 4); + theStartU = theXIndex*.25f; + theStartV = theYIndex*.25f; + theEndU = (theXIndex+1)*.25f; + theEndV = (theYIndex+1)*.25f; + } + + if(theSpriteHandle > 0) + { + // Draw sprite scaled here + AvHSpriteSetRenderMode(theRenderMode); + AvHSpriteDraw(theSpriteHandle, inFrame, inPosX, inPosY, inPosX + inWidth, inPosY + inHeight, theStartU, theStartV, theEndU, theEndV); + } + } +} + +void AvHHud::DrawHotgroups() +{ + AvHMessageID theHotgroups[kNumHotkeyGroups] = {GROUP_SELECT_1, GROUP_SELECT_2, GROUP_SELECT_3, GROUP_SELECT_4, GROUP_SELECT_5}; + + // Run through list of hotgroups, drawing them if they exist + for(int i = 0; i < kNumHotkeyGroups; i++) + { + EntityListType& theHotgroup = this->mGroups[i]; + + if(theHotgroup.size() > 0) + { + // Get message ID to draw + AvHUser3 theGroupTypeUser3 = this->mGroupTypes[i]; + + // Default is marine face + AvHMessageID theHotgroupIcon = MENU_SOLDIER_FACE; + AvHSHUUser3ToMessageID(theGroupTypeUser3, theHotgroupIcon); + + // Build component name ("PendingImpulseXPanel", where X is the impulse) + AvHMessageID theHotgroupMessageID = theHotgroups[i]; + char theComponentName[256]; + sprintf(theComponentName, kPendingImpulseSpecifier, (int)theHotgroupMessageID); + + AvHTechImpulsePanel* theTechImpulsePanel = NULL; + if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) + { + int thePosX, thePosY; + theTechImpulsePanel->getPos(thePosX, thePosY); + + int theWidth, theHeight; + theTechImpulsePanel->getSize(theWidth, theHeight); + + // Highlight if mouse is in region or if it's our current selection + bool theIsHighlighted = this->GetIsMouseInRegion(thePosX, thePosY, theWidth, theHeight) || (this->mSelected == theHotgroup); + int theFrame = theIsHighlighted ? 1 : 0; + + int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; + if((this->mGroupAlerts[i] == ALERT_UNDER_ATTACK) && (theSecondOfLastUpdate % 2)) + { + // Play blinking red frame when being attacked + theFrame = 3; + } + + this->DrawTechTreeSprite(theHotgroupIcon, thePosX, thePosY, theWidth, theHeight, theFrame); + + theTechImpulsePanel->SetVisualNumber((int)theHotgroup.size()); + } + } + } +} + +// Draw pending requests +void AvHHud::DrawPendingRequests() +{ + // Run through list of requests and delete any entries with + PendingRequestListType::iterator theIterator; + for(theIterator = this->mPendingRequests.begin(); theIterator != this->mPendingRequests.end(); /* nothing */) + { + if(theIterator->second == 0) + { + PendingRequestListType::iterator theSavedIter = theIterator; + theSavedIter++; + this->mPendingRequests.erase(theIterator); + theIterator = theSavedIter; + } + else + { + theIterator++; + } + } + + int theNumRequestsToDraw = (int)this->mPendingRequests.size(); + int theCounter = 0; + for(theIterator = this->mPendingRequests.begin(); theIterator != this->mPendingRequests.end(); theIterator++) + { + // Draw each one above the minimap + AvHMessageID theMessageID = theIterator->first; + int theNumRequests = theIterator->second; + + // Build component name ("PendingImpulseXPanel", where X is the impulse) + char theComponentName[256]; + sprintf(theComponentName, kPendingImpulseSpecifier, (int)theMessageID); + + AvHTechImpulsePanel* theTechImpulsePanel = NULL; + if(this->GetManager().GetVGUIComponentNamed(theComponentName, theTechImpulsePanel)) + { + // Get size of panel and draw tech sprite there + int thePosX, thePosY; + theTechImpulsePanel->getPos(thePosX, thePosY); + + int theWidth, theHeight; + theTechImpulsePanel->getSize(theWidth, theHeight); + + AvHMessageID theMessageToDraw = theMessageID; + switch(theMessageID) + { + case COMMANDER_NEXTIDLE: + theMessageToDraw = MENU_SOLDIER_FACE; + break; + case COMMANDER_NEXTAMMO: + theMessageToDraw = BUILD_AMMO; + break; + case COMMANDER_NEXTHEALTH: + theMessageToDraw = BUILD_HEALTH; + break; + } + + int theFrame = this->GetIsMouseInRegion(thePosX, thePosY, theWidth, theHeight) ? 1 : 0; + this->DrawTechTreeSprite(theMessageToDraw, thePosX, thePosY, theWidth, theHeight, theFrame); + + theTechImpulsePanel->SetVisualNumber(theNumRequests); + if(theNumRequests > 0) + { + theTechImpulsePanel->setVisible(true); + } + else + { + theTechImpulsePanel->setVisible(false); + } + + //this->DrawHUDNumber(thePosX + kTileWidth, thePosY + kTileHeight, DHN_2DIGITS, theNumRequests); + } + + // Draw indicator on sprite showing how many requests there are + theCounter++; + } +} + + +// : 0000988 +void AvHHud::DrawBuildHealthEffectsForEntity(int inEntityIndex, float inAlpha) +// : +{ + if ( this->GetHUDPlayMode() == PLAYMODE_READYROOM ) + return; + + // Get entity + int theUser3 = 0; + int theUser4 = 0; + float theFuser1 = 0.0f; + int theEntityTeam = 0; + bool theIsOnOurTeam=false; + vec3_t theOrigin; + vec3_t theMins; + vec3_t theMaxs; + bool theContinue = false; + float theHealthPercentage = 0.0f; + double theDistanceToEntity = 0; + + + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(inEntityIndex); + bool theEntityIsPlayer = ((inEntityIndex > 0) && (inEntityIndex <= gEngfuncs.GetMaxClients())); + + if(theEntity) + { + theUser3 = theEntity->curstate.iuser3; + theUser4 = theEntity->curstate.iuser4; + theFuser1 = theEntity->curstate.fuser1; + theEntityTeam = theEntity->curstate.team; + theIsOnOurTeam = (theEntityTeam == (int)this->GetHUDTeam()); + //theOrigin = theEntity->curstate.origin; + theOrigin = AvHSHUGetRealLocation(theEntity->origin, theEntity->curstate.mins, theEntity->curstate.maxs); + if(theEntity->player) + { + // Subtract half-height to be at feet + float theHeight = theEntity->curstate.maxs[2] - theEntity->curstate.mins[2]; + theOrigin[2] -= theHeight/2.0f; + } + theDistanceToEntity = VectorDistance(gPredictedPlayerOrigin, theOrigin); + + theMins = theEntity->curstate.mins; + theMaxs = theEntity->curstate.maxs; + theHealthPercentage = theEntity->curstate.fuser2/kNormalizationNetworkFactor; + // : 991 transmit armour and health for marines + if ( GetIsMarine() && theEntityIsPlayer && theIsOnOurTeam ) { + int tmpPercent=theEntity->curstate.fuser2; + if ( GetInTopDownMode() ) { + theHealthPercentage = (float)(tmpPercent&0x7F)/100; + } + else { + theHealthPercentage = (float)(tmpPercent >> 7)/100; + } + } + // : + theContinue = true; + + // 991: + // Do not display health rings for enemy players unless we are commander + if ( !GetInTopDownMode() && theEntityIsPlayer && !theIsOnOurTeam ) { + theContinue=false; + } + } + + // Get local player + cl_entity_s* theLocalPlayer = gEngfuncs.GetLocalPlayer(); + + bool theDrewBuildInProgress = false; + float theOversizeScalar = 1.0f; + + if(AvHSHUGetDrawRingsForUser3((AvHUser3)theUser3, theOversizeScalar)) + { + if(theContinue && theLocalPlayer) + { + const kDrawEnemyBuildingDistance = 200; + + // Draw effects if we are in top-down mode OR + if( this->GetInTopDownMode() || + + // It's an unfriendly building that's very close OR + (!theEntityIsPlayer && (theDistanceToEntity < kDrawEnemyBuildingDistance)) || + + // It's a friendly entity and we're a builder OR + (theIsOnOurTeam && (this->GetHUDUser3() == AVH_USER3_ALIEN_PLAYER2)) || + + // welder/healing spray is selected + (this->mCurrentWeaponID == 18 || this->mCurrentWeaponID == 27) + + ) + { + + // If they're not on opposite teams + //if((theEntityTeam == theLocalPlayer->curstate.team) || (theEntityTeam == 0)) + //{ + // Get entity build/research state + bool theIsBuilding = false; + bool theIsResearching = false; + float theNormalizedPercentage = 0.0f; + + // Calculate percentage full + AvHSHUGetBuildResearchState(theUser3, theUser4, theFuser1, theIsBuilding, theIsResearching, theNormalizedPercentage); + + bool theDrawHealth = true; + int theSpriteToUse = this->GetIsAlien() ? this->mAlienHealthSprite : this->mMarineHealthSprite; + bool theDrawAsRecyling = (GetHasUpgrade(theUser4, MASK_RECYCLING) && theIsOnOurTeam); + + if((theIsOnOurTeam && theIsBuilding && (GetHasUpgrade(theUser4, MASK_BUILDABLE))) || theDrawAsRecyling) + { + theSpriteToUse = this->GetIsAlien() ? this->mAlienBuildSprite : this->mMarineBuildSprite; + theDrawHealth = false; + + // Draw progress inverted, as we're "undoing" the structure + if(theDrawAsRecyling) + { + theNormalizedPercentage = (1.0f - theNormalizedPercentage); + } + } + else + { + theNormalizedPercentage = theHealthPercentage; + } + + if(theDrawHealth || (theEntityTeam == theLocalPlayer->curstate.team)) + { + // Draw it + int theNumFrames = SPR_Frames(theSpriteToUse); + ASSERT(theNumFrames > 0); + + int theCurrentFrame; + if ((theHealthPercentage < 1.0f) || theDrawAsRecyling) + { + theCurrentFrame = theNormalizedPercentage * (theNumFrames - 1); + // : 0000893 + // quick hack to eliminate 1 red bar shown for dead players + if (theEntity->player) + theCurrentFrame = min(max(0, theCurrentFrame), theNumFrames - 1); + else + theCurrentFrame = min(max(1, theCurrentFrame), theNumFrames - 1); + // : + } + else + { + theCurrentFrame = theNumFrames - 1; + } + + // Get real position of entity + vec3_t thePosition; + thePosition = AvHSHUGetRealLocation(theOrigin, theMins, theMaxs); + + float theMaxX = theMaxs[0] - theMins[0]; + float theMaxY = theMaxs[1] - theMins[1]; + + float theRadius = max(theMaxX, theMaxY)*theOversizeScalar; + + // Bring position off ground just a little to prevent z-fighting + thePosition.z += kZFightingOffset; + + //GetHasUpgrade(this->GetHUDUpgrades(), MASK_PARASITED) + + DrawSpriteOnGroundAtPoint(thePosition, theRadius, theSpriteToUse, kRenderTransAdd, theCurrentFrame, inAlpha); + + theDrewBuildInProgress = true; + } + } + } + } +} + +void AvHHud::DrawHUDNumber(int inX, int inY, int inFlags, int inNumber) +{ + // Draw number on HUD, in our HUD color + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, false, false); + + int theGammaSlope = this->GetGammaSlope(); + theR /= theGammaSlope; + theG /= theGammaSlope; + theB /= theGammaSlope; + + this->DrawHudNumber(inX, inY, inFlags, inNumber, theR, theG, theB); +} + +void AvHHud::DrawSelectionAndBuildEffects() +{ +// : 0000988 + list theSelectedList; +// : + + // Draw build effects + for(SelectionListType::iterator theSelectIter = this->mSelectionEffects.begin(); theSelectIter != this->mSelectionEffects.end(); theSelectIter++) + { + // Draw selection effect around the entity + int theEntIndex = theSelectIter->mEntIndex; + this->DrawBuildHealthEffectsForEntity(theEntIndex); + // : 0000988 + theSelectedList.push_back(theEntIndex); + // : + } + + bool theDrawBuildingEffect = false; + + for(EntityListType::iterator theBuildingIter = this->mBuildingEffectsEntityList.begin(); theBuildingIter != this->mBuildingEffectsEntityList.end(); theBuildingIter++) + { + int theEntIndex = *theBuildingIter; + this->DrawBuildHealthEffectsForEntity(theEntIndex); + // : 0000988 + theSelectedList.push_back(theEntIndex); + // : + } + + // : 0000988 & 0000991 + bool maintanceWeaponSelected = (this->mCurrentWeaponID == 18 || this->mCurrentWeaponID == 27); + bool isCommander = this->GetInTopDownMode(); + if (isCommander || maintanceWeaponSelected) { + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + gEngfuncs.pEventAPI->EV_PushPMStates(); + gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1); + + int localPlayerIndex = gEngfuncs.GetLocalPlayer()->index; + int currentteam = this->GetHUDTeam(); + int maxclients = gEngfuncs.GetMaxClients(); + + + int theNumEnts = pmove->numphysent; + physent_t* theEntity = NULL; + for (int i = 0; i < theNumEnts; i++) + { + theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(i); + if(theEntity) + { + if (localPlayerIndex != theEntity->info) + { + int theEntityIndex = theEntity->info; + list::iterator theSelectedIterator = find(theSelectedList.begin(), theSelectedList.end(), theEntityIndex); + if (theSelectedIterator == theSelectedList.end()) + { + bool theIsPlayer = ((theEntityIndex >= 1) && (theEntityIndex <= maxclients)); + bool theSameTeam = (theEntity->team == currentteam ); + if (isCommander && (theIsPlayer || theSameTeam)) + { + this->DrawBuildHealthEffectsForEntity(theEntityIndex, 0.2); + } + else if (maintanceWeaponSelected && theSameTeam && !theIsPlayer) + { + if (AvHTraceLineAgainstWorld(gEngfuncs.GetLocalPlayer()->origin, theEntity->origin) == 1.0f) + { + this->DrawBuildHealthEffectsForEntity(theEntityIndex, 0.3); + } + } + } + } + } + } + gEngfuncs.pEventAPI->EV_PopPMStates(); + } + // : +} + + +void AvHHud::Render() +{ + if (!IEngineStudio.IsHardware()) + { + + // Don't show anything in software mode. + + const char* theMessage = "Software mode is not supported by Natural Selection"; + + int theWidth; + int theHeight; + + float gammaScale = 1.0f / GetGammaSlope(); + + gEngfuncs.pfnDrawSetTextColor(0, gammaScale, 0); + gEngfuncs.pfnDrawConsoleStringLen(theMessage, &theWidth, &theHeight); + gEngfuncs.pfnDrawConsoleString((ScreenWidth() - theWidth) / 2, (ScreenHeight() - theHeight) / 2, (char*)theMessage); + + return; + + } + + if (m_Spectator.IsInOverviewMode()) + { + AvHSpriteSetViewport(mSpecialViewport[0], mSpecialViewport[1], + mSpecialViewport[0] + mSpecialViewport[2], mSpecialViewport[1] + mSpecialViewport[3]); + } + + AvHSpriteBeginFrame(); + + if (mSteamUIActive) + { + // Use the old 2D method if we're in the Steam UI. + AvHSpriteEnableVGUI(false); + } + else + { + AvHSpriteEnableVGUI(true); + AvHSpriteSetVGUIOffset(0, 0); + } + +#ifdef DEBUG + if ( CVAR_GET_FLOAT( "hud_hideview" ) != 0 ) + { + + // Black out the screen + + int sprite = Safe_SPR_Load(kWhiteSprite); + AvHSpriteSetColor(0, 0, 0, 1); + AvHSpriteDraw(sprite, 0, 0, 0, ScreenWidth(), ScreenHeight(), 0, 0, 1, 1); + AvHSpriteSetColor(1, 1, 1, 1); + + } +#endif + + // Don't draw the HUD while being digested. + + if (GetIsBeingDigested()) + { + + if (this->mDigestingSprite) + { + AvHSpriteSetColor(1,1,1); + AvHSpriteSetRenderMode(kRenderNormal); + DrawWarpedOverlaySprite(mDigestingSprite, 4, 3, .02, .02, .3, .15); + } + + RenderProgressBar(kProgressBarSprite); + } + else + { + + if (this->GetHUDPlayMode() == PLAYMODE_PLAYING || + this->GetHUDPlayMode() == PLAYMODE_READYROOM) + { + + if (!mSteamUIActive) + { + this->DrawReticleInfo(); + this->DrawPlayerNames(); + this->DrawToolTips(); + this->DrawCenterText(); + } + + } + + if (this->GetHUDPlayMode() == PLAYMODE_PLAYING) + { + + RenderCommonUI(); + + if (GetIsMarine()) + { + if (GetInTopDownMode()) + { + RenderCommanderUI(); + } + else + { + RenderMarineUI(); + } + } + else if (GetIsAlien()) + { + RenderAlienUI(); + } + + RenderProgressBar(kProgressBarSprite); + } + + } + + AvHSpriteEndFrame(); + AvHSpriteClearViewport(); + +} + +void AvHHud::RenderCommonUI() +{ + static bool speedMeasured=false; + if (!mSteamUIActive) + { + + if (gHUD.GetServerVariableFloat("sv_cheats") != 0 ) { + static int maxSpeed=0, maxGroundSpeed=0, maxClimb=0, maxDive=0; + if ( CVAR_GET_FLOAT("cl_showspeed") != 0) { + + // Draw the speedometer. + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, true, false); + + extern playermove_s* pmove; + + char buffer[1024]; + + maxClimb=max(maxClimb, (int)pmove->velocity[2]); + maxDive=min(maxDive, (int)pmove->velocity[2]); + + int speed=(int)Length(pmove->velocity); + + maxSpeed=max(speed, maxSpeed); + sprintf(buffer, "Speed = %d (%d) %d/%d", speed, maxSpeed, maxClimb, maxDive); + mFont.DrawString(10, 10, buffer, theR, theG, theB); + + float theGroundSpeed = sqrtf(pmove->velocity[0] * pmove->velocity[0] + pmove->velocity[1] * pmove->velocity[1]); + maxGroundSpeed=max(theGroundSpeed, maxGroundSpeed); + sprintf(buffer, "Ground speed = %d (%d)", (int)theGroundSpeed, maxGroundSpeed); + mFont.DrawString(10, 12 + mFont.GetStringHeight(), buffer, theR, theG, theB); + speedMeasured = true; + } + else if ( speedMeasured == true ) { + char msg[256]; + sprintf(msg, "Current Speed(%d)\tCurrent Ground Speed(%d) Max Speed(%d)\t Max Ground Speed (%d)\tMax Climb (%d)\tMax Dive(%d)\n", + (int)Length(pmove->velocity), (int)sqrtf(pmove->velocity[0] * pmove->velocity[0] + pmove->velocity[1] * pmove->velocity[1]), + maxSpeed, maxGroundSpeed, maxClimb, maxDive); + ConsolePrint(msg); + maxSpeed=0, maxGroundSpeed=0, maxClimb=0, maxDive=0; + speedMeasured = false; + } + } + + DrawInfoLocationText(); + DrawHUDStructureNotification(); + + this->DrawOrders(); + this->DrawHelpIcons(); + // : 0000971 + this->DrawTeammateOrders(); + // : 0000992 + this->DrawDisplayOrder(); + // : + + if (this->GetIsCombatMode()) + { + // If we're in combat mode, draw our rank + const float kTitleYInset = (kPlayerStatusVerticalInset+5*kPlayerStatusStatusSpacing); + + string theTitleText = this->GetRankTitle(true); + + char theCharArray[512]; + sprintf(theCharArray, theTitleText.c_str()); + + // Draw it + + int theX = mViewport[0] + kPlayerStatusHorizontalCenteredInset*(mViewport[2] - mViewport[0]); + int theY = mViewport[1] + mViewport[3] - (1-kTitleYInset)*ScreenHeight(); + + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, true, false); + + this->DrawHudStringCentered(theX, theY, ScreenWidth(), theCharArray, theR, theG, theB); + } + + // If we're parasited, draw a message as such + if (this->GetIsAlive()) + { + string theText; + char theCharArray[512]; + + int theR, theG, theB; + //UnpackRGB(theR, theG, theB, RGB_YELLOWISH); + this->GetPrimaryHudColor(theR, theG, theB, true, false); + + int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; + + // Draw blinking primal scream message + if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_BUFFED) && (theSecondOfLastUpdate % 2)) + { + if(this->GetIsAlien()) + { + LocalizeString(kPrimalScreamed, theText); + } + else if(this->GetIsMarine()) + { + LocalizeString(kCatalysted, theText); + } + + // Draw it + sprintf(theCharArray, "%s", theText.c_str()); + this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), kPlayerStatusVerticalInset*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); + } + + // Draw parasited indicator + if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_PARASITED)) + { + LocalizeString(kParasited, theText); + sprintf(theCharArray, "%s", theText.c_str()); + + // Draw it + this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); + } + + // Draw umbraed indicator + if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_UMBRA)) + { + LocalizeString(kUmbraed, theText); + sprintf(theCharArray, "%s", theText.c_str()); + + // Draw it + this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+2*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); + } + + // Draw blinking "webbed" message + if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_ENSNARED) && (theSecondOfLastUpdate % 2)) + { + LocalizeString(kWebbed, theText); + sprintf(theCharArray, "%s", theText.c_str()); + + // Draw it + this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+3*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); + } + + // Draw "stunned" message (it's so fast, try not blinking it) + if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_PLAYER_STUNNED) /*&& (theSecondOfLastUpdate % 2)*/) + { + LocalizeString(kStunned, theText); + sprintf(theCharArray, "%s", theText.c_str()); + + // Draw it + this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+4*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); + } + + // Draw "being digested" or "digesting" message + if(this->GetIsBeingDigested() && (theSecondOfLastUpdate % 2)) + { + LocalizeString(kDigested, theText); + sprintf(theCharArray, "%s", theText.c_str()); + + // Draw it + this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+5*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); + } + else if(this->GetIsDigesting() && (theSecondOfLastUpdate % 2)) + { + // Look up player in progress entity + if((this->mProgressBarEntityIndex >= 1) && (this->mProgressBarEntityIndex <= gEngfuncs.GetMaxClients())) + { + hud_player_info_t thePlayerInfo; + gEngfuncs.pfnGetPlayerInfo(this->mProgressBarEntityIndex, &thePlayerInfo); + + char* thePlayerName = thePlayerInfo.name; + if(thePlayerName) + { + LocalizeString(kDigestingPlayer, theText); + sprintf(theCharArray, theText.c_str(), thePlayerName); + } + } + + // Draw it + this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+5*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); + } + + // If we are in a squad, draw it on our HUD + if(this->mCurrentSquad > 0) + { + string theSquadIndicator; + if(LocalizeString(kSquadIndicator, theSquadIndicator)) + { + sprintf(theCharArray, theSquadIndicator.c_str(), this->mCurrentSquad); + + // Draw it + this->DrawHudStringCentered(kPlayerStatusHorizontalCenteredInset*ScreenWidth(), (kPlayerStatusVerticalInset+6*kPlayerStatusStatusSpacing)*ScreenHeight(), ScreenWidth(), theCharArray, theR, theG, theB); + } + } + } + + // Draw the numerical effects. + + if(this->GetIsAlive()) + { + for(NumericalInfoEffectListType::iterator theIter = this->mNumericalInfoEffects.begin(); theIter != this->mNumericalInfoEffects.end(); theIter++) + { + // Draw it + Vector theScreenPos; + float theWorldPosition[3]; + theIter->GetPosition(theWorldPosition); + if(AvHCUWorldToScreen(theWorldPosition, (float*)&theScreenPos)) + { + float theNormX = theScreenPos.x/ScreenWidth(); + float theNormY = theScreenPos.y/ScreenHeight(); + if(!this->GetInTopDownMode() || !this->GetIsRegionBlockedByUI(theNormX, theNormY)) + { + float theNumber = theIter->GetNumber(); + string thePrependString("+"); + + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, true, false); + if(theNumber < 0) + { + // Red, minus for negatives + theR = 255; + theG = 0; + theB = 0; + + // The "-" is prepended by MakeStringFromInt when negative + thePrependString = string(""); + } + + const char* thePostPendStringToTranslate = NULL; + string thePostPendString; + + switch(theIter->GetEventType()) + { + case kNumericalInfoResourcesEvent: + thePostPendStringToTranslate = kNumericalEventResources; + break; + case kNumericalInfoHealthEvent: + thePostPendStringToTranslate = kNumericalEventHealth; + break; + case kNumericalInfoResourcesDonatedEvent: + thePostPendStringToTranslate = kNumericalEventResourcesDonated; + break; + case kNumericalInfoAmmoEvent: + thePostPendStringToTranslate = kNumericalEventAmmo; + break; + } + + if(thePostPendStringToTranslate) + { + LocalizeString(thePostPendStringToTranslate, thePostPendString); + } + + // Don't draw 0, so we can have other messages + string theNumberString; + if(theNumber != 0) + { + theNumberString = thePrependString + MakeStringFromFloat(theNumber) + " "; + } + + string theDisplayString = theNumberString + thePostPendString; + + char theCharBuffer[512]; + sprintf(theCharBuffer, "%s", theDisplayString.c_str()); + + this->DrawHudStringCentered(theScreenPos.x, theScreenPos.y, ScreenWidth(), theCharBuffer, theR, theG, theB); + } + } + } + } + + } + + // Draw the combat HUD. + if (this->GetIsCombatMode()) + { + // Now draw our current experience level, so people know how close they are to the next level + // Load alien resource and energy sprites + string theSpriteName = UINameToSprite(kCombatExperienceSprite, ScreenWidth()); + int theExperienceSprite = Safe_SPR_Load(theSpriteName.c_str()); + + if(theExperienceSprite) + { + const float kNormalizedWidth = .1f; + const float kNormalizedYInset = .96f; + const float kNormalizedHeight = .025f; + const int kBaseIndex = this->GetIsMarine() ? 2 : 0; + + // Draw full background + const int kXStart = mViewport[0] + (.5f - kNormalizedWidth/2.0f)*(mViewport[2] - mViewport[0]); + const int kYStart = mViewport[1] + mViewport[3] - (1 - kNormalizedYInset)*ScreenHeight(); + + + AvHSpriteSetColor(1,1,1); + AvHSpriteSetRenderMode(kRenderTransAlpha); + + AvHSpriteDraw(theExperienceSprite, kBaseIndex + 1, kXStart, kYStart, kXStart + kNormalizedWidth*ScreenWidth(), kYStart + kNormalizedHeight*ScreenHeight(), 0, 0, 1, 1); + + // Draw overlay showing amount of experience + float thePercentToNextLevel = AvHPlayerUpgrade::GetPercentToNextLevel(this->GetHUDExperience()); + if((thePercentToNextLevel >= 0.0f) && (thePercentToNextLevel <= 1.0f)) + { + AvHSpriteDraw(theExperienceSprite, kBaseIndex, kXStart, kYStart, kXStart + thePercentToNextLevel*kNormalizedWidth*ScreenWidth(), kYStart + kNormalizedHeight*ScreenHeight(), 0, 0, thePercentToNextLevel, 1.0f); + } + } + + if(this->GetIsAlive(false)) + { + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, false, false); + this->mCombatUpgradeMenu.SetR(theR); + this->mCombatUpgradeMenu.SetG(theG); + this->mCombatUpgradeMenu.SetB(theB); + + this->mCombatUpgradeMenu.Draw(); + } + } + + // Draw top down help + if(this->GetInTopDownMode()) + { + this->mTopDownActionButtonHelp.Draw(); + } + +} + +void AvHHud::RenderProgressBar(char *spriteName) +{ + // Draw the progress bars + const float progressBarStayTime = 0.2f; + if (this->mProgressBarLastDrawn + progressBarStayTime > this->GetTimeOfLastUpdate()) + { + HSPRITE currentSprite=0; + if ( spriteName && ( strcmp(spriteName, kExperienceBarSprite) == 0 ) ) { + currentSprite=this->mExperienceBarSprite; + } + if ( spriteName && ( strcmp(spriteName, kProgressBarSprite) == 0 ) ) { + currentSprite=this->mProgressBarSprite; + } + if (currentSprite) + { + const float kNormalizedWidth = .1f; + const float kNormalizedYInset = .89f; + const float kNormalizedHeight = .025f; + + // Draw full background + const int kXStart = mViewport[0] + (.5f - kNormalizedWidth/2.0f)*(mViewport[2] - mViewport[0]); + const int kYStart = mViewport[1] + mViewport[3] - (1 - kNormalizedYInset)*ScreenHeight(); + + AvHSpriteSetColor(1,1,1); + AvHSpriteSetRenderMode(kRenderTransAlpha); + + AvHSpriteDraw(currentSprite, this->mProgressBarDrawframe + 1, kXStart, kYStart, kXStart + kNormalizedWidth*ScreenWidth(), kYStart + kNormalizedHeight*ScreenHeight(), 0, 0, 1, 1); + + // Draw overlay showing progress + float theProgress = this->mProgressBarStatus; + if((theProgress >= 0.0f) && (theProgress <= 1.0f)) + { + AvHSpriteDraw(currentSprite, this->mProgressBarDrawframe, kXStart, kYStart, kXStart + theProgress*kNormalizedWidth*ScreenWidth(), kYStart + kNormalizedHeight*ScreenHeight(), 0, 0, theProgress, 1.0f); + } + } + } +} + +void AvHHud::RenderMiniMap(int inX, int inY, int inWidth, int inHeight) +{ + + AvHOverviewMap& theOverviewMap = gHUD.GetOverviewMap(); + + float hudMinimap=CVAR_GET_FLOAT(kvHudMapZoom); + hudMinimap=min(3, max(1, hudMinimap)); + + float zoomScale=(3.0f-hudMinimap); + + AvHOverviewMap::DrawInfo theDrawInfo; + + theDrawInfo.mX = inX; + theDrawInfo.mY = inY; + theDrawInfo.mWidth = inWidth; + theDrawInfo.mHeight = inHeight; + theDrawInfo.mFullScreen = false; + + float worldViewWidth = 800 + 400.0f*zoomScale; + + theDrawInfo.mZoomScale = 1-(0.25f * zoomScale ); + + + float aspectRatio = (float)(theDrawInfo.mHeight) / theDrawInfo.mWidth; + + float thePlayerX; + float thePlayerY; + + theOverviewMap.GetWorldPosition(thePlayerX, thePlayerY); + + theDrawInfo.mViewWorldMinX = thePlayerX - worldViewWidth; + theDrawInfo.mViewWorldMinY = thePlayerY - worldViewWidth * aspectRatio; + theDrawInfo.mViewWorldMaxX = thePlayerX + worldViewWidth; + theDrawInfo.mViewWorldMaxY = thePlayerY + worldViewWidth * aspectRatio; + + theOverviewMap.Draw(theDrawInfo); + +} + +void AvHHud::RenderMarineUI() +{ + + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, false, false); + + + if (mMarineTopSprite && (this->mMapMode == MAP_MODE_NS)) + { + + float theX; + float theY; + float theWidth = .75f*ScreenWidth(); + float theHeight = ((float)256/768)*ScreenHeight(); + + AvHSpriteSetRenderMode(kRenderTransAdd); + AvHSpriteSetColor(1,1,1); + int hudMinimap=CVAR_GET_FLOAT(kvHudMapZoom); + for ( int i=0; i<3; i++ ) { + float width=theWidth/3.0f; + int frame=i; + if ( hudMinimap == 0 && i == 2 ) frame=i+1; + + AvHSpriteDraw(mMarineTopSprite, frame, mViewport[2] - width*(3-i) + mViewport[0], + mViewport[1], mViewport[2] - width*(2-i) + mViewport[0], mViewport[1] + theHeight, 0, 0, 1, 1); + + } + + + // Draw the minimap. + if ( hudMinimap > 0 ) { + int theMiniMapX = mViewport[2] - 0.21f * ScreenWidth() + mViewport[0]; + int theMiniMapY = mViewport[1] + 0.013 * ScreenHeight(); + int theMiniMapWidth = 0.200 * ScreenWidth(); + int theMiniMapHeight = 0.202 * ScreenHeight(); + + RenderMiniMap(theMiniMapX, theMiniMapY, theMiniMapWidth, theMiniMapHeight); + } + // Draw the resource label. + + theHeight = ScreenHeight() * 0.038; + + std::string theResourceText; + char theResourceBuffer[64]; + + LocalizeString(kMarineResourcePrefix, theResourceText); + _snprintf(theResourceBuffer, 64, "%s %d", theResourceText.c_str(), this->mVisualResources); + + theX = mViewport[0] + 0.3 * ScreenWidth(); + theY = mViewport[1] + 0.007 * ScreenHeight() + (theHeight - GetSmallFont().GetStringHeight()) / 2; + + GetSmallFont().DrawString(theX, theY, theResourceBuffer, theR, theG, theB); + + // Draw the commander status label. + + std::string theCommanderName; + bool theCommanderNameVisible = true; + + if (!GetCommanderLabelText(theCommanderName)) + { + // Blink if there is no commander. + if ((int)this->mTimeOfLastUpdate % 2) + { + theCommanderNameVisible = false; + } + } + + if (theCommanderNameVisible) + { + + theX = mViewport[0] + 0.50 * ScreenWidth(); + theY = mViewport[1] + 0.006 * ScreenHeight() + (theHeight - GetSmallFont().GetStringHeight()) / 2; + + GetSmallFont().DrawString(theX, theY, theCommanderName.c_str(), theR, theG, theB); + + } + + } + + AvHSpriteEnableClippingRect(false); + + // TODO: Draw various marine upgrades (assumes 256x256 sprite, with each upgrade being 64x64, listed right to left, top to bottom, armor upgrades first, + // weapon upgrades next, then motion-tracking, then jetpacks, then exoskeleton) + + // Do we have a jetpack? + if(GetHasUpgrade(this->GetHUDUpgrades(), MASK_UPGRADE_7)) + { + // Draw jetpack energy indicator + if(this->mMarineUIJetpackSprite) + { + float theFactor = 1 - pmove->fuser3/kNormalizationNetworkFactor; + int theFrame = 0; + + string theOutputMessage; + if(LocalizeString(kJetpackEnergyHUDText, theOutputMessage)) + { + + float theX = .9f*ScreenWidth(); + float theY = .8f*ScreenHeight(); + float theWidth = .02f*ScreenWidth(); + float theHeight = .1f*ScreenHeight(); + + AvHSpriteSetRenderMode(kRenderTransAdd); + AvHSpriteSetColor(1,1,1); + + AvHSpriteDraw(mMarineUIJetpackSprite, 0, theX, theY, theX + theWidth, theY + theHeight * theFactor, 0, 0, 1, theFactor); + AvHSpriteDraw(mMarineUIJetpackSprite, 1, theX, theY + theHeight * theFactor, theX + theWidth, theY + theHeight, 0, theFactor, 1, 1); + + } + } + } + + + const int kNumUpgradesToDraw = 4; + AvHUpgradeMask kUpgradeToMaskTable[kNumUpgradesToDraw] = {MASK_UPGRADE_4, MASK_UPGRADE_1, MASK_UPGRADE_8, MASK_UPGRADE_7}; + + const float kAspectRatio = ScreenWidth()/ScreenHeight(); + + const float kNormalizedSpacing = 0.01f; + const int kBaseRightOffset = kNormalizedSpacing*ScreenWidth(); + const int kVerticalUpgradeSpacing = kNormalizedSpacing*kAspectRatio*ScreenHeight(); + int theUpgradeVar = this->GetHUDUpgrades(); + const int kUpgradeFrame = 0; + const float kUpgradeSize = 0.05; + int theUpgradeWidth = kUpgradeSize*ScreenWidth(); + int theUpgradeHeight = kUpgradeSize*kAspectRatio*ScreenHeight(); + + for(int i = 0; i < kNumUpgradesToDraw; i++) + { + AvHUpgradeMask theUpgradeMask = kUpgradeToMaskTable[i]; + + // Draw highest ammo upgrade level + if(theUpgradeMask == MASK_UPGRADE_4) + { + if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_5)) + { + theUpgradeMask = MASK_UPGRADE_5; + + if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_6)) + { + theUpgradeMask = MASK_UPGRADE_6; + } + } + } + + // Draw highest armor level + if(theUpgradeMask == MASK_UPGRADE_1) + { + if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_2)) + { + theUpgradeMask = MASK_UPGRADE_2; + + if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_3)) + { + theUpgradeMask = MASK_UPGRADE_3; + } + } + } + + // Draw heavy armor instead of jetpacks if they have it + if(theUpgradeMask == MASK_UPGRADE_7) + { + if(GetHasUpgrade(theUpgradeVar, MASK_UPGRADE_13)) + { + theUpgradeMask = MASK_UPGRADE_13; + } + } + + if(GetHasUpgrade(theUpgradeVar, theUpgradeMask)) + { + int theFrame = 0; + + switch(theUpgradeMask) + { + case MASK_UPGRADE_4: + theFrame = 0; + break; + case MASK_UPGRADE_5: + theFrame = 1; + break; + case MASK_UPGRADE_6: + theFrame = 2; + break; + case MASK_UPGRADE_1: + theFrame = 3; + break; + case MASK_UPGRADE_2: + theFrame = 4; + break; + case MASK_UPGRADE_3: + theFrame = 5; + break; + case MASK_UPGRADE_8: + theFrame = 6; + break; + case MASK_UPGRADE_7: + theFrame = 7; + break; + case MASK_UPGRADE_13: + theFrame = 8; + break; + } + + float theStartU = (theFrame % 4)*.25f; + float theStartV = (theFrame / 4)*.333f; + float theEndU = theStartU + .25f; + float theEndV = theStartV + .333f; + + const int kBaseX = ScreenWidth() - .05*ScreenWidth(); + const int kBaseY = .75*ScreenHeight(); + const int kIconWidth = .05*ScreenWidth(); + const int kIconHeight = .05*ScreenHeight(); + + float x1 = kBaseX; + float y1 = kBaseY + i*kIconHeight; + float x2 = x1 + kIconWidth; + float y2 = y1 + kIconHeight; + + AvHSpriteSetRenderMode(kRenderTransAdd); + AvHSpriteEnableClippingRect(false); + AvHSpriteSetColor(1, 1, 1); + AvHSpriteDraw(mMarineUpgradesSprite, 0, x1, y1, x2, y2, theStartU, theStartV, theEndU, theEndV); + + } + } + + bool frames[3] = { false, false, false}; + if ( this->mHasGrenades ) frames[0]=true; + if ( this->mHasMines ) frames[1]=true; + if ( this->mHasWelder ) frames[2]=true; + + for(int i = 0; i < 3; i++) + { + int theFrame=i+9; + if ( frames[i] == true ) { + const int kIconWidth = .05*ScreenWidth(); + const int kIconHeight = .05*ScreenHeight(); + const int kBaseX = ScreenWidth() - .05*ScreenWidth(); + const int kBaseY = .75*ScreenHeight(); + + float theStartU = (theFrame % 4)*.25f; + float theStartV = (theFrame / 4)*.333f; + float theEndU = theStartU + .25f; + float theEndV = theStartV + .333f; + + float x1 = kBaseX; + float y1 = kBaseY - (i+1)*kIconHeight; + float x2 = x1 + kIconWidth; + float y2 = y1 + kIconHeight; + + AvHSpriteSetRenderMode(kRenderTransAdd); + AvHSpriteEnableClippingRect(false); + AvHSpriteSetColor(1, 1, 1); + AvHSpriteDraw(mMarineUpgradesSprite, theFrame, x1, y1, x2, y2, theStartU, theStartV, theEndU, theEndV); + } + } +} + +void AvHHud::RenderCommanderUI() +{ + + RenderStructureRanges(); + + // Draw command button + if(this->mCommandButtonSprite) + { + // Animate the button unless the mouse is over it (?) + int kNumFrames = 16; // Last frame is highlighted, first frame seems off + const float kFramesPerSecond = 10; + const float kCycleTime = (1.0f/kFramesPerSecond)*kNumFrames; + + float theNumCycles = this->mTimeOfLastUpdate/kCycleTime; + int theFrame = 1 + (theNumCycles - (int)theNumCycles)*kNumFrames; + + const int kStartX = 890; + int theX = (kStartX/1024.0f)*ScreenWidth(); + int theY = (525/768.0f)*ScreenHeight(); + + float theWidth = ((1024 - kStartX)/1024.0f)*ScreenWidth(); + float theHeight = (38/768.0f)*ScreenHeight(); + + float x1 = theX; + float y1 = theY; + float x2 = x1 + theWidth; + float y2 = y1 + theHeight; + + AvHSpriteSetRenderMode(kRenderTransAlpha); + AvHSpriteDraw(mCommandButtonSprite, theFrame, x1, y2, x2, y2, 0, 0, 1, 1); + + } + + // Draw "select all" button + if(this->mSelectAllSprite) + { + AvHTechImpulsePanel* theTechImpulsePanel = NULL; + if(this->GetManager().GetVGUIComponentNamed(kSelectAllImpulsePanel, theTechImpulsePanel)) + { + if(theTechImpulsePanel->isVisible()) + { + int theX, theY; + theTechImpulsePanel->getPos(theX, theY); + + int theWidth, theHeight; + theTechImpulsePanel->getSize(theWidth, theHeight); + + // Draw unhighlighted, highlighted, or active + int theFrame = 0; + + // If mouse is over, use frame 1 + if(this->GetIsMouseInRegion(theX, theY, theWidth, theHeight)) + { + theFrame = 1; + } + + // If all troops selected, use frame 2 + if((this->mSelected.size()) > 0 && (this->mSelected == this->mSelectAllGroup)) + { + theFrame = 2; + } + + AvHSpriteSetRenderMode(kRenderTransAlpha); + AvHSpriteDraw(mSelectAllSprite, theFrame, theX, theY, theX + theWidth, theY + theHeight, 0, 0, 1, 1); + + } + } + } + + // Draw blinking "under attack" indicator + if(this->mCommandStatusSprite) + { + int theFrame = 0; + + int theX = (725/1024.0f)*ScreenWidth(); + int theY = (702/768.0f)*ScreenHeight(); + + float theWidth = (61.5f/1024.0f)*ScreenWidth(); + float theHeight = (66/768.0f)*ScreenHeight(); + + // Blink button when an alert is set! + if(this->mBlinkingAlertType) + { + int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; + if(theSecondOfLastUpdate % 2) + { + theFrame = this->mBlinkingAlertType; + } + } + + AvHSpriteSetRenderMode(kRenderTransAlpha); + AvHSpriteDraw(mCommandStatusSprite, theFrame, theX, theY, theX + theWidth, theY + theHeight, 0, 0, 1, 1); + + } + + if(this->mTopDownTopSprite) + { + float theHeight = ((float)128/768)*ScreenHeight(); + AvHSpriteDrawTiles(mTopDownTopSprite, 4, 1, 0, 0, ScreenWidth(), theHeight, 0, 0, 1, 1); + } + + if(this->mTopDownBottomSprite) + { + // The artwork is three 128s high + float theHeight = ((float)256/768)*ScreenHeight(); + AvHSpriteSetRenderMode(kRenderTransAlpha); + AvHSpriteDrawTiles(mTopDownBottomSprite, 4, 1, 0, ScreenHeight()-theHeight, ScreenWidth(), ScreenHeight(), 0, 0, 1, 1); + } + + // Draw scaled sprites for all ActionButtons + this->DrawActionButtons(); + + // Draw hotgroups + this->DrawHotgroups(); + + // Draw pending requests + this->DrawPendingRequests(); + + // Draw logout button + if(this->mLogoutSprite) + { + int theFrame = 0; + + int theX = .785*ScreenWidth(); + int theY = .013*ScreenHeight(); + + float theWidth = .197f*ScreenWidth(); + float theHeight = .083f*ScreenHeight(); + + // Detect mouseover + if(this->GetIsMouseInRegion(theX, theY, theWidth, theHeight)) + { + theFrame = 1; + } + + AvHSpriteSetRenderMode(kRenderTransAlpha); + AvHSpriteDraw(mLogoutSprite, theFrame, theX, theY, theX + theWidth, theY + theHeight, 0, 0, 1, 1); + + } + +} + +void AvHHud::RenderStructureRanges() +{ + + float theRangeR = 0; + float theRangeG = 1; + float theRangeB = 0; + float theRangeA = 0.2f; + + // Draw range for current ghost building, if applicable + EntityListType theEntities; + IntList theDistanceRequirements; + float theZAdjustment = 0.0f; + bool theSnapToLocation = false; + bool theDrewGhostRegions = false; + + AvHSHUGetBuildRegions(this->mGhostBuilding, theEntities, theDistanceRequirements, theZAdjustment, theSnapToLocation); + + // For each entity to draw + int theDistanceCounter = 0; + for(EntityListType::iterator theRangeIter = theEntities.begin(); theRangeIter != theEntities.end(); theRangeIter++, theDistanceCounter++) + { + //cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(*theRangeIter); + physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theRangeIter); + if(theEntity) + { + vec3_t thePosition; + thePosition = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs); + + //int theSprite = (theEntity->iuser3 == AVH_USER3_SIEGETURRET) ? this->mSiegeTurretSprite : this->mBuildCircleSprite; + int theSprite = this->mBuildCircleSprite; + + int theDistanceRequirement = theDistanceRequirements[theDistanceCounter]; + RenderStructureRange(thePosition, theDistanceRequirement, theSprite, kRenderTransAdd, 0, theRangeR, theRangeG, theRangeB, theRangeA); + + theDrewGhostRegions = true; + } + } + + // Draw selection as well + for(EntityListType::iterator theSelectedIter = this->mSelected.begin(); theSelectedIter != this->mSelected.end(); theSelectedIter++) + { + cl_entity_s* theEntity = gEngfuncs.GetEntityByIndex(*theSelectedIter); + //physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theSelectedIter); + if(theEntity) + { + vec3_t thePosition; + thePosition = AvHSHUGetRealLocation(theEntity->curstate.origin, theEntity->curstate.mins, theEntity->curstate.maxs); + + AvHUser3 theUser3 = (AvHUser3)(theEntity->curstate.iuser3); + int theRange = AvHSHUGetDrawRangeForUser3(theUser3); + if(theRange > 0) + { + // Don't draw structures that are recycling and therefore inactive + if(!GetHasUpgrade(theEntity->curstate.iuser4, MASK_RECYCLING)) + { + //int theSprite = (theEntity->curstate.iuser3 == AVH_USER3_SIEGETURRET) ? this->mSiegeTurretSprite : this->mBuildCircleSprite; + int theSprite = this->mBuildCircleSprite; + RenderStructureRange(thePosition, theRange, theSprite, kRenderTransAdd, 0, theRangeR, theRangeG, theRangeB, theRangeA); + } + } + } + } + + // Draw the minimum build radius violations. + + EntityListType theBuildViolations; + AvHSHUGetMinBuildRadiusViolations(mGhostBuilding, mGhostWorldLocation, theBuildViolations); + + + + for (EntityListType::iterator theBuildViolationsIter = theBuildViolations.begin(); theBuildViolationsIter != theBuildViolations.end(); ++theBuildViolationsIter) + { + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + + // Store off the old count + gEngfuncs.pEventAPI->EV_PushPMStates(); + + // Now add in all of the players. + gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); + + physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theBuildViolationsIter); + + gEngfuncs.pEventAPI->EV_PopPMStates(); + + if(theEntity) + { + vec3_t thePosition; + thePosition = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs); + + AvHUser3 theUser3 = (AvHUser3)(theEntity->iuser3); + + vec3_t theMinSize, theMaxSize; + + AvHSHUGetSizeForUser3(theUser3, theMinSize, theMaxSize); + float theMaxRadius2 = max(max(theMinSize.x, theMaxSize.x), max(theMinSize.y, theMaxSize.y)); + + int theSprite = this->mBuildCircleSprite; + // : 0000291 + // It's possible to place "on" marines if you're offset a little from center. This code and + // associated changes above and in AvHSharedUtil.cpp is to enforce a build distance around marines, + // in the same way as structures, to prevent this exploit. + float theMinMarineBuildDistance; + if (theUser3 == AVH_USER3_MARINE_PLAYER) { + theMinMarineBuildDistance = BALANCE_VAR(kMinMarinePlayerBuildDistance); + } + else + { + theMinMarineBuildDistance = BALANCE_VAR(kMinMarineBuildDistance); + } + // : + RenderStructureRange(thePosition, theMinMarineBuildDistance + theMaxRadius2, theSprite, kRenderTransAdd, 0, 1, 0, 0, 0.3f); + + } + } +} + +void AvHHud::RenderStructureRange(vec3_t inOrigin, int inRadius, HSPRITE inSprite, int inRenderMode, int inFrame, float inR, float inG, float inB, float inAlpha) +{ + + vec3_t w1; + + w1[0] = inOrigin[0] - inRadius; + w1[1] = inOrigin[1] - inRadius; + w1[2] = inOrigin[2]; + + vec3_t s1; + gEngfuncs.pTriAPI->WorldToScreen(w1, s1); + + vec3_t w2; + + w2[0] = inOrigin[0] + inRadius; + w2[1] = inOrigin[1] + inRadius; + w2[2] = inOrigin[2]; + + vec3_t s2; + gEngfuncs.pTriAPI->WorldToScreen(w2, s2); + + float x1 = XPROJECT(s1[0]); + float y1 = YPROJECT(s1[1]); + float x2 = XPROJECT(s2[0]); + float y2 = YPROJECT(s2[1]); + + AvHSpriteSetColor(inR, inG, inB, inAlpha); + AvHSpriteSetRenderMode(inRenderMode); + AvHSpriteDraw(inSprite, inFrame, x1, y1, x2, y2, 0, 0, 1, 1); + AvHSpriteSetColor(1, 1, 1, 1); + +} + +void AvHHud::RenderAlienUI() +{ + + const float kAspectRatio = ScreenWidth()/ScreenHeight(); + + // Draw the embryo overlay if the player is gestating. + + if (GetHUDUser3() == AVH_USER3_ALIEN_EMBRYO) + { + if (mMembraneSprite) + { + AvHSpriteSetRenderMode(kRenderTransAdd); + AvHSpriteSetColor(1,1,1); + DrawWarpedOverlaySprite(mMembraneSprite, 4, 3, .007 * 2, .002 * 2, .15, .3); + } + return; + } + + AvHSpriteSetRenderMode(kRenderTransAlpha); + + int theWidth = kResourceEnergyBarWidth*ScreenWidth(); + int theHeight = kResourceEnergyBarHeight*ScreenHeight(); + + int theX = mViewport[0]; + int theY = mViewport[3] - theHeight + mViewport[1]; + + // Draw resource text label. + + if (!mSteamUIActive) + { + + const float kTextInset = kResourceEnergyBarWidth*.5f; + const int kNumericYOffset = 1.5*this->GetHudStringHeight(); + + // : 0000989 + // moved resource label a bit down + //int theResourceLabelX = mViewport[0] + kTextInset*ScreenWidth(); + //int theResourceLabelY = theY - + .05f * ScreenHeight(); + int theResourceLabelX = 10; + int theResourceLabelY = .68f * ScreenHeight(); + // : + + if(this->mMapMode == MAP_MODE_NS) + { + string theLocalizedResourceDescription; + if(LocalizeString(kAlienResourcePrefix, theLocalizedResourceDescription)) + { + int theStringWidth = this->GetHudStringWidth(theLocalizedResourceDescription.c_str()); + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, false, false); + this->DrawHudString(theResourceLabelX, theResourceLabelY, ScreenWidth(), theLocalizedResourceDescription.c_str(), theR, theG, theB); + } + + // Draw amount we have + char theBuffer[128]; + sprintf(theBuffer, "(%d/%d)", this->mVisualResources, this->GetMaxAlienResources()); + this->DrawTranslatedString(theResourceLabelX, theResourceLabelY + kNumericYOffset, theBuffer); + } + } + + // Draw energy bar. + + if (mAlienUIEnergySprite) + { + theX = mViewport[2] - theWidth + mViewport[0]; + + float theFactor = 1 - this->mVisualEnergyLevel; + + AvHSpriteSetColor(1,1,1); + AvHSpriteSetRenderMode(kRenderTransTexture); + + AvHSpriteDraw(mAlienUIEnergySprite, 0, theX, theY, theX + theWidth, theY + theHeight * theFactor, 0, 0, 1, theFactor); + AvHSpriteDraw(mAlienUIEnergySprite, 1, theX, theY + theHeight * theFactor, theX + theWidth, theY + theHeight, 0, theFactor, 1, 1); + + } + + if (mAlienUICloakSprite ) + { + cl_entity_s* theLocalPlayer = GetVisiblePlayer(); + if(theLocalPlayer ) { + theX = mViewport[2] - theWidth + mViewport[0]; + + int amount=0; + int range=255-kAlienSelfCloakingBaseOpacity; + if ( theLocalPlayer->curstate.renderamt > 0 ) { + amount=theLocalPlayer->curstate.renderamt-kAlienSelfCloakingBaseOpacity; + amount=max(0, min(range, amount)); + } + float theFactor = 1; + if ( theLocalPlayer->curstate.rendermode == kRenderTransTexture ) + theFactor=(float)amount/(float)range; + + + AvHSpriteSetColor(1,1,1); + AvHSpriteSetRenderMode(kRenderTransTexture); + + AvHSpriteDraw(mAlienUICloakSprite, 0, theX, theY, theX + theWidth, theY + theHeight * theFactor, 0, 0, 1, theFactor); + AvHSpriteDraw(mAlienUICloakSprite, 1, theX, theY + theHeight * theFactor, theX + theWidth, theY + theHeight, 0, theFactor, 1, 1); +// } +// else { +// int a=0; +// } + } + } + + // Draw hive indicators. + + if (mHiveInfoSprite) // && (this->mMapMode == MAP_MODE_NS)) // removed check to enable hive with health to be shown in combat + { + + int theHiveIndex = 0; + + for (HiveInfoListType::iterator theIter = this->mHiveInfoList.begin(); theIter != this->mHiveInfoList.end(); theIter++, theHiveIndex++) + { + + // For this hive, figure out which frame to draw + + int theFrame = theIter->mStatus; + + // If under attack, make it blink red + int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; + if(theIter->mUnderAttack && (theSecondOfLastUpdate % 2)) + { + theFrame = kHiveInfoStatusUnderAttack; + } + + int theHiveWidth = kHiveNormScreenWidth*ScreenWidth(); + + // Draw hive + //float theNormX = theHiveWidth; + float theNormY = kHiveNormScreenY + kHiveNormScreenVerticalSpacing*theHiveIndex; + + //DrawScaledHUDSprite(this->mHiveInfoSprite, kRenderTransAlpha, 1, theNormX*ScreenWidth, theNormY*ScreenHeight(), kHiveNormScreenWidth*ScreenWidth, kHiveNormScreenHeight*ScreenHeight(), theFrame); + + float x1 = mViewport[0] + mViewport[2] - theHiveWidth; + float y1 = mViewport[1] + theNormY*ScreenHeight(); + float x2 = x1 + theHiveWidth; + float y2 = y1 + kHiveNormScreenHeight*ScreenHeight(); + + AvHSpriteSetColor(1,1,1); + AvHSpriteSetRenderMode(kRenderTransTexture); + AvHSpriteDraw(mHiveInfoSprite, theFrame, x1, y1, x2, y2, 0, 0, 1, 1); + +// AvHSpriteSetColor(1,1,1); +// AvHSpriteSetRenderMode(kRenderTransTexture); +// AvHSpriteDraw(mHiveHealthSprite, 0, x1, y1, x1+100, y1+100, 0, 0, 1, 1); + + + // use the hive sprite height for the hivehealth sprite height and width + float theHealthSpriteHeight = (float)(kHiveNormScreenHeight) *ScreenHeight(); + float theHealthSpriteWidth = theHealthSpriteHeight; + + AvHSpriteSetColor(1,1,1); + AvHSpriteSetRenderMode(kRenderTransTexture); + + // adjust the position for the sprite -- to the left + float w1 = mViewport[0] + mViewport[2] - (kHiveNormScreenWidth + 0.02f)*ScreenWidth(); + + AvHSpriteDraw(mHiveHealthSprite, 0, w1, y1, w1 + theHealthSpriteWidth, y1 + theHealthSpriteHeight, 0, 0, 1, 1); + + if (theIter->mStatus > 0) { + if (theIter->mHealthPercentage < 100) { + AvHSpriteDraw(mHiveHealthSprite, 0, w1, y1, w1 + theHealthSpriteWidth, y1 + theHealthSpriteHeight, 0, 0, 1, 1); + AvHSpriteDraw(mHiveHealthSprite, 1, + w1, y1 + theHealthSpriteHeight - theHealthSpriteHeight * ((float)(theIter->mHealthPercentage) * 0.92f / 100), + w1 + theHealthSpriteWidth, y1 + theHealthSpriteHeight, + 0, 1 - ((float)(theIter->mHealthPercentage) * 0.92f / 100), + 1, 1); + } else { + AvHSpriteDraw(mHiveHealthSprite, 1, w1, y1, w1 + theHealthSpriteWidth, y1 + theHealthSpriteHeight, 0, 0, 1, 1); + } + } + + + + // Draw technology it's supporting + AvHMessageID theTechnology = (AvHMessageID)theIter->mTechnology; + theFrame = -1; + switch(theTechnology) + { + case ALIEN_BUILD_DEFENSE_CHAMBER: + theFrame = 0; + break; + case ALIEN_BUILD_SENSORY_CHAMBER: + theFrame = 3; + break; + case ALIEN_BUILD_MOVEMENT_CHAMBER: + theFrame = 2; + break; + } + + // Draw it inset + if(theFrame >= 0) + { + + float x1 = mViewport[0] + mViewport[2] - theHiveWidth + (kHiveTechnologyInsetXScalar*kHiveNormScreenWidth)*ScreenWidth(); + float y1 = mViewport[1] + (theNormY + kHiveTechnologyInsetYScalar*kHiveNormScreenHeight)*ScreenHeight(); + float x2 = x1 + kHiveTechnologyInsetWidthScalar*kHiveNormScreenWidth*ScreenWidth(); + float y2 = y1 + kHiveTechnologyInsetHeightScalar*kHiveNormScreenHeight*ScreenHeight(); + + AvHSpriteDraw(mAlienUIUpgradeCategories, theFrame, x1, y1, x2, y2, 0, 0, 1, 1); + + } + + } + + } + + // Draw the upgrades. + + const int kNumberXInset = 2; + const int kNumberYInset = 2; + + const int kBaseYOffset = .1*ScreenHeight(); + + // Now draw alien upgrades + const float kNormalizedSpacing = 0.01f; + const int kBaseRightOffset = kNormalizedSpacing*ScreenWidth(); + const int kVerticalUpgradeSpacing = kNormalizedSpacing*kAspectRatio*ScreenHeight(); + int theUpgradeVar = this->GetHUDUpgrades(); + const int kUpgradeFrame = 0; + const float kUpgradeSize = 0.04; + int theUpgradeWidth = kUpgradeSize*ScreenWidth(); + int theUpgradeHeight = kUpgradeSize*kAspectRatio*ScreenHeight(); + + // In Combat mode, we can have multiple upgrades in each category + int theNumDrawnInCategory[ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE + 1]; + memset(theNumDrawnInCategory, 0, sizeof(int)*(ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE + 1)); + + AvHUpgradeMask theUpgradeMasks[kNumAlienUpgrades] = {MASK_UPGRADE_1, MASK_UPGRADE_2, MASK_UPGRADE_3, MASK_NONE, MASK_NONE, MASK_NONE, MASK_UPGRADE_4, MASK_UPGRADE_5, MASK_UPGRADE_6, MASK_UPGRADE_7, MASK_UPGRADE_8, MASK_UPGRADE_9}; + for(int i = 0; i < kNumAlienUpgrades; i++) + { + AvHUpgradeMask theUpgradeMask = theUpgradeMasks[i]; + AvHAlienUpgradeCategory theCategory = ALIEN_UPGRADE_CATEGORY_INVALID; + AvHGetAlienUpgradeCategoryFromMask(theUpgradeMask, theCategory); + + int theCategoryNumber = (int)theCategory; + //int theRowNumber = i % (kNumAlienUpgrades/kNumAlienUpgradeCategories); + + // Quick hack: treat defense as offense for drawing purposes, because offense has been (temporarily?) removed + int theCategoryNumberOffset = (int)(((theCategory == ALIEN_UPGRADE_CATEGORY_DEFENSE) ? ALIEN_UPGRADE_CATEGORY_OFFENSE : theCategory)); + int theX = ScreenWidth() - theUpgradeWidth - kBaseRightOffset; + + // Inset drawing of multiple upgrades in same category (needed for Combat mode) + theX -= (theNumDrawnInCategory[theCategory]*theUpgradeWidth); + + int theEnergyHeight = kResourceEnergyBarHeight*ScreenHeight(); + + int theY = (3*kHiveNormScreenVerticalSpacing)*ScreenHeight() + kVerticalUpgradeSpacing + theCategoryNumberOffset*(kVerticalUpgradeSpacing + theUpgradeHeight); + + if(GetHasUpgrade(theUpgradeVar, theUpgradeMask)) + { + theNumDrawnInCategory[theCategory]++; + + int theLevelOfUpgrade = AvHGetAlienUpgradeLevel(theUpgradeVar, theUpgradeMask); + for(int theLevel = theLevelOfUpgrade; theLevel > 0; theLevel--) + { + // Draw them slightly overlapping + const float kOffset = .01f; + + float x1 = theX - theLevel*(kOffset*ScreenWidth()); + float y1 = theY - theLevel*(kOffset*ScreenHeight()); + float x2 = x1 + theUpgradeWidth; + float y2 = y1 + theUpgradeHeight; + + AvHSpriteDraw(mAlienUIUpgrades, i, x1, y1, x2, y2, 0, 0, 1, 1); + } + } + else + { + if(this->GetGameStarted() && !this->GetIsCombatMode()) + { + // If upgrade is pending, draw it blinking + for(int i = 0; i < this->mNumUpgradesAvailable; i++) + { + if(this->mCurrentUpgradeCategory[i] == theCategory) + { + int theSecondOfLastUpdate = (int)this->mTimeOfLastUpdate; + if(theSecondOfLastUpdate % 2) + { + int numSprites=1; + switch ( theCategory ) { + case ALIEN_UPGRADE_CATEGORY_DEFENSE: + numSprites=this->mNumDefense; + break; + case ALIEN_UPGRADE_CATEGORY_SENSORY: + numSprites=this->mNumSensory; + break; + case ALIEN_UPGRADE_CATEGORY_MOVEMENT: + numSprites=this->mNumMovement; + break; + } + for ( int j = numSprites; j > 0; j-- ) { + const float kOffset = .01f; + int theFrame = theCategory-1; + + float x1 = theX - j*(kOffset*ScreenWidth()); + float y1 = theY - j*(kOffset*ScreenHeight()); + float x2 = x1 + theUpgradeWidth; + float y2 = y1 + theUpgradeHeight; + + AvHSpriteDraw(mAlienUIUpgradeCategories, theFrame, x1, y1, x2, y2, 0, 0, 1, 1); + } + } + break; + } + } + } + } + } + + + if(this->GetIsAlien() && (this->GetHUDTeam() != TEAM_IND)) + { + bool theIsGestating = (this->GetHUDUser3() == AVH_USER3_ALIEN_EMBRYO); + bool theIsBeingDigested = this->GetIsBeingDigested(); + + this->mSelectedNodeResourceCost = -1; + + // Find the blip nearest our view reticle + int theNearestBlip = -1; + float theDotProductOfClosestBlip = -1; + + // Get view vector + Vector theViewVector; + vec3_t theForward, theRight, theUp; + AngleVectors(v_angles, theForward, theRight, theUp); + VectorNormalize(theForward); + + for(int theBlip = 0; theBlip < this->mFriendlyBlips.mNumBlips; theBlip++) + { + // Get vector to current blip + Vector theVectorToBlip; + VectorSubtract(this->mFriendlyBlips.mBlipPositions[theBlip], gPredictedPlayerOrigin, theVectorToBlip); + VectorNormalize(theVectorToBlip); + + // Dot them + float theDotProduct = DotProduct(theForward, theVectorToBlip); + + // Is dot product closer to 1 then best? + if(theDotProduct > theDotProductOfClosestBlip) + { + // Save new blip and dot product + theNearestBlip = theBlip; + theDotProductOfClosestBlip = theDotProduct; + } + } + + // Draw information about our friendly blips + theBlip = theNearestBlip; + if(theNearestBlip >= 0) + { + ASSERT(theNearestBlip < this->mFriendlyBlips.mNumBlips); + + // Draw location when player under attack + int theBlipStatus = this->mFriendlyBlips.mBlipStatus[theBlip]; + //if((theBlipStatus == kVulnerableFriendlyBlipStatus) || ((theBlipStatus >= kSayingOne) && (theBlipStatus <= kSayingSix))) + //{ + // Get location + string theLocationName = this->GetNameOfLocation(this->mFriendlyBlips.mBlipPositions[theBlip]); + + // Get name of entity + string theBlipName; + + int theBlipInfo = this->mFriendlyBlips.mBlipInfo[theBlip]; + if(theBlipInfo <= kBaseBlipInfoOffset) + { + hud_player_info_t thePlayerInfo; + gEngfuncs.pfnGetPlayerInfo(theBlipInfo, &thePlayerInfo); + if(thePlayerInfo.name) + { + theBlipName = thePlayerInfo.name; + } + } + else + { + AvHUser3 theUser3 = AvHUser3(theBlipInfo - kBaseBlipInfoOffset); + this->GetTranslatedUser3Name(theUser3, theBlipName); + } + + string theBlipStatusText; + char theBlipStatusString[512]; + sprintf(theBlipStatusString, "#%s%d", kBlipStatusPrefix, theBlipStatus); + LocalizeString(theBlipStatusString, theBlipStatusText); + + Vector theScreenPos; + Vector theMessageWorldPos = this->mFriendlyBlips.mBlipPositions[theBlip]; + const float kSpacingScalar = 1.2f; + theMessageWorldPos.z -= (kWorldBlipScale/2.0f)*kSpacingScalar; + + if(AvHCUWorldToScreen(theMessageWorldPos, (float*)&theScreenPos)) + { + if((theBlipName != "") && (theBlipStatusText != "") && (theLocationName != "") && (CVAR_GET_FLOAT(kvLabelHivesight) == 1)) + { + // Find alpha for the blip-text based on position on the screen + float screenWidth = ScreenWidth(); + float screenHeight = ScreenHeight(); + float xdiff = fabs(theScreenPos[0] - screenWidth/2); + float ydiff = fabs(theScreenPos[1] - screenHeight/2); + float quadrance = xdiff * xdiff + ydiff * ydiff; + float alpha = max(0.0f, 0.9f - quadrance / (screenHeight * screenHeight)); + alpha *= alpha * alpha * alpha; + + // "MonsieurEvil is under attack" + // "Resource tower is under attack" + char theFirstLine[512]; + sprintf(theFirstLine, "%s %s\n", theBlipName.c_str(), theBlipStatusText.c_str()); + this->DrawTranslatedString(theScreenPos[0], theScreenPos[1], theFirstLine, true, true, false, alpha); + + char theLocationNameCStr[512]; + strcpy(theLocationNameCStr, theLocationName.c_str()); + this->DrawTranslatedString(theScreenPos[0], theScreenPos[1] + .022f*ScreenHeight(), theLocationNameCStr, true, true, true, alpha); + } + } + + //} + } + + // Draw hive locations under each hive indicator + if(!theIsGestating && !theIsBeingDigested && (this->mMapMode == MAP_MODE_NS)) + { + int theHiveIndex = 0; + for(HiveInfoListType::iterator theIter = this->mHiveInfoList.begin(); theIter != this->mHiveInfoList.end(); theIter++, theHiveIndex++) + { + vec3_t theHivePosition(theIter->mPosX, theIter->mPosY, theIter->mPosZ); + string theLocationName = this->GetNameOfLocation(theHivePosition); + + int theHiveWidth = kHiveNormScreenWidth*ScreenWidth(); + + int theScreenPosX = mViewport[0] + mViewport[2] - theHiveWidth; + int theScreenPosY = mViewport[1] + (kHiveNormScreenY + theHiveIndex*kHiveNormScreenVerticalSpacing + kHiveNormScreenHeight)*ScreenHeight(); + + string theTranslatedLocationName; + LocalizeString(theLocationName.c_str(), theTranslatedLocationName); + + AvHCUTrimExtraneousLocationText(theTranslatedLocationName); + + int theR, theG, theB; + this->GetPrimaryHudColor(theR, theG, theB, false, false); + + // Draw text in greyscale if hive isn't building or active + if(theIter->mStatus == kHiveInfoStatusUnbuilt) + { + theR = theG = theB = 100; + } + + this->DrawHudStringReverse(mViewport[0] + mViewport[2] - 5, theScreenPosY, ScreenWidth(), (char*)theTranslatedLocationName.c_str(), theR, theG, theB); + } + } + } + + +} + +void AvHHud::DrawWarpedOverlaySprite(int spriteHandle, int numXFrames, int numYFrames, float inWarpXAmount, float inWarpYAmount, float inWarpXSpeed, float inWarpYSpeed) +{ + + float dx = ScreenWidth(); + float dy = ScreenHeight(); + + float theCurrentTime = gHUD.GetTimeOfLastUpdate(); + float theNormXAmount = theCurrentTime*inWarpXSpeed - (int)(theCurrentTime*inWarpXSpeed); // Get fractional part of second + float theNormYAmount = theCurrentTime*inWarpYSpeed - (int)(theCurrentTime*inWarpYSpeed); + float theSinusoidalNormXAmount = cos(theNormXAmount*2.0f*M_PI); + float theSinusoidalNormYAmount = sin(theNormYAmount*2.0f*M_PI); + float theXAmount = theSinusoidalNormXAmount*inWarpXAmount; + float theYAmount = theSinusoidalNormYAmount*inWarpYAmount; + + for (int frameY = 0; frameY < numYFrames; ++frameY) + { + for (int frameX = 0; frameX < numXFrames; ++frameX) + { + + float theWarpXStartAmount = 0; + float theWarpYStartAmount = 0; + float theWarpXEndAmount = 0; + float theWarpYEndAmount = 0; + + int frame = frameX + frameY * numXFrames; + + float pw = SPR_Width(spriteHandle, frame); + float ph = SPR_Height(spriteHandle, frame); + + float fx1 = ((float)(frameX)) / numXFrames; + float fy1 = ((float)(frameY)) / numYFrames; + + float fx2 = ((float)(frameX + 1)) / numXFrames; + float fy2 = ((float)(frameY + 1)) / numYFrames; + + if(frameX > 0) + { + theWarpXStartAmount = theXAmount*ScreenWidth(); + } + if(frameX < numXFrames - 1) + { + theWarpXEndAmount = theXAmount*ScreenWidth(); + } + if(frameY > 0) + { + theWarpYStartAmount = theYAmount*ScreenHeight(); + } + if(frameY < numYFrames - 1) + { + theWarpYEndAmount = theYAmount*ScreenHeight(); + } + + AvHSpriteDraw(spriteHandle, frame, + dx * fx1 + theWarpXStartAmount, + dy * fy1 + theWarpYStartAmount, + dx * fx2 + theWarpXEndAmount, + dy * fy2 + theWarpYEndAmount, + 1 / pw, 1 / ph, 1 - 1 / pw, 1 - 1 / ph); + + } + } + +} + +void AvHHud::RenderNoZBuffering() +{ + + if (mSteamUIActive) + { + // Hack since the HUD doesn't get drawn while the Steam UI is open. + Render(); + } + + // Don't draw this stuff while being digested + if(this->GetIsBeingDigested() || gParticleEditorHandler.GetInEditMode()) + { + return; + } + + // Temporary, so we can make movies without having the UI flit around (known HL playback bug) + if(/*gEngfuncs.pDemoAPI->IsPlayingback() ||*/ gViewPort->IsOptionsMenuVisible()) + { + return; + } + + const int theDistance = 50; + + const float kAspectRatio = ScreenWidth()/ScreenHeight(); + + float m_vRenderOrigin[3]; + pVector m_vUp; + pVector m_vRight; + pVector m_vNormal; + + //vec3_t theViewAngles; + //gEngfuncs.GetViewAngles((float*)theViewAngles) + //gEngfuncs.GetView + + IEngineStudio.GetViewInfo( m_vRenderOrigin, (float*)&m_vUp, (float*)&m_vRight, (float*)&m_vNormal ); + + //float m_fSoftwareXScale, m_fSoftwareYScale; + //IEngineStudio.GetAliasScale( &m_fSoftwareXScale, &m_fSoftwareYScale ); + + //vec3_t theViewHeight; + //gEngfuncs.pEventAPI->EV_LocalPlayerViewheight(theViewHeight); + + // pVector p; + // p.x = m_vRenderOrigin[0] + m_vNormal.x*theDistance; + // p.y = m_vRenderOrigin[1] + m_vNormal.y*theDistance; + // p.z = m_vRenderOrigin[2] + m_vNormal.z*theDistance; + + // pVector p; + // p.x = m_vRenderOrigin[0]; + // p.y = m_vRenderOrigin[1]; + // p.z = m_vRenderOrigin[2]; + // p.x = v_origin[0]; + // p.y = v_origin[1]; + // p.z = v_origin[2]; + + // Allow scripts to perform render + AvHScriptManager::Instance()->DrawNoZBuffering(); + +// if(cl_particleinfo->value) +// { +// // Draw particles here for debugging +// pVector theRealView; +// vec3_t angles, up, right, forward; +// gEngfuncs.pfnAngleVectors(pmove->angles, forward, right, up); +// +// theRealView.x = forward[0]; +// theRealView.y = forward[1]; +// theRealView.z = forward[2]; +// +// AvHParticleSystemManager::Instance()->Draw(theRealView); +// } + + //if(this->mMappingTechSprite && !this->mIsRenderingSelectionView && !this->mGameStarted) + //{ + // float theXPercentage = 256/1024.0f; + // float theYPercentage = 64/768.0f; + // DrawScaledHUDSprite(this->mMappingTechSprite, kRenderTransColor, 1, 0, ScreenHeight/2, theXPercentage*ScreenWidth, theYPercentage*ScreenHeight()); + //} + + // Draw blips, orders, selection and build effects for spectators and demo watchers too + int theUpgradeVar = this->GetHUDUpgrades(); + bool theHasHiveSightUpgrade = true;//GetHasUpgrade(theUpgradeVar, MASK_ALIEN_UPGRADE_11); + + if(this->GetIsAlien()) + { + if(theHasHiveSightUpgrade) + { + // Draw friendly blips + this->mFriendlyBlips.Draw(m_vNormal, kFriendlyBlipStatus); + } + } + + // Draw enemy blips + this->mEnemyBlips.Draw(m_vNormal, kEnemyBlipStatus); + + //this->DrawOrders(true); + this->DrawSelectionAndBuildEffects(); + //this->DrawHelpIcons(); + +} + +void AvHHud::InitHUDData( void ) +{ + this->ResetGame(true); +} + +void AvHHud::VidInit(void) +{ + UIHud::VidInit(); + + mOverviewMap.VidInit(); + + int theScreenWidth = ScreenWidth(); + string theSpriteName; + + // theSpriteName = UINameToSprite(kEggSprite, theScreenWidth); + // this->mAlienUIEggSprite = Safe_SPR_Load(theSpriteName.c_str()); + + // theSpriteName = UINameToSprite(kHiveSprite, theScreenWidth); + // this->mAlienUIHiveSprite = Safe_SPR_Load(theSpriteName.c_str()); + + int i = 0; + // for(i = 0; i < kNumAlienLifeforms; i++) + // { + // char theBaseName[128]; + // sprintf(theBaseName, "%s%d", kLifeformSprite, i+1); + // string theSpriteName = "sprites/level1_hud.spr";//UINameToSprite(theBaseName, theScreenWidth, true); + // this->mAlienUILifeforms[i] = Safe_SPR_Load(theSpriteName.c_str()); + // } + + char theBaseName[128]; + sprintf(theBaseName, "%s", kAlienUpgradeSprite); + theSpriteName = UINameToSprite(theBaseName, theScreenWidth); + this->mAlienUIUpgrades = Safe_SPR_Load(theSpriteName.c_str()); + + sprintf(theBaseName, "%s", kAlienUpgradeCategory); + theSpriteName = UINameToSprite(theBaseName, theScreenWidth); + this->mAlienUIUpgradeCategories = Safe_SPR_Load(theSpriteName.c_str()); + + // Load jetpack sprite + theSpriteName = UINameToSprite(kJetpackSprite, theScreenWidth); + this->mMarineUIJetpackSprite = Safe_SPR_Load(theSpriteName.c_str()); + + // Load alien energy sprite + theSpriteName = UINameToSprite(kAlienEnergySprite, theScreenWidth); + this->mAlienUIEnergySprite = Safe_SPR_Load(theSpriteName.c_str()); + theSpriteName = UINameToSprite(kAlienCloakSprite, theScreenWidth); + this->mAlienUICloakSprite = Safe_SPR_Load(theSpriteName.c_str()); + + // Load background for topdown mode + this->mBackgroundSprite = Safe_SPR_Load(kTopDownBGSprite); + + // Load HUD + this->mTopDownTopSprite = Safe_SPR_Load(kTopDownTopHUDSprite); + this->mTopDownBottomSprite = Safe_SPR_Load(kTopDownBottomHUDSprite); + this->mMarineTopSprite = Safe_SPR_Load(kMarineTopHUDSprite); + + this->mLogoutSprite = Safe_SPR_Load(kLogoutSprite); + this->mCommandButtonSprite = Safe_SPR_Load(kCommandButtonSprite); + this->mCommandStatusSprite = Safe_SPR_Load(kCommandStatusSprite); + this->mSelectAllSprite = Safe_SPR_Load(kSelectAllSprite); + + //this->mTopDownBottomSprite = Safe_SPR_Load("sprites/distorttest.spr"); + //this->mTopDownBottomSprite = Safe_SPR_Load("sprites/ns.spr"); + //this->mTopDownBottomSprite = Safe_SPR_Load("sprites/distorttest.spr"); + + // Load overlays + this->mMembraneSprite = Safe_SPR_Load(kMembraneSprite); + this->mDigestingSprite = Safe_SPR_Load(kDigestingSprite); + + // Load order sprite + theSpriteName = UINameToSprite(kOrdersSprite, theScreenWidth); + this->mOrderSprite = Safe_SPR_Load(theSpriteName.c_str()); + this->mHiveInfoSprite = Safe_SPR_Load(kHiveInfoSprite); + this->mHiveHealthSprite = Safe_SPR_Load(kHiveHealthSprite); + + // Load cursor sprite + this->mMarineCursor = Safe_SPR_Load(kCursorsSprite); + this->mAlienCursor = Safe_SPR_Load(kAlienCursorSprite); + this->mMarineOrderIndicator = Safe_SPR_Load(kMarineOrderSprite); + this->mMarineUpgradesSprite = Safe_SPR_Load(kMarineUpgradesSprite); + //this->mMappingTechSprite = Safe_SPR_Load("sprites/ns.spr"); + + this->mAlienBuildSprite = Safe_SPR_Load(kAlienBuildSprite); + this->mMarineBuildSprite = Safe_SPR_Load(kMarineBuildSprite); + + this->mAlienHealthSprite = Safe_SPR_Load(kAlienHealthSprite); + this->mMarineHealthSprite = Safe_SPR_Load(kMarineHealthSprite); + + this->mBuildCircleSprite = Safe_SPR_Load(kBuildCircleSprite); + //this->mSiegeTurretSprite = Safe_SPR_Load(kSiegeTurretSprite); + + this->mActionButtonSprites.clear(); + //this->mHelpSprites.clear(); + + string theIconName = string(kHelpIconPrefix) + ".spr"; + this->mHelpSprite = Safe_SPR_Load(theIconName.c_str()); + + // : 0000971 + this->mTeammateOrderSprite = Safe_SPR_Load(kTeammateOrderSprite); + // : + + this->mExperienceBarSprite = Safe_SPR_Load(kExperienceBarSprite); + this->mProgressBarSprite = Safe_SPR_Load(kProgressBarSprite); + + this->mEnemyBlips.VidInit(); + this->mFriendlyBlips.VidInit(); +} diff --git a/main/source/mod/AvHKnife.cpp b/main/source/mod/AvHKnife.cpp index 288dac8..d7b8079 100644 --- a/main/source/mod/AvHKnife.cpp +++ b/main/source/mod/AvHKnife.cpp @@ -90,9 +90,7 @@ float AvHKnife::GetDeployTime() const char* AvHKnife::GetDeploySound() const { - //return kKNDeploySound; - return NULL; - + return kKNDeploySound; } char* AvHKnife::GetHeavyViewModel() const @@ -157,8 +155,6 @@ void AvHKnife::FireProjectiles(void) // Do trace hull here float theDamage = this->mDamage; CBaseEntity* pHurt = this->m_pPlayer->CheckTraceHullAttack(kKNRange, theDamage, DMG_SLASH); - - if(pHurt) { char* theSoundToPlay = NULL; @@ -189,7 +185,6 @@ void AvHKnife::FireProjectiles(void) } } - #endif } diff --git a/main/source/mod/AvHMarineEquipment.cpp b/main/source/mod/AvHMarineEquipment.cpp index 47a190f..e96aae2 100644 --- a/main/source/mod/AvHMarineEquipment.cpp +++ b/main/source/mod/AvHMarineEquipment.cpp @@ -1,2478 +1,2486 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHMarineEquipment.cpp $ -// $Date: 2002/11/22 21:28:16 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHMarineEquipment.cpp,v $ -// Revision 1.50 2002/11/22 21:28:16 Flayra -// - mp_consistency changes -// -// Revision 1.49 2002/11/12 02:25:29 Flayra -// - HLTV updates -// -// Revision 1.48 2002/11/06 01:40:17 Flayra -// - Turrets now need an active turret factory to keep firing -// -// Revision 1.47 2002/11/03 04:50:43 Flayra -// - Hard-coded gameplay constants instead of putting in skill.cfg -// - Ammo and health now expire -// -// Revision 1.46 2002/10/28 20:35:46 Flayra -// - Allow HAs and jetpacks to only be picked up by marines -// -// Revision 1.45 2002/10/25 23:19:30 Flayra -// - Fixed bug where people were being telefragged improperly -// -// Revision 1.44 2002/10/24 21:32:09 Flayra -// - All heavy armor to be given via console -// - Fix for AFKers on inf portals, also for REIN players when recycling portals -// -// Revision 1.43 2002/10/20 21:10:48 Flayra -// - Optimizations -// -// Revision 1.42 2002/10/19 22:33:44 Flayra -// - Various server optimizations -// -// Revision 1.41 2002/10/18 22:20:49 Flayra -// - Reduce llamability of placing phases near drops or hazards -// -// Revision 1.40 2002/10/16 20:54:30 Flayra -// - Added phase gate sound -// - Fixed ghostly command station view model problem after building it -// -// Revision 1.39 2002/10/16 01:00:14 Flayra -// - Allow any jetpack to be picked up (needed for cheat, but this is the way all weapons work too, may need to be changed for marine vs. marine, not sure) -// -// Revision 1.38 2002/10/03 18:57:20 Flayra -// - Picking up heavy armor holsters your weapon for view model switch -// - Added "armory's upgrading, ammo not available" message but removed it for some reason (I think it was acting strange, like playing way too often) -// -// Revision 1.37 2002/09/25 21:12:26 Flayra -// - Undid solidity change (causes Sys_Error) -// -// Revision 1.36 2002/09/25 20:48:47 Flayra -// - Phase gates no longer solid -// -// Revision 1.35 2002/09/23 22:21:21 Flayra -// - Added jetpack and heavy armor -// - Added "cc online" sound -// - Turret factories now upgrade to advanced turret factories for siege -// - Added automatic resupply at armory, but removed it -// - Observatories scan in 2D now, to match commander range overlay -// -// Revision 1.34 2002/09/09 19:59:31 Flayra -// - Fixed up phase gates (no longer teleport you unless you have two, and they work properly now) -// - Refactored reinforcements -// - Fixed bug where secondary command station couldn't be built -// -// Revision 1.33 2002/08/31 18:01:02 Flayra -// - Work at VALVe -// -// Revision 1.32 2002/08/16 02:39:14 Flayra -// - Fixed command station problems after game ends -// -// Revision 1.31 2002/08/09 01:06:02 Flayra -// - Removed ability for command station to be resurrected -// - Fixed up phase gate effects -// -// Revision 1.30 2002/08/02 21:55:55 Flayra -// - Allow phase teleport to fail nicely. For some reason, players don't hear their own phase sound anymore, it seems to play at the point where they left instead of where they arrive -// -// Revision 1.29 2002/07/26 23:05:54 Flayra -// - Numerical event feedback -// - Started to add sparks when buildings were hit but didn't know the 3D point to play it at -// -// Revision 1.28 2002/07/24 18:45:42 Flayra -// - Linux and scripting changes -// -// Revision 1.27 2002/07/23 17:11:47 Flayra -// - Phase gates must be built and can be destroyed, observatories decloak aliens, hooks for fast reinforcements upgrade, nuke damage increased, commander banning -// -// Revision 1.26 2002/07/08 17:02:57 Flayra -// - Refactored reinforcements, updated entities for new artwork -// -// Revision 1.25 2002/07/01 21:29:59 Flayra -// - Removed phase particles, added implosion instead, don't select command station on log-in (messy for drawing building radii) -// -// Revision 1.24 2002/06/25 18:04:43 Flayra -// - Renamed some buildings, armory is now upgraded to advanced armory -// -// Revision 1.23 2002/06/10 19:58:17 Flayra -// - Scan update happens more often, in case aliens go cloaked during scan -// -// Revision 1.22 2002/06/03 16:50:35 Flayra -// - Renamed weapons factory and armory, added ammo resupplying -// -// Revision 1.21 2002/05/28 17:51:34 Flayra -// - Tried to make nuke sound play, extended shake duration to sound length, reinforcement refactoring, mark command stations as mapper placed, so they aren't deleted on level cleanup, support for point-entity buildable command stations -// -// Revision 1.20 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHGamerules.h" -#include "dlls/client.h" -#include "common/vec_op.h" -#include "common/vector_util.h" -#include "mod/AvHSoundListManager.h" -#include "dlls/weapons.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHConstants.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHMovementUtil.h" -#include "dlls/explode.h" -#include "dlls/weapons.h" -#include "util/MathUtil.h" -#include "mod/AvHTitles.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHParticleConstants.h" -#include "mod/AvHMarineTurret.h" -#include "mod/AvHSiegeTurret.h" -#include "mod/AvHHulls.h" - -//LINK_ENTITY_TO_CLASS(kwMine, AvHMine); -//LINK_ENTITY_TO_CLASS(kwDeployedTurret, AvHDeployedTurret); -//LINK_ENTITY_TO_CLASS(kwTurret, AvHTurret); -LINK_ENTITY_TO_CLASS(kwDeployedMine, AvHDeployedMine); -LINK_ENTITY_TO_CLASS(kwHealth, AvHHealth); -LINK_ENTITY_TO_CLASS(kwCatalyst, AvHCatalyst); -LINK_ENTITY_TO_CLASS(kwGenericAmmo, AvHGenericAmmo); -LINK_ENTITY_TO_CLASS(kwJetpack, AvHJetpack); -LINK_ENTITY_TO_CLASS(kwAmmoPack, AvHAmmoPack); -LINK_ENTITY_TO_CLASS(kwHeavyArmor, AvHHeavyArmor); - -LINK_ENTITY_TO_CLASS(kwWelder, AvHWelder); -LINK_ENTITY_TO_CLASS(kwScan, AvHScan); -LINK_ENTITY_TO_CLASS(kwPhaseGate, AvHPhaseGate); -//LINK_ENTITY_TO_CLASS(kwSiegeTurret, AvHSiegeTurret); -LINK_ENTITY_TO_CLASS(kwNuke, AvHNuke); - -LINK_ENTITY_TO_CLASS(kwInfantryPortal, AvHInfantryPortal); -LINK_ENTITY_TO_CLASS(kwTeamCommand, AvHCommandStation); -LINK_ENTITY_TO_CLASS(kwTurretFactory, AvHTurretFactory); -LINK_ENTITY_TO_CLASS(kwArmory, AvHArmory); -LINK_ENTITY_TO_CLASS(kwAdvancedArmory, AvHArmory); -//LINK_ENTITY_TO_CLASS(kwAdvancedArmory, AvHAdvancedArmory); -LINK_ENTITY_TO_CLASS(kwArmsLab, AvHArmsLab); -LINK_ENTITY_TO_CLASS(kwPrototypeLab, AvHPrototypeLab); -LINK_ENTITY_TO_CLASS(kwObservatory, AvHObservatory); - -extern int gTeleportEventID; -extern int gSiegeHitEventID; -extern int gSiegeViewHitEventID; -extern AvHSoundListManager gSoundListManager; - -void AvHDeployedMine::Precache(void) -{ - PRECACHE_UNMODIFIED_MODEL(kTripmineWModel); - PRECACHE_UNMODIFIED_MODEL(kTripmineW2Model); - PRECACHE_UNMODIFIED_SOUND(kTripmineDeploySound); - PRECACHE_UNMODIFIED_SOUND(kTripmineActivateSound); - PRECACHE_UNMODIFIED_SOUND(kTripmineChargeSound); - PRECACHE_UNMODIFIED_SOUND(kTripmineStepOnSound); -} - -void AvHDeployedMine::ActiveTouch(CBaseEntity* inOther) -{ - float kTimeBetweenBeeps = 3.0f; - - // Hack to circumvent the hack where owners can't collide. If this isn't done, then mines will blowup when touching the world sometimes. - //edict_t* theTempOwner = this->pev->owner; - //this->pev->owner = this->m_pRealOwner; - bool theEntityCanDoDamage = GetGameRules()->CanEntityDoDamageTo(this, inOther); - //this->pev->owner = theTempOwner; - // Check team here and only emit warning beep for friendlies - if(theEntityCanDoDamage && (this->pev->team != inOther->pev->team)) - { - GetGameRules()->MarkDramaticEvent(kMineExplodePriority, inOther, this); - this->Detonate(); - } - else - { - if(gpGlobals->time > this->mLastTimeTouched + kTimeBetweenBeeps) - { - // Only players trigger this, not buildings or other mines - AvHPlayer* thePlayer = dynamic_cast(inOther); - if(thePlayer) - { - // Play warning proximity beep - EMIT_SOUND( ENT(pev), CHAN_BODY, kTripmineStepOnSound, 0.5, ATTN_NORM ); // shut off chargeup - this->mLastTimeTouched = gpGlobals->time; - } - } - } -} - -// Ripped from old grenade -void AvHDeployedMine::Explode(TraceResult* inTrace, int inBitsDamageType) -{ - float flRndSound;// sound randomizer - - pev->model = iStringNull;//invisible - pev->solid = SOLID_NOT;// intangible - - float theDamageModifier; - int theUpgradeLevel = AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageModifier); - float theDamage = this->pev->dmg*theDamageModifier; - - pev->takedamage = DAMAGE_NO; - - // TODO: Look at upgrade and mark up damage - - // Pull out of the wall a bit - if ( inTrace->flFraction != 1.0 ) - { - pev->origin = inTrace->vecEndPos + (inTrace->vecPlaneNormal * (theDamage - 24) * 0.6); - } - - int iContents = UTIL_PointContents ( pev->origin ); - - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound - WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - if (iContents != CONTENTS_WATER) - { - WRITE_SHORT( g_sModelIndexFireball ); - } - else - { - WRITE_SHORT( g_sModelIndexWExplosion ); - } - WRITE_BYTE( (theDamage - 50) * .60 ); // scale * 10 - WRITE_BYTE( 15 ); // framerate - WRITE_BYTE( TE_EXPLFLAG_NONE ); - MESSAGE_END(); - - CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, NORMAL_EXPLOSION_VOLUME, 3.0 ); - - //RadiusDamage(this->pev, this->pevOwner, theDamage, CLASS_NONE, inBitsDamageType); - int theRadius = BALANCE_VAR(kMineRadius); - RadiusDamage(this->pev->origin, this->pev, this->mPlacer, theDamage, theRadius, CLASS_NONE, inBitsDamageType); - - // Play view shake here - float theShakeAmplitude = 80; - float theShakeFrequency = 100; - float theShakeDuration = 1.0f; - float theShakeRadius = 700; - UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius); - - if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 ) - { - UTIL_DecalTrace( inTrace, DECAL_SCORCH1 ); - } - else - { - UTIL_DecalTrace( inTrace, DECAL_SCORCH2 ); - } - - flRndSound = RANDOM_FLOAT( 0 , 1 ); - - switch ( RANDOM_LONG( 0, 2 ) ) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris1.wav", 0.55, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris2.wav", 0.55, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris3.wav", 0.55, ATTN_NORM); break; - } - - pev->effects |= EF_NODRAW; - SetThink( &CGrenade::Smoke ); - pev->velocity = g_vecZero; - pev->nextthink = gpGlobals->time + 0.3; - - if (iContents != CONTENTS_WATER) - { - int sparkCount = RANDOM_LONG(0,3); - for ( int i = 0; i < sparkCount; i++ ) - Create( "spark_shower", pev->origin, inTrace->vecPlaneNormal, NULL ); - } -} - -void AvHDeployedMine::Smoke(void) -{ - if (UTIL_PointContents(this->pev->origin ) == CONTENTS_WATER) - { - UTIL_Bubbles(this->pev->origin - Vector( 64, 64, 64 ), this->pev->origin + Vector( 64, 64, 64 ), 100 ); - } - else - { - float theDamageModifier; - int theUpgradeLevel = AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageModifier); - int theDamage = this->pev->dmg*theDamageModifier; - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, this->pev->origin); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( this->pev->origin.x ); - WRITE_COORD( this->pev->origin.y ); - WRITE_COORD( this->pev->origin.z ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( (theDamage - 50) * 0.80 ); // scale * 10 - WRITE_BYTE( 12 ); // framerate - MESSAGE_END(); - } - UTIL_Remove( this ); -} - -void AvHDeployedMine::Killed(entvars_t* inAttacker, int inGib) -{ - this->Detonate(); - - CBasePlayerItem::Killed(inAttacker, inGib); -} - -int AvHDeployedMine::TakeDamage(entvars_t *inInflictor, entvars_t *inAttacker, float inDamage, int inBitsDamageType) -{ - // Don't include this for potential overflow reasons - //UTIL_Sparks(this->pev->origin); - - return CBasePlayerItem::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType); -} - -void AvHDeployedMine::Detonate() -{ - // Stop charging up - EMIT_SOUND(ENT(this->pev), CHAN_BODY, "common/null.wav", 0.5, ATTN_NORM ); - - if(!this->mDetonated && this->mPoweredUp) - { - TraceResult tr; - UTIL_TraceLine(this->pev->origin + this->mVecDir * 8, this->pev->origin - this->mVecDir * 64, dont_ignore_monsters, ENT(this->pev), &tr); - - this->Explode(&tr, NS_DMG_NORMAL); - - this->mDetonated = true; - } -} - -const float kTripmineActiveThinkTime = .8f; -const float kTripminePowerUpThinkTime = .2f; -const float kTripminePowerUpTime = 3.8f; -const float kTripmineFailTime = 20.0f; - -void AvHDeployedMine::SetPlacer(entvars_t* inPlacer) -{ - this->mPlacer = inPlacer; -} - -void AvHDeployedMine::PowerupThink() -{ - // Find an owner - if(this->mOwner == NULL) - { - edict_t* theOldOwner = this->pev->owner; - this->pev->owner = NULL; - - TraceResult tr; - UTIL_TraceLine(this->pev->origin + this->mVecDir * 8, this->pev->origin - this->mVecDir * 32, dont_ignore_monsters, ENT(this->pev), &tr); - - if (tr.fStartSolid || (theOldOwner && tr.pHit == theOldOwner)) - { - this->pev->owner = theOldOwner; - } - else if (tr.flFraction < 1.0) - { - this->pev->owner = tr.pHit; - this->mOwner = CBaseEntity::Instance(this->pev->owner); - this->mOwnerOrigin = this->mOwner->pev->origin; - this->mOwnerAngles = this->mOwner->pev->angles; - } - else - { - STOP_SOUND(ENT(this->pev), CHAN_VOICE, kTripmineDeploySound); - STOP_SOUND(ENT(this->pev), CHAN_BODY, kTripmineChargeSound); - - SetThink(&CBaseEntity::SUB_Remove); - this->pev->nextthink = gpGlobals->time + 0.1; - - ALERT(at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", this->pev->origin.x, this->pev->origin.y, this->pev->origin.z); - //KillBeam(); - return; - } - } - else - { - this->DetonateIfOwnerInvalid(); - } - - if(!this->mPoweredUp) - { - if(gpGlobals->time > (this->mTimePlaced + kTripminePowerUpTime)) - { - //if(this->pev->solid == SOLID_BBOX) - //{ - // play enabled sound - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, kTripmineActivateSound, 0.5, ATTN_NORM, 1.0, 75 ); - - SetTouch(&AvHDeployedMine::ActiveTouch); - - SetThink(&AvHDeployedMine::ActiveThink); - this->pev->nextthink = gpGlobals->time + kTripmineActiveThinkTime; - - this->mPoweredUp = true; - //} - } - } - - if(!this->mPoweredUp) - { - this->pev->nextthink = gpGlobals->time + kTripminePowerUpThinkTime; - } -} - -void AvHDeployedMine::DetonateIfOwnerInvalid() -{ - if((this->mOwner == NULL) || (this->mOwner->pev->origin != this->mOwnerOrigin) || (this->mOwner->pev->angles != this->mOwnerAngles)) - { - this->Detonate(); - } -} - -// Check to see if our position is no longer valid -void AvHDeployedMine::ActiveThink() -{ - this->DetonateIfOwnerInvalid(); - this->pev->nextthink = gpGlobals->time + kTripmineActiveThinkTime; -} - -void AvHDeployedMine::Spawn(void) -{ - this->Precache(); - - this->pev->movetype = MOVETYPE_FLY; - this->pev->solid = SOLID_BBOX; - this->pev->classname = MAKE_STRING(kwsDeployedMine); - this->pev->iuser3 = AVH_USER3_MINE; - - SET_MODEL(ENT(pev), kTripmineWModel); - - UTIL_SetSize(this->pev, kMineMinSize, kMineMaxSize); - UTIL_SetOrigin(this->pev, this->pev->origin ); - - // Can't emit beep until active - this->mTimePlaced = gpGlobals->time; - this->mLastTimeTouched = this->mTimePlaced; - - SetThink(&AvHDeployedMine::PowerupThink); - this->pev->nextthink = gpGlobals->time + kTripminePowerUpThinkTime; - - // give them hit points - this->pev->takedamage = DAMAGE_YES; - this->pev->health = BALANCE_VAR(kMineHealth); - this->pev->dmg = BALANCE_VAR(kMineDamage); - - // play deploy sound - EMIT_SOUND( ENT(this->pev), CHAN_VOICE, kTripmineDeploySound, .8f, ATTN_NORM ); - EMIT_SOUND( ENT(this->pev), CHAN_BODY, kTripmineChargeSound, .5f, ATTN_NORM ); // chargeup - - UTIL_MakeAimVectors(this->pev->angles); - this->mVecDir = gpGlobals->v_forward; - this->mVecEnd = this->pev->origin + this->mVecDir * 2048; - - this->mDetonated = false; - this->mPoweredUp = false; - this->mOwner = NULL; - this->mPlacer = NULL; -} - -AvHPlayerEquipment::AvHPlayerEquipment() -{ - this->mIsPersistent = false; - this->mLifetime = -1; -} - -void AvHPlayerEquipment::KeyValue(KeyValueData* pkvd) -{ - // Any entity placed by the mapper is persistent - this->SetPersistent(); - - if(FStrEq(pkvd->szKeyName, "teamchoice")) - { - //this->mTeam = (AvHTeamNumber)(atoi(pkvd->szValue)); - this->pev->team = (AvHTeamNumber)(atoi(pkvd->szValue)); - - pkvd->fHandled = TRUE; - } - else if(FStrEq(pkvd->szKeyName, "angles")) - { - // TODO: Insert code here - //pkvd->fHandled = TRUE; - int a = 0; - } - else if(FStrEq(pkvd->szKeyName, "lifetime")) - { - this->mLifetime = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBasePlayerItem::KeyValue(pkvd); - } -} - -// 0 means never expire, -1 means no value was set so use default -int AvHPlayerEquipment::GetLifetime() const -{ - int theLifetime = this->mLifetime; - - if(theLifetime < 0) - { - theLifetime = AvHSUGetWeaponStayTime(); - } - - return theLifetime; -} - -bool AvHPlayerEquipment::GetIsPersistent() const -{ - return this->mIsPersistent; -} - -void AvHPlayerEquipment::SetPersistent() -{ - this->mIsPersistent = true; -} - -void AvHHealth::Precache(void) -{ - PRECACHE_UNMODIFIED_MODEL(kHealthModel); - PRECACHE_UNMODIFIED_SOUND(kHealthPickupSound); -} - -void AvHHealth::Spawn(void) -{ - this->Precache(); - - SET_MODEL(ENT(pev), kHealthModel); - this->pev->movetype = MOVETYPE_TOSS; - this->pev->solid = SOLID_TRIGGER; - this->pev->framerate = 1.0; - - UTIL_SetSize(pev, kHealthMinSize, kHealthMaxSize); - UTIL_SetOrigin( pev, pev->origin ); - - SetTouch(&AvHHealth::Touch); - - // Expire after a time. - int theLifetime = this->GetLifetime(); - if(theLifetime > 0) - { - SetThink(&AvHHealth::SUB_Remove); - this->pev->nextthink = gpGlobals->time + theLifetime; - } - - this->pev->iuser3 = AVH_USER3_MARINEITEM; -} - -BOOL AvHHealth::GiveHealth(CBaseEntity* inOther, float points) -{ - BOOL theSuccess = FALSE; - -// puzl: 1017 -// Amount of health to give is now a paramater to allow us to vary the resupply amount for the armoury - -// float thePointsPerHealth = BALANCE_VAR(kPointsPerHealth) - - - AvHPlayer* thePlayer = dynamic_cast(inOther); - if(thePlayer && thePlayer->GetIsRelevant() && thePlayer->GetIsMarine()) - { - float thePlayerMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, thePlayer->GetUser3(), thePlayer->GetExperienceLevel()); - if(thePlayer->pev->health < thePlayerMaxHealth) - { - float thePointsGiven = min(points, (thePlayerMaxHealth - thePlayer->pev->health)); - - thePlayer->pev->health += thePointsGiven; - - if(CVAR_GET_FLOAT(kvDrawDamage)) - { - thePlayer->PlaybackNumericalEvent(kNumericalInfoHealthEvent, thePointsGiven); - } - - // Remove parasite if player has one - //int& theUser4 = thePlayer->pev->iuser4; - //SetUpgradeMask(&theUser4, MASK_PARASITED, false); - - EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kHealthPickupSound, 1, ATTN_NORM); - - theSuccess = TRUE; - } - } - - return theSuccess; -} - -void AvHHealth::Touch(CBaseEntity* inOther) -{ - // puzl: 1017 medpack health amount - if(AvHHealth::GiveHealth(inOther, BALANCE_VAR(kPointsPerHealth))) - { - UTIL_Remove(this); - } -} - - -void AvHCatalyst::Precache(void) -{ - PRECACHE_UNMODIFIED_MODEL(kCatalystModel); - PRECACHE_UNMODIFIED_SOUND(kCatalystPickupSound); -} - -void AvHCatalyst::Spawn(void) -{ - this->Precache(); - - SET_MODEL(ENT(pev), kCatalystModel); - this->pev->movetype = MOVETYPE_TOSS; - this->pev->solid = SOLID_TRIGGER; - - UTIL_SetSize(pev, kCatalystMinSize, kCatalystMaxSize); - UTIL_SetOrigin( pev, pev->origin ); - - SetTouch(&AvHCatalyst::Touch); - - // Expire after a time. - int theLifetime = this->GetLifetime(); - if(theLifetime > 0) - { - SetThink(&AvHCatalyst::SUB_Remove); - this->pev->nextthink = gpGlobals->time + theLifetime; - } - - this->pev->iuser3 = AVH_USER3_MARINEITEM; -} - -BOOL AvHCatalyst::GiveCatalyst(CBaseEntity* inOther) -{ - BOOL theSuccess = FALSE; - - float theCatalystDuration = BALANCE_VAR(kCatalystDuration); - - AvHPlayer* thePlayer = dynamic_cast(inOther); - if(thePlayer && thePlayer->GetIsRelevant() && thePlayer->GetIsMarine() && !thePlayer->GetIsCatalysted()) - { - //// The player takes damage too - //float theDamagePercent = BALANCE_VAR(kCatalystDamagePercent); - //float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3); - //float theDamage = theDamagePercent*theMaxHealth; - // - //// Never kill the player - //theDamage = min(theDamage, thePlayer->pev->health - 1); - //thePlayer->TakeDamage(thePlayer->pev, thePlayer->pev, theDamage, DMG_GENERIC | DMG_IGNOREARMOR); - - EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kCatalystPickupSound, 1, ATTN_NORM); - - thePlayer->SetIsCatalysted(true, theCatalystDuration); - - theSuccess = TRUE; - } - - return theSuccess; -} - -void AvHCatalyst::Touch(CBaseEntity* inOther) -{ - if(AvHCatalyst::GiveCatalyst(inOther)) - { - UTIL_Remove(this); - } -} - -void AvHHeavyArmor::Precache(void) -{ - PRECACHE_UNMODIFIED_MODEL(kHeavyModel); - PRECACHE_UNMODIFIED_SOUND(kHeavyPickupSound); -} - -void AvHHeavyArmor::Spawn(void) -{ - this->Precache(); - - SET_MODEL(ENT(pev), kHeavyModel); - this->pev->movetype = MOVETYPE_TOSS; - this->pev->solid = SOLID_TRIGGER; - UTIL_SetSize(pev, kHeavyMinSize, kHeavyMaxSize); - UTIL_SetOrigin( pev, pev->origin ); - - SetTouch(&AvHHeavyArmor::Touch); - - this->pev->iuser3 = AVH_USER3_HEAVY; -} - -void AvHHeavyArmor::Touch(CBaseEntity* inOther) -{ - AvHPlayer* thePlayer = dynamic_cast(inOther); - if(thePlayer && thePlayer->GetIsRelevant()) - { - if(thePlayer->GetIsMarine()) - { - // Check to make sure they don't have heavy armor or jetpack already - if((!thePlayer->GetHasJetpack() || GetGameRules()->GetIsCombatMode()) && !thePlayer->GetHasHeavyArmor())//voogru: ignore in combat mode since were trying to touch it. - { - // Needed because view model changes - if(thePlayer->HolsterWeaponToUse()) - { - // Give player heavy armor - SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_7, false); - SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_13); - - // Mark player with heavy armor - thePlayer->EffectivePlayerClassChanged(); - - // Set new armor value - thePlayer->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3); - - EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kHeavyPickupSound, 1, ATTN_NORM); - - UTIL_Remove(this); - } - } - } - } -} - -void AvHJetpack::Precache(void) -{ - PRECACHE_UNMODIFIED_MODEL(kJetpackModel); - PRECACHE_UNMODIFIED_SOUND(kJetpackPickupSound); -} - -void AvHJetpack::Spawn(void) -{ - this->Precache(); - - SET_MODEL(ENT(pev), kJetpackModel); - this->pev->movetype = MOVETYPE_TOSS; - this->pev->solid = SOLID_TRIGGER; - UTIL_SetSize(pev, kJetpackMinSize, kJetpackMaxSize); - UTIL_SetOrigin( pev, pev->origin ); - - SetTouch(&AvHJetpack::Touch); - - this->pev->iuser3 = AVH_USER3_JETPACK; -} - -void AvHJetpack::Touch(CBaseEntity* inOther) -{ - AvHPlayer* thePlayer = dynamic_cast(inOther); - if(thePlayer && thePlayer->GetIsRelevant()) - { - if(thePlayer->GetIsMarine()) - { - // Check to make sure they don't have heavy armor or jetpack already - if((!thePlayer->GetHasHeavyArmor() || GetGameRules()->GetIsCombatMode()) && !thePlayer->GetHasJetpack())//voogru: ignore in combat mode since were trying to touch it. - { - if(thePlayer->HolsterWeaponToUse()) - { - // Give player jetpack - SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_13, false); - SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_7); - - // Mark player with jetpack - thePlayer->EffectivePlayerClassChanged(); - - EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kJetpackPickupSound, 1, ATTN_NORM); - - // Set new armor value - thePlayer->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3); - - // Set full energy to start - thePlayer->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; - - - UTIL_Remove(this); - } - } - } - } -} - - -void AvHAmmoPack :: Precache( void ) -{ - PRECACHE_UNMODIFIED_MODEL(kAmmoPackModel); - PRECACHE_UNMODIFIED_SOUND(kAmmoPackPickupSound); -} - -void AvHAmmoPack :: Spawn( void ) -{ - this->Precache(); - - SET_MODEL(ENT(pev), kAmmoPackModel); - - this->pev->movetype = MOVETYPE_TOSS; - this->pev->solid = SOLID_TRIGGER; - this->m_flNoTouch = gpGlobals->time + 0.25; - - UTIL_SetSize(pev, kAmmoPackMinSize, kAmmoPackMaxSize); - UTIL_SetOrigin( pev, pev->origin ); - - SetTouch(&AvHAmmoPack::Touch); -} - -void AvHAmmoPack :: Touch( CBaseEntity *inOther) -{ - if(this->m_flNoTouch > gpGlobals->time) - return; - - //Dont touch non-players - if(!inOther->IsPlayer()) - return; - - AvHPlayer *thePlayer = dynamic_cast(inOther); - - //if they dont have a weapon that uses this ammo, dont pick up the ammo pack. - if ( !(thePlayer->pev->weapons & (1<GiveAmmo(this->m_iAmmoAmt, this->m_szAmmoType, this->m_iMaxAmmo) != -1) - { - EMIT_SOUND(ENT(thePlayer->pev), CHAN_ITEM, kAmmoPackPickupSound, 1, ATTN_NORM); - thePlayer->PlaybackNumericalEvent(kNumericalInfoAmmoEvent, 0); - UTIL_Remove(this); - } -} - -BOOL AvHGenericAmmo::GiveAmmo(CBaseEntity* inOther) -{ - // Give ammo to the player. It will be added as generic ammo, added as one clip to - // the player's current weapon. - if (inOther->GiveAmmo( 0, kwsGenericAmmo, 0 ) != -1) - { - EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kAmmoPickupSound, 1, ATTN_NORM); - - AvHPlayer* thePlayer = dynamic_cast(inOther); - if(thePlayer) - { - // Just say "ammo received" instead of number of bullets, too confusing - thePlayer->PlaybackNumericalEvent(kNumericalInfoAmmoEvent, 0); - } - - return TRUE; - } - - return FALSE; -} - -BOOL AvHGenericAmmo::AddAmmo( CBaseEntity *pOther ) -{ - if(this->mDropped) - { - return GiveAmmo(pOther); - } - - return FALSE; -} - -void AvHGenericAmmo::Dropped(void) -{ - this->mDropped = true; - SetThink(NULL); - - // Expire after a time - SetThink(&AvHGenericAmmo::SUB_Remove); - this->pev->nextthink = gpGlobals->time + AvHSUGetWeaponStayTime(); -} - -void AvHGenericAmmo::Precache( void ) -{ - PRECACHE_UNMODIFIED_MODEL(kAmmoModel); - PRECACHE_UNMODIFIED_SOUND(kAmmoPickupSound); -} - -void AvHGenericAmmo::Spawn( void ) -{ - Precache( ); - SET_MODEL(ENT(pev), kAmmoModel); - UTIL_SetSize(pev, kAmmoMinSize, kAmmoMaxSize); - - CBasePlayerAmmo::Spawn( ); - this->mDropped = false; - SetThink(&AvHGenericAmmo::Dropped); - this->pev->nextthink = gpGlobals->time + .15f; - - this->pev->iuser3 = AVH_USER3_MARINEITEM; -} - - -const float kScanThinkTime = .5f; - -AvHScan::AvHScan() -{ -} - -void AvHScan::Precache(void) -{ - CBaseAnimating::Precache(); - - PRECACHE_UNMODIFIED_MODEL(kScanModel); - PRECACHE_UNMODIFIED_SOUND(kScanSound); -} - -void AvHScan::Spawn(void) -{ -// this->Precache(); -// CBaseAnimating::Spawn(); -// -// SET_MODEL(ENT(this->pev), kScanModel); -// -// this->pev->movetype = MOVETYPE_NONE; -// this->pev->solid = SOLID_NOT; -// -// this->pev->classname = MAKE_STRING(kwsScan); -// -// this->pev->takedamage = DAMAGE_NO; -// -// // Start animating -// this->pev->sequence = 0; -// this->pev->frame = 0; -// ResetSequenceInfo(); - - this->pev->effects |= (/*EF_BRIGHTFIELD |*/ EF_DIMLIGHT); - - this->mTimeCreated = gpGlobals->time; - - SetThink(&AvHScan::ScanThink); - this->pev->nextthink = gpGlobals->time + GetGameRules()->GetFirstScanThinkTime(); - - EMIT_SOUND(this->edict(), CHAN_AUTO, kScanSound, 1.0f, ATTN_NORM); - - AvHSUPlayParticleEvent(kpsScanEffect, this->edict(), this->pev->origin); -} - -void AvHScan::ScanThink() -{ - // Remove cloaking from nearby enemies - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team)) - { - // Check that entity is in range of scan - float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); - if(theDistance < BALANCE_VAR(kScanRadius)) - { - // Remove cloaking, if player has it - theEntity->TriggerUncloak(); - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - // Look in sphere for cloakables - CBaseEntity* theSphereEntity = NULL; - while ((theSphereEntity = UTIL_FindEntityInSphere(theSphereEntity, this->pev->origin, BALANCE_VAR(kScanRadius))) != NULL) - { - if(!AvHSUGetIsExternalClassName(STRING(theSphereEntity->pev->classname))) - { - // TODO: Check team here? - AvHCloakable* theCloakable = dynamic_cast(theSphereEntity); - if(theCloakable) - { - theCloakable->Uncloak(); - } - } - } - - float theScanTime = GetGameRules()->GetBuildTimeForMessageID(BUILD_SCAN); - if(gpGlobals->time < (this->mTimeCreated + theScanTime)) - { - this->pev->nextthink = gpGlobals->time + kScanThinkTime; - } - else - { - UTIL_Remove(this); - } -} - - - -AvHPhaseGate::AvHPhaseGate() : AvHMarineBaseBuildable(TECH_PHASE_GATE, BUILD_PHASEGATE, kwsPhaseGate, AVH_USER3_PHASEGATE) -{ - this->mEnabled = false; - this->mTimeOfLastDeparture=0.0f; - this->mHasWarmedUp=false; -} - -int AvHPhaseGate::GetSequenceForBoundingBox() const -{ - return 1; -} - -void AvHPhaseGate::Killed(entvars_t* inAttacker, int inGib) -{ - this->SetEnabled(false); - - AvHMarineBaseBuildable::Killed(inAttacker, inGib); - - GetGameRules()->MarkDramaticEvent(kPGDeathPriority, this->entindex(), inAttacker); -} - -void AvHPhaseGate::Precache(void) -{ - CBaseAnimating::Precache(); - - PRECACHE_UNMODIFIED_MODEL(kPhaseGateModel); - PRECACHE_UNMODIFIED_SOUND(kPhaseGateSound); - PRECACHE_UNMODIFIED_SOUND(kPhaseGateTransportSound); -} - -void AvHPhaseGate::SetHasBeenBuilt() -{ - AvHBuildable::SetHasBeenBuilt(); - - // Include a "warm-up" time so movement chambers don't teleport the player immediately - this->mTimeOfLastDeparture=gpGlobals->time; - SetThink(&AvHPhaseGate::IdleThink); - - this->pev->nextthink = gpGlobals->time + kBuildingUseWarmupTime; -} - -int AvHPhaseGate::GetIdleAnimation() const -{ - return 0; -} - -void AvHPhaseGate::IdleThink() -{ - bool theIsEnabled = false; - this->mHasWarmedUp=true; - // Check if there are any other phase gates on our team and set our enabled state accordingly - bool theDone = false; - bool potentialDeadlock=false; - int theNumPhaseGates=0; - if(this->GetIsBuilt() && !this->GetIsRecycling()) - { - edict_t* thePhaseGateEdict = ENT(this->pev); - - // Keep looping until we come back to ourself or we find another gate on our team to phase to - while(!theDone) - { - thePhaseGateEdict = FIND_ENTITY_BY_CLASSNAME(thePhaseGateEdict, kwsPhaseGate); - AvHPhaseGate* thePhaseGate = dynamic_cast(CBaseEntity::Instance(thePhaseGateEdict)); - - // This assert fails in normal operation, seemingly harmlessly - //ASSERT(thePhaseGate); - - if(thePhaseGateEdict == ENT(this->pev)) - { - theDone = true; - } - else if((thePhaseGateEdict->v.team == this->pev->team) && thePhaseGate && thePhaseGate->GetIsBuilt() && !thePhaseGate->GetIsRecycling() && thePhaseGate->HasWarmedUp() ) - { - theIsEnabled = true; - theDone = true; - } - theNumPhaseGates++; - } - } - - AvHBaseBuildable::AnimateThink(); - - this->SetEnabled(theIsEnabled); - - this->pev->nextthink = gpGlobals->time + kPhaseGateIdleThink; -} - -void AvHPhaseGate::ResetEntity() -{ - this->SetEnabled(false); - - AvHMarineBaseBuildable::ResetEntity(); -} - -void AvHPhaseGate::SetEnabled(bool inEnabledState) -{ - if(inEnabledState != this->mEnabled) - { - if(inEnabledState) - { - // Start animating - this->pev->sequence = 0; - this->pev->frame = 0; - ResetSequenceInfo(); - - //this->pev->effects |= EF_BRIGHTLIGHT; - this->pev->effects |= EF_DIMLIGHT; - - UTIL_EmitAmbientSound( ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, ATTN_NORM, 0, 100); - - SetUse(&AvHPhaseGate::TeleportUse); - - this->mEnabled = true; - } - else - { - // Stop animating - this->pev->sequence = 1; - this->pev->frame = 0; - ResetSequenceInfo(); - - this->pev->effects &= ~EF_DIMLIGHT; - - UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, .5, SND_STOP, 100); - - SetUse(NULL); - - this->mEnabled = false; - } - } -} - -bool AvHPhaseGate::GetAreTeammatesBlocking(AvHPlayer* inPlayer, const Vector& inOrigin) const -{ - - // This is based off the logic of AvHSUKillPlayersTouchingPlayer so that - // the results are consistent. - - bool theResult = false; - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetIsRelevant() && theEntity->pev->team == inPlayer->pev->team) - { - float theDistanceToPlayer = VectorDistance(inOrigin, theEntity->pev->origin); - if(theDistanceToPlayer < 30) - { - if (gpGlobals->time - theEntity->GetTimeOfLastTeleport() < kPhaseGateAmnestyTime) - { - theResult = true; - } - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - return theResult; - -} - -void AvHPhaseGate::KillBuildablesTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor) -{ - - FOR_ALL_BASEENTITIES(); - AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); - if(theBuildable) - { - if (theBuildable->pev->iuser3 != AVH_USER3_HIVE && theBuildable->pev->iuser3 != AVH_USER3_PHASEGATE) - { - float theDistanceToPlayer = VectorDistance(inPlayer->pev->origin, theBuildable->pev->origin); - if(theDistanceToPlayer < 50) - { - theBuildable->TakeDamage(inInflictor, theBuildable->pev, 10000, DMG_GENERIC); - } - } - } - END_FOR_ALL_BASEENTITIES(); - -} -bool AvHPhaseGate::HasWarmedUp() const -{ - return this->mHasWarmedUp; -} -bool AvHPhaseGate::GetIsEnabled() const -{ - return this->GetIsBuilt() && this->mEnabled && !this->GetIsRecycling(); -} -void AvHPhaseGate::SetTimeOfLastDeparture(float timeOfLastDeparture) -{ - mTimeOfLastDeparture=timeOfLastDeparture; -} -bool AvHPhaseGate::IsReadyToUse() -{ - bool theReturn=false; - if ( (gpGlobals->time - mTimeOfLastDeparture) > BALANCE_VAR(kPhaseGateDepartureInterval) ) - { - theReturn=true; - } - return theReturn; -} - -void AvHPhaseGate::TeleportUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - AvHPlayer* thePlayer = dynamic_cast(pActivator); - CBasePlayerItem* thePlayerItem = dynamic_cast(pActivator); - - // Only players on the team of the person that created the phase gate can travel through, - bool theTeleportAllowed = (thePlayer != NULL) && (thePlayer->pev->team == this->pev->team) && thePlayer->GetIsAbleToAct(); - //bool theTeleportAllowed = thePlayerItem || (thePlayer && ((pActivator->pev->team == this->pev->team) && (this->pev->team != 0))); - if(theTeleportAllowed && this->GetIsEnabled()) - { - float theLastTeleportTime = thePlayer->GetTimeOfLastTeleport(); - theTeleportAllowed = (theLastTeleportTime == -1) || ((gpGlobals->time - theLastTeleportTime) >= BALANCE_VAR(kPhaseGateDelay)); - if(theTeleportAllowed) - { - if(!GetGameRules()->GetIsTesting()) - { - // Teleport to "next" gate. If there isn't more then one gate, respawn the player - vec3_t theOrigin; - - bool theDone = false; - bool theSuccess = false; - edict_t* thePhaseGateEdict = ENT(this->pev); - AvHPhaseGate *theTargetGate=0; - // Keep looping until we come back to ourself or we find another gate on our team to phase to - while(!theDone) - { - thePhaseGateEdict = FIND_ENTITY_BY_CLASSNAME(thePhaseGateEdict, kwsPhaseGate); - AvHPhaseGate* thePhaseGate = dynamic_cast(CBaseEntity::Instance(thePhaseGateEdict)); - - // This assert fails in normal operation, seemingly harmlessly - //ASSERT(thePhaseGate); - - if(thePhaseGateEdict == ENT(this->pev)) - { - theDone = true; - } - else if((thePhaseGateEdict->v.team == this->pev->team) && thePhaseGate && thePhaseGate->GetIsEnabled() && this->IsReadyToUse() ) - { - // Players come through on top of phase gate, plus a little above - theOrigin = thePhaseGateEdict->v.origin; - - theOrigin.z +=kRespawnFudgeFactorHeight; - - // Add in proper hull size so players don't get stuck - Vector thePlayerMinSize, thePlayerMaxSize; - thePlayer->GetSize(thePlayerMinSize, thePlayerMaxSize); - theOrigin.z += -thePlayerMinSize.z; - - //Elven Thief - Changed from AvHUGetHull(false to true to allow crouching marines access to phase gates. - // check out bug 487 - if(AvHSUGetIsEnoughRoomForHull(theOrigin, AvHMUGetHull(true, thePlayer->pev->iuser3), thePlayer->edict(), true, true)) - { - theSuccess = true; - theDone = true; - } - - // Now check to see if there are any teammates blocking who just phased in. - - if (GetAreTeammatesBlocking(thePlayer, theOrigin)) - { - theSuccess = false; - theDone = true; - } - - if ( theDone == true ) - { - theTargetGate=thePhaseGate; - } - - - } - } - - if(theSuccess) - { - // Mark the player as just having teleported so he doesn't teleport immediately again - thePlayer->SetPosition(theOrigin); - - thePlayer->pev->velocity = g_vecZero; - thePlayer->SetTimeOfLastTeleport(gpGlobals->time); - - int theFlags = 0;//thePlayer ? FEV_NOTHOST : 0; - this->SetTimeOfLastDeparture(gpGlobals->time); - AvHSUPlayPhaseInEffect(theFlags, this, thePlayer); - - AvHSUKillPlayersTouchingPlayer(thePlayer, this->pev); - KillBuildablesTouchingPlayer(thePlayer, this->pev); - - Vector theFadeColor; - theFadeColor.x = 235; - theFadeColor.y = 255; - theFadeColor.z = 255; - UTIL_ScreenFade(thePlayer, theFadeColor, .9f, 0.0f, 255, FFADE_IN); - - } - } - } - } -} - -void AvHPhaseGate::UpdateOnRecycle(void) -{ - this->SetEnabled(false); -} - -void AvHPhaseGate::UpdateOnRemove(void) -{ - // Make sure sound gets turned off when round ends without it being killed - if(this->pev) - { - UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, .5, SND_STOP, 100); - } -} - - -//AvHSiegeTurret::AvHSiegeTurret() -//{ -// float theStartTime = RANDOM_FLOAT(0, avh_siegerof.value); -// this->mTimeLastFired = gpGlobals->time - theStartTime; -// this->mTimeToConstruct = GetGameRules()->GetBuildTimeForMessageID(BUILD_SIEGE); -//} -// -//CBaseEntity* AvHSiegeTurret::BestVisibleEnemy(void) -//{ -// CBaseEntity *theCurrentEnemy = NULL; -// CBaseEntity *theBestEnemy = NULL; -// float theDistanceToBestEnemy = -1; -// -// while((theCurrentEnemy = UTIL_FindEntityInSphere(theCurrentEnemy, this->pev->origin, avh_siegemaxrange.value)) != NULL) -// { -// // If entity is a valid target and within our valid range -// if(this->GetIsValidTarget(theCurrentEnemy)) -// { -// // Is it closer than our current target? -// float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, theCurrentEnemy); -// if((theDistanceToBestEnemy == -1) || (theDistanceToCurrentEnemy < theDistanceToBestEnemy)) -// { -// theBestEnemy = theCurrentEnemy; -// theDistanceToBestEnemy = theDistanceToCurrentEnemy; -// } -// } -// } -// return theBestEnemy; -//} -// -//bool AvHSiegeTurret::GetIsValidTarget(CBaseEntity* inEntity) const -//{ -// bool theTargetIsValid = false; -// -// if(AvHDeployedTurret::GetIsValidTarget(inEntity)) -// { -// float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, inEntity); -// if(theDistanceToCurrentEnemy >= this->GetMinimumRange()) -// { -// // We have to see it as well -// Vector vecMid = this->pev->origin + this->pev->view_ofs; -// Vector vecMidEnemy = inEntity->BodyTarget(vecMid); -// if(FBoxVisible(this->pev, inEntity->pev, vecMidEnemy)) -// { -// theTargetIsValid = true; -// } -// } -// } -// return theTargetIsValid; -//} -// -//char* AvHSiegeTurret::GetActiveSound() const -//{ -// return kSiegeActive; -//} -// -//char* AvHSiegeTurret::GetAlertSound() const -//{ -// return kSiegeAlert; -//} -// -//char* AvHSiegeTurret::GetDeploySound() const -//{ -// return kSiegeDeploy; -//} -// -//char* AvHSiegeTurret::GetPingSound() const -//{ -// return kSiegePing; -//} -// -//int AvHSiegeTurret::GetPointValueOfTarget(void) const -//{ -// return 3; -//} -// -//int AvHSiegeTurret::GetMinimumRange() const -//{ -// return avh_siegeminrange.value; -//} -// -//bool AvHSiegeTurret::NeedsLineOfSight() const -//{ -// return true; -//} -// -//void AvHSiegeTurret::Precache(void) -//{ -// AvHDeployedTurret::Precache(); -// -// PRECACHE_MODEL(kSiegeTurretModel); -// PRECACHE_SOUND(kSiegeTurretFire1); -//} -// -// -//void AvHSiegeTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) -//{ -// // Only fire once every few seconds...this is hacky but there's no way to override think functions so it must be done -// // I wish it was easier to change the update rate but it's not so... -// if((gpGlobals->time - this->mTimeLastFired) > avh_siegerof.value) -// { -// // Find enemy player in range, ignore walls and everything else -// if(this->m_hEnemy) -// { -// if(this->GetIsValidTarget(this->m_hEnemy)) -// { -// // Apply damage, taking upgrade into account -// float theDamageMultiplier; -// AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser4, &theDamageMultiplier); -// float theDamage = theDamageMultiplier*avh_siegedamage.value; -// -// if(!GetGameRules()->GetIsTesting()) -// { -// ::RadiusDamage(this->m_hEnemy->pev->origin, this->pev, this->pev, theDamage, avh_siegesplashradius.value, CLASS_NONE, DMG_BLAST); -// } -// -// // Play fire sound -// EMIT_SOUND_DYN(ENT(this->pev), CHAN_AUTO, kSiegeTurretFire1, 1.0, ATTN_NORM, 0, PITCH_NORM); -// -// this->pev->effects &= ~EF_MUZZLEFLASH; -// -// // Send normal effect to all -// PLAYBACK_EVENT_FULL(0, this->m_hEnemy->edict(), gSiegeHitEventID, 0, this->m_hEnemy->pev->origin, this->m_hEnemy->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); -// -// if(this->m_hEnemy->IsPlayer()) -// { -// // Send personal view shake to recipient only (check for splash here, pass param to lessen effect for others?) -// // TODO: Use upgrade level to parameterize screen shake and fade? -// PLAYBACK_EVENT_FULL(FEV_HOSTONLY, this->m_hEnemy->edict(), gSiegeViewHitEventID, 0, this->m_hEnemy->pev->origin, this->m_hEnemy->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); -// -// Vector theFadeColor; -// theFadeColor.x = 255; -// theFadeColor.y = 100; -// theFadeColor.z = 100; -// UTIL_ScreenFade(this->m_hEnemy, theFadeColor, .3f, 0.0f, 255, FFADE_OUT); -// } -// } -// else -// { -// this->m_hEnemy = NULL; -// } -// } -// -// this->mTimeLastFired = gpGlobals->time; -// } -//} -// -//void AvHSiegeTurret::Spawn() -//{ -// this->Precache(); -// AvHDeployedTurret::Spawn(); -// -// SET_MODEL(ENT(this->pev), kSiegeTurretModel); -// UTIL_SetSize(pev, kSiegeMinSize, kSiegeMaxSize); -// -// this->pev->classname = MAKE_STRING(kwsSiegeTurret); -// -// this->pev->takedamage = 1; -// this->pev->health = (int)(avh_siegehealth.value); -// this->pev->armorvalue = (int)(avh_siegehealth.value); -//} - - - -AvHMarineBaseBuildable::AvHMarineBaseBuildable(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser4) : AvHBaseBuildable(inTechID, inMessageID, inClassName, inUser4) -{ - this->SetEnergy(0.0f); -} - -char* AvHMarineBaseBuildable::GetDeploySound() const -{ - return kMarineBuildingDeploy; -} - -bool AvHMarineBaseBuildable::GetIsTechnologyAvailable(AvHMessageID inMessageID) const -{ - bool theIsAvailable = AvHBaseBuildable::GetIsTechnologyAvailable(inMessageID); - - // Disable scanning if not enough energy - if(theIsAvailable && AvHSHUGetDoesTechCostEnergy(inMessageID)) - { - int theEnergyCost = GetGameRules()->GetCostForMessageID(inMessageID); - if(this->mEnergy < theEnergyCost) - { - theIsAvailable = false; - } - } - - return theIsAvailable; -} - -char* AvHMarineBaseBuildable::GetKilledSound() const -{ - return kMarineBuildingKilled; -} - -int AvHMarineBaseBuildable::GetPointValue() const -{ - return BALANCE_VAR(kScoringMarineBuildableValue); -} - -int AvHMarineBaseBuildable::GetTakeDamageAnimation() const -{ - return -1; -} - -void AvHMarineBaseBuildable::ResetEntity() -{ - AvHBaseBuildable::ResetEntity(); -} - -void AvHMarineBaseBuildable::SetEnergy(float inEnergy) -{ - this->mEnergy = max(min(inEnergy, kMarineStructureMaxEnergy), 0.0f); - - float theNormValue = this->mEnergy/kMarineStructureMaxEnergy; - - if(this->pev && this->GetIsBuilt()) - { - AvHSHUSetEnergyState(this->pev->iuser3, this->pev->fuser1, theNormValue); - } -} - -int AvHMarineBaseBuildable::TakeDamage(entvars_t* inInflictor, entvars_t* inAttacker, float inDamage, int inBitsDamageType) -{ - //UTIL_Sparks() - - return AvHBaseBuildable::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType); -} - -void AvHMarineBaseBuildable::TechnologyBuilt(AvHMessageID inMessageID) -{ - // Pay energy cost - if(AvHSHUGetDoesTechCostEnergy(inMessageID)) - { - int theEnergyCost = GetGameRules()->GetCostForMessageID(inMessageID); - float theNewEnergy = max(this->mEnergy - theEnergyCost, 0.0f); - this->SetEnergy(theNewEnergy); - - // Play animation? - this->PlayAnimationAtIndex(this->GetActiveAnimation(), true); - } -} - -// Marine buildings -const float kNukeThinkInterval = 1.5f; - -AvHNuke::AvHNuke() : AvHMarineBaseBuildable(TECH_NULL, BUILD_NUKE, kwsNuke, AVH_USER3_NUKE) -{ -} - -void AvHNuke::Precache() -{ - AvHMarineBaseBuildable::Precache(); - PRECACHE_UNMODIFIED_SOUND(kNukeActive); - PRECACHE_UNMODIFIED_SOUND(kNukeExplode); -} - -void AvHNuke::ActiveThink() -{ - float theThinkInterval = kNukeThinkInterval; - - // If not active - if(!this->mActive) - { - // Set construction complete - this->SetConstructionComplete(); - - // Play sounds - - // Set active - this->mActive = true; - - // Mark as a monster - SetBits(this->pev->flags, FL_MONSTER); - - this->mTimeActivated = gpGlobals->time; - } - - // Emit higher and higher frequency sound - float theTimeToDetonate = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID()); - - float thePercentDone = (gpGlobals->time - this->mTimeActivated)/theTimeToDetonate; - thePercentDone = min(max(0.0f, thePercentDone), 1.0f); - - int thePitch = 100 + thePercentDone*3; - EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, kNukeActive, 1.0, ATTN_NORM, 0, thePitch); - - // If active - if(this->mActive) - { - // If enough time has passed - ASSERT(this->mTimeActivated != -1); - - if(gpGlobals->time > (this->mTimeActivated + theTimeToDetonate)) - { - // Detonation visuals (any bigger magnitude then this and the explosion doesn't show up) - int theMagnitude = 300; - ExplosionCreate(this->pev->origin, this->pev->angles, this->edict(), theMagnitude, FALSE, this->pev->team); - - // Do damage (theDamage at epicenter, falls off from there, doesn't hurt people in water, only hurts people that can be seen by blast) - float theDamage = kNukeDamage; - float theRadius = kNukeRange; - ::RadiusDamage(this->pev->origin, this->pev, this->pev, theDamage, theRadius, CLASS_NONE, DMG_BLAST); - - // Play view shake here - float theShakeAmplitude = 100; - float theShakeFrequency = 150; - float theShakeDuration = 10.0f; - float theShakeRadius = 2000; - UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius); - - // Add white out effect for players in radius - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetIsRelevant() && !theEntity->GetIsInTopDownMode()) - { - // If near the epicenter, or if it's visible, we're blinded - bool theNearEpicenter = false; - float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); - if(theDistance < theRadius/2) - { - theNearEpicenter = true; - } - - bool theExplosionVisible = false; - if(theEntity->GetIsEntityInSight(this)) - { - theExplosionVisible = true; - } - - if(theNearEpicenter || theExplosionVisible) - { - Vector theFadeColor; - theFadeColor.x = 255; - theFadeColor.y = 255; - theFadeColor.z = 255; - UTIL_ScreenFade(theEntity, theFadeColor, 1.5f, .5f, 255, FFADE_IN); - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - //this->pev->flags |= EF_NODRAW; - SET_MODEL(ENT(pev), kNullModel); - this->pev->flags |= EF_BRIGHTFIELD; - this->pev->flags |= EF_BRIGHTLIGHT; - - // Remove entity - SetThink(&AvHNuke::DeathThink); - theThinkInterval = theShakeDuration; - - // Play special nuke sound (not playing for some reason) - EMIT_SOUND(this->edict(), CHAN_BODY, kNukeExplode, 1.0f, ATTN_IDLE); - } - } - - this->pev->nextthink = gpGlobals->time + theThinkInterval; -} - -void AvHNuke::DeathThink() -{ - UTIL_Remove(this); -} - -void AvHNuke::Spawn() -{ - AvHBaseBuildable::Spawn(); - - // Set ActiveThink - this->mActive = false; - this->mTimeActivated = -1; - - SetThink(&AvHNuke::ActiveThink); - this->pev->nextthink = gpGlobals->time + kNukeThinkInterval; -} - -char* AvHNuke::GetDeploySound() const -{ - return kNukeDeploy; -} - -char* AvHNuke::GetKilledSound() const -{ - return kNukeKilled; -} - - -const float kInfantryPortalThinkTime = 1.0f; -#define kInfantryPortalLightEffect EF_LIGHT - -AvHInfantryPortal::AvHInfantryPortal() : AvHMarineBaseBuildable(TECH_INFANTRYPORTAL, BUILD_INFANTRYPORTAL, kwsInfantryPortal, AVH_USER3_INFANTRYPORTAL) -{ -} - -void AvHInfantryPortal::Killed(entvars_t* inAttacker, int inGib) -{ - AvHBaseBuildable::Killed(inAttacker, inGib); - - GetGameRules()->MarkDramaticEvent(kIPDeathPriority, this->entindex(), inAttacker); -} - -float AvHInfantryPortal::GetReinforceTime() const -{ - float theReinforceTime = BALANCE_VAR(kMarineRespawnTime); - - if(GetGameRules()->GetCheatsEnabled()) - { - theReinforceTime = 2; - } - - theReinforceTime = max(0.0f, theReinforceTime); - - return theReinforceTime; -} - -void AvHInfantryPortal::PortalThink() -{ - this->UpdateReinforcements(); - - AvHBaseBuildable::AnimateThink(); - - this->pev->nextthink = gpGlobals->time + kInfantryPortalThinkTime; -} - - -void AvHInfantryPortal::ResetEntity() -{ - AvHMarineBaseBuildable::ResetEntity(); - AvHReinforceable::ResetEntity(); -} - -void AvHInfantryPortal::SetHasBeenBuilt() -{ - AvHBuildable::SetHasBeenBuilt(); - - SetThink(&AvHInfantryPortal::PortalThink); - this->pev->nextthink = gpGlobals->time + kInfantryPortalThinkTime; - // this->pev->effects |= kInfantryPortalLightEffect; -} - -void AvHInfantryPortal::Precache() -{ - AvHMarineBaseBuildable::Precache(); - - PRECACHE_UNMODIFIED_SOUND(kTransportSound); -} - - -void AvHInfantryPortal::CueRespawnEffect(AvHPlayer* inPlayer) -{ - // Playback teleporting event - PLAYBACK_EVENT_FULL(0, inPlayer->edict(), gTeleportEventID, 0, inPlayer->pev->origin, inPlayer->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - - EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kTransportSound, .8, ATTN_NORM); -} - -bool AvHInfantryPortal::GetCanReinforce() const -{ - return this->GetIsBuilt() && !GetGameRules()->GetIsCombatMode(); -} - -bool AvHInfantryPortal::GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const -{ - // Set player position to on top of gate - vec3_t thePosition = AvHSHUGetRealLocation(this->pev->origin, this->pev->mins, this->pev->maxs); - - Vector theIPMinSize, theIPMaxSize; - AvHSHUGetSizeForTech(BUILD_INFANTRYPORTAL, theIPMinSize, theIPMaxSize); - thePosition.z += theIPMaxSize.z; - - AvHPlayer* thePlayer = dynamic_cast(inPlayer); - ASSERT(thePlayer); - - // Respawn player on top of portal - Vector thePlayerMinSize, thePlayerMaxSize; - thePlayer->GetSize(thePlayerMinSize, thePlayerMaxSize); - - thePosition.z += (-thePlayerMinSize.z + kRespawnFudgeFactorHeight); - - VectorCopy(thePosition, outLocation); - - return true; -} - -AvHTeamNumber AvHInfantryPortal::GetReinforceTeamNumber() const -{ - return this->GetTeamNumber(); -} - -void AvHInfantryPortal::ResetReinforcingPlayer(bool inSuccess) -{ - // If we're respawning, we telefrag. Make sure to telefrag after the player's new location has been set - bool theTelefrag = false; - AvHPlayer* thePlayer = this->GetReinforcingPlayer(); - if(inSuccess && thePlayer) - { - theTelefrag = true; - } - - AvHReinforceable::ResetReinforcingPlayer(inSuccess); - - if(theTelefrag) - { - AvHSUKillPlayersTouchingPlayer(thePlayer, this->pev); - } -} - -void AvHInfantryPortal::UpdateOnRecycle(void) -{ - this->ResetReinforcingPlayer(false); - // this->pev->effects &= ~kInfantryPortalLightEffect; -} - -void AvHInfantryPortal::UpdateOnRemove(void) -{ - this->ResetReinforcingPlayer(false); - SetThink(NULL); -} - -int AvHInfantryPortal::GetIdleAnimation() const -{ - // AvHBaseBuildable randomly shows Idle2, which we don't have in this model. - return 2; -} - -int AvHInfantryPortal::GetIdle1Animation() const -{ - // AvHBaseBuildable randomly shows Idle2, which we don't have in this model. - return 2; -} - -int AvHInfantryPortal::GetIdle2Animation() const -{ - // AvHBaseBuildable randomly shows Idle2, which we don't have in this model. - return 2; -} - -// tankefugl: -int AvHInfantryPortal::GetDeployAnimation() const -{ - return 0; -} - -int AvHInfantryPortal::GetSpawnAnimation() const -{ - return 1; -} -// :tankefugl - -const int kCommandStationExitAnimation = 12; - -AvHCommandStation::AvHCommandStation() : AvHMarineBaseBuildable(TECH_COMMAND_CENTER, BUILD_COMMANDSTATION, kwsTeamCommand, AVH_USER3_COMMANDER_STATION) -{ - this->mCommanderAtThisStation = -1; - this->mTimeToPlayOnlineSound = -1; -} - -int AvHCommandStation::GetIdleAnimation() const -{ - int theAnimation = 2; - - // If we're in use - if(this->mCommanderAtThisStation != -1 || GetGameRules()->GetIsCombatMode()) - { - theAnimation = 3; - } - - return theAnimation; -} - -int AvHCommandStation::GetPointValue() const -{ - return BALANCE_VAR(kScoringCCValue); -} - -void AvHCommandStation::Killed( entvars_t *pevAttacker, int iGib ) -{ - this->SetInactive(); - - AvHMarineBaseBuildable::Killed(pevAttacker, iGib); -} - -int AvHCommandStation::ObjectCaps( void ) -{ - - // Recycled command stations cannot be used. - - int theObjectCaps = 0; - - if ( !(pev->effects & EF_NODRAW) ) - { - theObjectCaps |= FCAP_CONTINUOUS_USE; - } - - return theObjectCaps; - -} - -void AvHCommandStation::CommandTouch(CBaseEntity* pOther) -{ - AvHPlayer* thePlayer = dynamic_cast(pOther); - if(thePlayer) - { - AvHTeamNumber theStationTeamNumber = this->GetTeamNumber(); - if(thePlayer->pev->team == theStationTeamNumber) - { - // Check to see if we already have a commander and don't make the person a commander if so - if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0) - { - thePlayer->SendMessage(kHelpTextCSAttractMode, TOOLTIP); - } - } - } -} - -void AvHCommandStation::CommandUse( CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value ) -{ - AvHPlayer* thePlayer = dynamic_cast(pActivator); - - // Mapper-placed CCs can be killed but they don't go away - if(thePlayer && !(thePlayer->pev->flags & FL_FAKECLIENT) && !this->GetHasBeenKilled() && thePlayer->GetIsAbleToAct()) - { - AvHTeam* theTeam = thePlayer->GetTeamPointer(); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) - { - AvHTeamNumber theStationTeamNumber = this->GetTeamNumber(); - if(thePlayer->pev->team == theStationTeamNumber) - { - // Check to see if we already have a commander and don't make the person a commander if so - if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0) - { - string theErrorMessage; - if(thePlayer->GetCanCommand(theErrorMessage)) - { - if(this->pev->health > 0 && !this->GetIsRecycling()) - { - thePlayer->SetUser3(AVH_USER3_COMMANDER_PLAYER); - //thePlayer->SetSelection(this->entindex()); - - EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kCommandStationStartSound, .8, kCommandStationAttenuation); - - SetThink(&AvHCommandStation::CommanderUsingThink); - this->pev->nextthink = gpGlobals->time + kCommandStationThinkInterval; - - const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationUseTeamOne : kTargetCommandStationUseTeamTwo; - FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f); - - this->mCommanderAtThisStation = thePlayer->entindex(); - - this->PlayAnimationAtIndex(5, true); - - this->mTimeToPlayOnlineSound = this->GetTimeAnimationDone(); - - GetGameRules()->MarkDramaticEvent(kCCNewCommanderPriority, thePlayer, this); - } - else - { - thePlayer->SendMessage(kCommandStationDestroyed, TOOLTIP); - } - } - else - { - thePlayer->SendMessage(theErrorMessage.c_str()); - } - } - else - { - // The player somehow touches the command station while still a commander - if(thePlayer->GetUser3() != AVH_USER3_COMMANDER_PLAYER) - { - thePlayer->SendMessage(kCommandStationInUse, TOOLTIP); - } - } - } - else - { - thePlayer->SendMessage(kCommandStationForOtherTeam, TOOLTIP); - } - } - } -} - -void AvHCommandStation::Precache(void) -{ - AvHMarineBaseBuildable::Precache(); - - PRECACHE_UNMODIFIED_MODEL(kCommandStationModel); - PRECACHE_UNMODIFIED_SOUND(kCommandStationStartSound); - PRECACHE_UNMODIFIED_SOUND(kCommandStationEndSound); -} - -void AvHCommandStation::ActivateThink(void) -{ - // Don't allow use of the Command station in Combat mode - if(!GetGameRules()->GetIsCombatMode()) - { - SetTouch(&AvHCommandStation::CommandTouch); - SetUse(&AvHCommandStation::CommandUse); - } - - SetThink(&AvHBaseBuildable::AnimateThink); - this->pev->nextthink = gpGlobals->time + .1f; -} - -void AvHCommandStation::SetHasBeenBuilt() -{ - AvHBuildable::SetHasBeenBuilt(); - - SetThink(&AvHCommandStation::ActivateThink); - this->pev->nextthink = gpGlobals->time + 2.0f; -} - -void AvHCommandStation::EjectCommander() -{ - // If this command station had a commander, log him out! - if(this->mCommanderAtThisStation != -1) - { - // if the command station is killed, kick out any commander - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->pev->team == this->pev->team) - { - if(theEntity->GetUser3() == AVH_USER3_COMMANDER_PLAYER) - { - theEntity->StopTopDownMode(); - theEntity->SetUser3(AVH_USER3_MARINE_PLAYER); - - this->PlayAnimationAtIndex(kCommandStationExitAnimation, true); - - GetGameRules()->MarkDramaticEvent(kCCEjectPriority, theEntity, this); - - break; - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - } - - this->mCommanderAtThisStation = -1; -} - -char* AvHCommandStation::GetKilledSound() const -{ - return kCommandStationDeathSound; -} - - -void AvHCommandStation::SetInactive() -{ - AvHBaseBuildable::SetInactive(); - - this->EjectCommander(); -} - -void AvHCommandStation::Materialize() -{ - AvHMarineBaseBuildable::Materialize(); - - this->mCommanderAtThisStation = -1; - - //this->ResetEntity(); -} - -void AvHCommandStation::Spawn(void) -{ - AvHMarineBaseBuildable::Spawn(); - - this->Materialize(); -} - -int AvHCommandStation::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - int theReturnCode = 0; - - if(this->pev->health > 0) - { - theReturnCode = AvHBaseBuildable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); - - if(this->pev->health <= 0) - { - AvHTeamNumber theStationTeamNumber = (AvHTeamNumber)this->pev->team; - const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationDestroyedTeamOne : kTargetCommandStationDestroyedTeamTwo; - FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f); - - GetGameRules()->MarkDramaticEvent(kCCDeathPriority, this->entindex(), false, pevAttacker); - } - } - - return theReturnCode; -} - - -void AvHCommandStation::CommanderUsingThink(void) -{ - AvHTeamNumber theStationTeamNumber = this->GetTeamNumber(); - - // Check to see if we already have a commander and don't make the person a commander if so - if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0) - { - this->mCommanderAtThisStation = -1; - this->mTimeToPlayOnlineSound = -1; - - EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kCommandStationEndSound, .8, kCommandStationAttenuation); - this->PlayAnimationAtIndex(kCommandStationExitAnimation, true); - - //SetThink(NULL); - SetThink(&AvHBaseBuildable::AnimateThink); - this->pev->nextthink = gpGlobals->time + .1f; - } - else - { - if(this->mTimeToPlayOnlineSound > 0.0f) - { - if(gpGlobals->time > this->mTimeToPlayOnlineSound) - { - ASSERT(this->mCommanderAtThisStation > 0); - - // If enough time has passed and we haven't played online sound, play it - AvHPlayer* theCommander = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCommanderAtThisStation))); - if(theCommander) - { - theCommander->PlayHUDSound(HUD_SOUND_MARINE_CCONLINE); - } - - this->mTimeToPlayOnlineSound = -1; - } - } - - AvHBaseBuildable::AnimateThink(); - - this->pev->nextthink = gpGlobals->time + kCommandStationThinkInterval; - } -} - -bool AvHCommandStation::GetIsTechnologyAvailable(AvHMessageID inMessageID) const -{ - bool theTechIsAvailable = AvHMarineBaseBuildable::GetIsTechnologyAvailable(inMessageID); - - // Only allow CC to be recycled if it's unoccupied - if(inMessageID == BUILD_RECYCLE) - { - theTechIsAvailable = (this->mCommanderAtThisStation == -1); - } - - return theTechIsAvailable; -} - -AvHTurretFactory::AvHTurretFactory() : AvHMarineBaseBuildable(TECH_TURRET_FACTORY, BUILD_TURRET_FACTORY, kwsTurretFactory, AVH_USER3_TURRET_FACTORY) -{ -} - -void AvHTurretFactory::CheckTurretEnabledState() const -{ - // Check to see if turrets should come online - FOR_ALL_ENTITIES(kwsDeployedTurret, AvHMarineTurret*) - if(theEntity->pev->team == this->pev->team) - { - theEntity->CheckEnabledState(); - } - END_FOR_ALL_ENTITIES(kwsDeployedTurret); - - FOR_ALL_ENTITIES(kwsSiegeTurret, AvHSiegeTurret*) - if(theEntity->pev->team == this->pev->team) - { - theEntity->CheckEnabledState(); - } - END_FOR_ALL_ENTITIES(kwsSiegeTurret) -} - -void AvHTurretFactory::TriggerAddTech() const -{ - AvHBuildable::TriggerAddTech(); - - this->CheckTurretEnabledState(); -} - -void AvHTurretFactory::TriggerRemoveTech() const -{ - AvHBuildable::TriggerRemoveTech(); - - this->CheckTurretEnabledState(); -} - -int AvHTurretFactory::GetIdle1Animation() const -{ - int theAnimation = 3; - - // Different animation when electrified - if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_11)) - { - theAnimation = 12; - } - - return theAnimation; -} - -int AvHTurretFactory::GetIdle2Animation() const -{ - return this->GetIdle1Animation(); -} - -int AvHTurretFactory::GetResearchAnimation() const -{ - return 4; -} - -void AvHTurretFactory::SetHasBeenBuilt() -{ - AvHBuildable::SetHasBeenBuilt(); - - // Animate - SetThink(&AvHBaseBuildable::AnimateThink); - this->pev->nextthink = gpGlobals->time + .1f; -} - -bool AvHTurretFactory::GetSupportsTechID(AvHTechID inTechID) const -{ - bool theSuccess = AvHBuildable::GetSupportsTechID(inTechID); - - if(this->GetTechID() == TECH_ADVANCED_TURRET_FACTORY) - { - if(!theSuccess) - { - // Adv. turret factory also counts as a turret factory - theSuccess = (inTechID == TECH_TURRET_FACTORY); - } - } - - return theSuccess; -} - -void AvHTurretFactory::Upgrade() -{ - // Set iuser3 - this->pev->iuser3 = AVH_USER3_ADVANCED_TURRET_FACTORY; - - // Set classname - this->pev->classname = MAKE_STRING(kwsAdvancedTurretFactory); - - this->mMessageID = TURRET_FACTORY_UPGRADE; - this->SetTechID(TECH_ADVANCED_TURRET_FACTORY); - - this->SetSelectID(this->pev->iuser3); - - this->SetConstructionComplete(true); - - this->HealthChanged(); -} - - -AvHArmory::AvHArmory() : AvHMarineBaseBuildable(TECH_ARMORY, BUILD_ARMORY, kwsArmory, AVH_USER3_ARMORY) -{ -} - -int AvHArmory::GetSequenceForBoundingBox() const -{ - return 2; -} - -bool AvHArmory::GetSupportsTechID(AvHTechID inTechID) const -{ - bool theSuccess = AvHBuildable::GetSupportsTechID(inTechID); - - if(this->GetTechID() == TECH_ADVANCED_ARMORY) - { - // Adv. armory also counts as an armory - if(!theSuccess) - { - theSuccess = (inTechID == TECH_ARMORY); - } - } - - return theSuccess; -} - -int AvHArmory::GetIdle1Animation() const -{ - int theAnim = 2; - if(this->pev->iuser3 == AVH_USER3_ADVANCED_ARMORY) - { - theAnim = 3; - } - return theAnim; -} - -int AvHArmory::GetIdle2Animation() const -{ - return this->GetIdle1Animation(); -} - -int AvHArmory::GetActiveAnimation() const -{ - int theAnim = 5; - if(this->pev->iuser3 == AVH_USER3_ADVANCED_ARMORY) - { - theAnim = 12; - } - return theAnim; -} - -int AvHArmory::GetResearchAnimation() const -{ - return 5; -} - -void AvHArmory::Precache() -{ - AvHMarineBaseBuildable::Precache(); - - PRECACHE_UNMODIFIED_SOUND(kArmoryResupplySound); -} - -void AvHArmory::ResupplyUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue) -{ - AvHPlayer* thePlayer = dynamic_cast(inCaller); - - if(thePlayer && (thePlayer->pev->team == this->pev->team) && this->GetIsBuilt() && !this->GetIsRecycling() && thePlayer->GetIsAbleToAct()) - { - if(thePlayer->GetCanBeResupplied()) - { - // puzl: 1017 -// // Give health back occasionally -// bool theGiveHealthIfNeeded = (RANDOM_LONG(0, 3) == 0); -// - // resupply gives 10 health each use - thePlayer->Resupply(true); - - // Always play "getting ammo" sound when ammo or health are needed, to indicate to player when to stop pressing +use - EMIT_SOUND(thePlayer->edict(), CHAN_WEAPON, kArmoryResupplySound, .3f, ATTN_NORM); - } - } -} - -void AvHArmory::SetHasBeenBuilt() -{ - AvHBuildable::SetHasBeenBuilt(); - - SetUse(&AvHArmory::ResupplyUse); -} - -void AvHArmory::Upgrade() -{ - // Set iuser3 - this->pev->iuser3 = AVH_USER3_ADVANCED_ARMORY; - - // Set classname - this->pev->classname = MAKE_STRING(kwsAdvancedArmory); - - this->mMessageID = ARMORY_UPGRADE; - this->SetTechID(TECH_ADVANCED_ARMORY); - - this->SetSelectID(this->pev->iuser3); - - this->SetConstructionComplete(true); - - this->HealthChanged(); -} - - -AvHArmsLab::AvHArmsLab() : AvHMarineBaseBuildable(TECH_ARMSLAB, BUILD_ARMSLAB, kwsArmsLab, AVH_USER3_ARMSLAB) -{ -} - -int AvHArmsLab::GetResearchAnimation() const -{ - return 5; -} - -int AvHArmsLab::GetSequenceForBoundingBox() const -{ - return 1; -} - -AvHPrototypeLab::AvHPrototypeLab() : AvHMarineBaseBuildable(TECH_PROTOTYPE_LAB, BUILD_PROTOTYPE_LAB, kwsPrototypeLab, AVH_USER3_PROTOTYPE_LAB) -{ -} - - -const float kObservatoryThinkTime = 1.0f; -const float kObservatoryStartEnergy = 40; - -AvHObservatory::AvHObservatory() : AvHMarineBaseBuildable(TECH_OBSERVATORY, BUILD_OBSERVATORY, kwsObservatory, AVH_USER3_OBSERVATORY) -{ -} - -int AvHObservatory::GetSequenceForBoundingBox() const -{ - return 1; -} - -void AvHObservatory::ObservatoryThink() -{ - // Remove cloaking from nearby enemies - if ( !this->GetIsRecycling() ) { - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team)) - { - // Check that entity is in range of scan - float theDistance = VectorDistance2D(theEntity->pev->origin, this->pev->origin); - if(theDistance < BALANCE_VAR(kObservatoryXYDetectionRadius)) - { - // Remove cloaking, if player has it - theEntity->TriggerUncloak(); - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - } - - AvHBaseBuildable::AnimateThink(); - - // Update scanning energy - float theRate = kMarineStructureEnergyRate; - if(GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsCheatEnabled(kcSlowResearch)) - { - theRate *= 6; - } - - this->SetEnergy(this->mEnergy + (kObservatoryThinkTime*theRate)); - - this->pev->nextthink = gpGlobals->time + kObservatoryThinkTime; -} - -void AvHObservatory::Materialize() -{ - AvHMarineBaseBuildable::Materialize(); -} - -void AvHObservatory::ResetEntity() -{ - AvHMarineBaseBuildable::ResetEntity(); - - this->SetEnergy(0); -} - -void AvHObservatory::SetHasBeenBuilt() -{ - AvHBuildable::SetHasBeenBuilt(); - - SetThink(&AvHObservatory::ObservatoryThink); - this->pev->nextthink = gpGlobals->time + kObservatoryThinkTime; - - this->SetEnergy(kObservatoryStartEnergy); -} - -void AvHObservatory::Spawn() -{ - AvHMarineBaseBuildable::Spawn(); -} - -int AvHObservatory::GetActiveAnimation() const -{ - return 2; -} - -int AvHObservatory::GetIdle1Animation() const -{ - return 3; -} - -int AvHObservatory::GetIdle2Animation() const -{ - return 3; -} - -int AvHObservatory::GetResearchAnimation() const -{ - return 4; -} +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHMarineEquipment.cpp $ +// $Date: 2002/11/22 21:28:16 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHMarineEquipment.cpp,v $ +// Revision 1.50 2002/11/22 21:28:16 Flayra +// - mp_consistency changes +// +// Revision 1.49 2002/11/12 02:25:29 Flayra +// - HLTV updates +// +// Revision 1.48 2002/11/06 01:40:17 Flayra +// - Turrets now need an active turret factory to keep firing +// +// Revision 1.47 2002/11/03 04:50:43 Flayra +// - Hard-coded gameplay constants instead of putting in skill.cfg +// - Ammo and health now expire +// +// Revision 1.46 2002/10/28 20:35:46 Flayra +// - Allow HAs and jetpacks to only be picked up by marines +// +// Revision 1.45 2002/10/25 23:19:30 Flayra +// - Fixed bug where people were being telefragged improperly +// +// Revision 1.44 2002/10/24 21:32:09 Flayra +// - All heavy armor to be given via console +// - Fix for AFKers on inf portals, also for REIN players when recycling portals +// +// Revision 1.43 2002/10/20 21:10:48 Flayra +// - Optimizations +// +// Revision 1.42 2002/10/19 22:33:44 Flayra +// - Various server optimizations +// +// Revision 1.41 2002/10/18 22:20:49 Flayra +// - Reduce llamability of placing phases near drops or hazards +// +// Revision 1.40 2002/10/16 20:54:30 Flayra +// - Added phase gate sound +// - Fixed ghostly command station view model problem after building it +// +// Revision 1.39 2002/10/16 01:00:14 Flayra +// - Allow any jetpack to be picked up (needed for cheat, but this is the way all weapons work too, may need to be changed for marine vs. marine, not sure) +// +// Revision 1.38 2002/10/03 18:57:20 Flayra +// - Picking up heavy armor holsters your weapon for view model switch +// - Added "armory's upgrading, ammo not available" message but removed it for some reason (I think it was acting strange, like playing way too often) +// +// Revision 1.37 2002/09/25 21:12:26 Flayra +// - Undid solidity change (causes Sys_Error) +// +// Revision 1.36 2002/09/25 20:48:47 Flayra +// - Phase gates no longer solid +// +// Revision 1.35 2002/09/23 22:21:21 Flayra +// - Added jetpack and heavy armor +// - Added "cc online" sound +// - Turret factories now upgrade to advanced turret factories for siege +// - Added automatic resupply at armory, but removed it +// - Observatories scan in 2D now, to match commander range overlay +// +// Revision 1.34 2002/09/09 19:59:31 Flayra +// - Fixed up phase gates (no longer teleport you unless you have two, and they work properly now) +// - Refactored reinforcements +// - Fixed bug where secondary command station couldn't be built +// +// Revision 1.33 2002/08/31 18:01:02 Flayra +// - Work at VALVe +// +// Revision 1.32 2002/08/16 02:39:14 Flayra +// - Fixed command station problems after game ends +// +// Revision 1.31 2002/08/09 01:06:02 Flayra +// - Removed ability for command station to be resurrected +// - Fixed up phase gate effects +// +// Revision 1.30 2002/08/02 21:55:55 Flayra +// - Allow phase teleport to fail nicely. For some reason, players don't hear their own phase sound anymore, it seems to play at the point where they left instead of where they arrive +// +// Revision 1.29 2002/07/26 23:05:54 Flayra +// - Numerical event feedback +// - Started to add sparks when buildings were hit but didn't know the 3D point to play it at +// +// Revision 1.28 2002/07/24 18:45:42 Flayra +// - Linux and scripting changes +// +// Revision 1.27 2002/07/23 17:11:47 Flayra +// - Phase gates must be built and can be destroyed, observatories decloak aliens, hooks for fast reinforcements upgrade, nuke damage increased, commander banning +// +// Revision 1.26 2002/07/08 17:02:57 Flayra +// - Refactored reinforcements, updated entities for new artwork +// +// Revision 1.25 2002/07/01 21:29:59 Flayra +// - Removed phase particles, added implosion instead, don't select command station on log-in (messy for drawing building radii) +// +// Revision 1.24 2002/06/25 18:04:43 Flayra +// - Renamed some buildings, armory is now upgraded to advanced armory +// +// Revision 1.23 2002/06/10 19:58:17 Flayra +// - Scan update happens more often, in case aliens go cloaked during scan +// +// Revision 1.22 2002/06/03 16:50:35 Flayra +// - Renamed weapons factory and armory, added ammo resupplying +// +// Revision 1.21 2002/05/28 17:51:34 Flayra +// - Tried to make nuke sound play, extended shake duration to sound length, reinforcement refactoring, mark command stations as mapper placed, so they aren't deleted on level cleanup, support for point-entity buildable command stations +// +// Revision 1.20 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHGamerules.h" +#include "dlls/client.h" +#include "common/vec_op.h" +#include "common/vector_util.h" +#include "mod/AvHSoundListManager.h" +#include "dlls/weapons.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHConstants.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHMovementUtil.h" +#include "dlls/explode.h" +#include "dlls/weapons.h" +#include "util/MathUtil.h" +#include "mod/AvHTitles.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHParticleConstants.h" +#include "mod/AvHMarineTurret.h" +#include "mod/AvHSiegeTurret.h" +#include "mod/AvHHulls.h" + +//LINK_ENTITY_TO_CLASS(kwMine, AvHMine); +//LINK_ENTITY_TO_CLASS(kwDeployedTurret, AvHDeployedTurret); +//LINK_ENTITY_TO_CLASS(kwTurret, AvHTurret); +LINK_ENTITY_TO_CLASS(kwDeployedMine, AvHDeployedMine); +LINK_ENTITY_TO_CLASS(kwHealth, AvHHealth); +LINK_ENTITY_TO_CLASS(kwCatalyst, AvHCatalyst); +LINK_ENTITY_TO_CLASS(kwGenericAmmo, AvHGenericAmmo); +LINK_ENTITY_TO_CLASS(kwJetpack, AvHJetpack); +LINK_ENTITY_TO_CLASS(kwAmmoPack, AvHAmmoPack); +LINK_ENTITY_TO_CLASS(kwHeavyArmor, AvHHeavyArmor); + +LINK_ENTITY_TO_CLASS(kwWelder, AvHWelder); +LINK_ENTITY_TO_CLASS(kwScan, AvHScan); +LINK_ENTITY_TO_CLASS(kwPhaseGate, AvHPhaseGate); +//LINK_ENTITY_TO_CLASS(kwSiegeTurret, AvHSiegeTurret); +LINK_ENTITY_TO_CLASS(kwNuke, AvHNuke); + +LINK_ENTITY_TO_CLASS(kwInfantryPortal, AvHInfantryPortal); +LINK_ENTITY_TO_CLASS(kwTeamCommand, AvHCommandStation); +LINK_ENTITY_TO_CLASS(kwTurretFactory, AvHTurretFactory); +LINK_ENTITY_TO_CLASS(kwArmory, AvHArmory); +LINK_ENTITY_TO_CLASS(kwAdvancedArmory, AvHArmory); +//LINK_ENTITY_TO_CLASS(kwAdvancedArmory, AvHAdvancedArmory); +LINK_ENTITY_TO_CLASS(kwArmsLab, AvHArmsLab); +LINK_ENTITY_TO_CLASS(kwPrototypeLab, AvHPrototypeLab); +LINK_ENTITY_TO_CLASS(kwObservatory, AvHObservatory); + +extern int gTeleportEventID; +extern int gSiegeHitEventID; +extern int gSiegeViewHitEventID; +extern AvHSoundListManager gSoundListManager; + +void AvHDeployedMine::Precache(void) +{ + PRECACHE_UNMODIFIED_MODEL(kTripmineWModel); + PRECACHE_UNMODIFIED_MODEL(kTripmineW2Model); + PRECACHE_UNMODIFIED_SOUND(kTripmineDeploySound); + PRECACHE_UNMODIFIED_SOUND(kTripmineActivateSound); + PRECACHE_UNMODIFIED_SOUND(kTripmineChargeSound); + PRECACHE_UNMODIFIED_SOUND(kTripmineStepOnSound); +} + +void AvHDeployedMine::ActiveTouch(CBaseEntity* inOther) +{ + float kTimeBetweenBeeps = 3.0f; + + // Hack to circumvent the hack where owners can't collide. If this isn't done, then mines will blowup when touching the world sometimes. + //edict_t* theTempOwner = this->pev->owner; + //this->pev->owner = this->m_pRealOwner; + bool theEntityCanDoDamage = GetGameRules()->CanEntityDoDamageTo(this, inOther); + //this->pev->owner = theTempOwner; + // Check team here and only emit warning beep for friendlies + if(theEntityCanDoDamage && (this->pev->team != inOther->pev->team)) + { + GetGameRules()->MarkDramaticEvent(kMineExplodePriority, inOther, this); + this->Detonate(); + } + else + { + if(gpGlobals->time > this->mLastTimeTouched + kTimeBetweenBeeps) + { + // Only players trigger this, not buildings or other mines + AvHPlayer* thePlayer = dynamic_cast(inOther); + if(thePlayer) + { + // Play warning proximity beep + EMIT_SOUND( ENT(pev), CHAN_BODY, kTripmineStepOnSound, 0.5, ATTN_NORM ); // shut off chargeup + this->mLastTimeTouched = gpGlobals->time; + } + } + } +} + +// Ripped from old grenade +void AvHDeployedMine::Explode(TraceResult* inTrace, int inBitsDamageType) +{ + float flRndSound;// sound randomizer + + pev->model = iStringNull;//invisible + pev->solid = SOLID_NOT;// intangible + + float theDamageModifier; + int theUpgradeLevel = AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageModifier); + float theDamage = this->pev->dmg*theDamageModifier; + + pev->takedamage = DAMAGE_NO; + + // TODO: Look at upgrade and mark up damage + + // Pull out of the wall a bit + if ( inTrace->flFraction != 1.0 ) + { + pev->origin = inTrace->vecEndPos + (inTrace->vecPlaneNormal * (theDamage - 24) * 0.6); + } + + int iContents = UTIL_PointContents ( pev->origin ); + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound + WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + if (iContents != CONTENTS_WATER) + { + WRITE_SHORT( g_sModelIndexFireball ); + } + else + { + WRITE_SHORT( g_sModelIndexWExplosion ); + } + WRITE_BYTE( (theDamage - 50) * .60 ); // scale * 10 + WRITE_BYTE( 15 ); // framerate + WRITE_BYTE( TE_EXPLFLAG_NONE ); + MESSAGE_END(); + + CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, NORMAL_EXPLOSION_VOLUME, 3.0 ); + + //RadiusDamage(this->pev, this->pevOwner, theDamage, CLASS_NONE, inBitsDamageType); + int theRadius = BALANCE_VAR(kMineRadius); + RadiusDamage(this->pev->origin, this->pev, this->mPlacer, theDamage, theRadius, CLASS_NONE, inBitsDamageType); + + // Play view shake here + float theShakeAmplitude = 80; + float theShakeFrequency = 100; + float theShakeDuration = 1.0f; + float theShakeRadius = 700; + UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius); + + if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 ) + { + UTIL_DecalTrace( inTrace, DECAL_SCORCH1 ); + } + else + { + UTIL_DecalTrace( inTrace, DECAL_SCORCH2 ); + } + + flRndSound = RANDOM_FLOAT( 0 , 1 ); + + switch ( RANDOM_LONG( 0, 2 ) ) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris1.wav", 0.55, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris2.wav", 0.55, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris3.wav", 0.55, ATTN_NORM); break; + } + + pev->effects |= EF_NODRAW; + SetThink( &CGrenade::Smoke ); + pev->velocity = g_vecZero; + pev->nextthink = gpGlobals->time + 0.3; + + if (iContents != CONTENTS_WATER) + { + int sparkCount = RANDOM_LONG(0,3); + for ( int i = 0; i < sparkCount; i++ ) + Create( "spark_shower", pev->origin, inTrace->vecPlaneNormal, NULL ); + } +} + +void AvHDeployedMine::Smoke(void) +{ + if (UTIL_PointContents(this->pev->origin ) == CONTENTS_WATER) + { + UTIL_Bubbles(this->pev->origin - Vector( 64, 64, 64 ), this->pev->origin + Vector( 64, 64, 64 ), 100 ); + } + else + { + float theDamageModifier; + int theUpgradeLevel = AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageModifier); + int theDamage = this->pev->dmg*theDamageModifier; + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, this->pev->origin); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( this->pev->origin.x ); + WRITE_COORD( this->pev->origin.y ); + WRITE_COORD( this->pev->origin.z ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( (theDamage - 50) * 0.80 ); // scale * 10 + WRITE_BYTE( 12 ); // framerate + MESSAGE_END(); + } + UTIL_Remove( this ); +} + +void AvHDeployedMine::Killed(entvars_t* inAttacker, int inGib) +{ + this->Detonate(); + + CBasePlayerItem::Killed(inAttacker, inGib); +} + +int AvHDeployedMine::TakeDamage(entvars_t *inInflictor, entvars_t *inAttacker, float inDamage, int inBitsDamageType) +{ + // Don't include this for potential overflow reasons + //UTIL_Sparks(this->pev->origin); + + return CBasePlayerItem::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType); +} + +void AvHDeployedMine::Detonate() +{ + // Stop charging up + EMIT_SOUND(ENT(this->pev), CHAN_BODY, "common/null.wav", 0.5, ATTN_NORM ); + + if(!this->mDetonated && this->mPoweredUp) + { + TraceResult tr; + UTIL_TraceLine(this->pev->origin + this->mVecDir * 8, this->pev->origin - this->mVecDir * 64, dont_ignore_monsters, ENT(this->pev), &tr); + + this->Explode(&tr, NS_DMG_NORMAL); + + this->mDetonated = true; + } +} + +const float kTripmineActiveThinkTime = .8f; +const float kTripminePowerUpThinkTime = .2f; +const float kTripminePowerUpTime = 3.8f; +const float kTripmineFailTime = 20.0f; + +void AvHDeployedMine::SetPlacer(entvars_t* inPlacer) +{ + this->mPlacer = inPlacer; +} + +void AvHDeployedMine::PowerupThink() +{ + // Find an owner + if(this->mOwner == NULL) + { + edict_t* theOldOwner = this->pev->owner; + this->pev->owner = NULL; + + TraceResult tr; + UTIL_TraceLine(this->pev->origin + this->mVecDir * 8, this->pev->origin - this->mVecDir * 32, dont_ignore_monsters, ENT(this->pev), &tr); + + if (tr.fStartSolid || (theOldOwner && tr.pHit == theOldOwner)) + { + this->pev->owner = theOldOwner; + } + else if (tr.flFraction < 1.0) + { + this->pev->owner = tr.pHit; + this->mOwner = CBaseEntity::Instance(this->pev->owner); + this->mOwnerOrigin = this->mOwner->pev->origin; + this->mOwnerAngles = this->mOwner->pev->angles; + } + else + { + STOP_SOUND(ENT(this->pev), CHAN_VOICE, kTripmineDeploySound); + STOP_SOUND(ENT(this->pev), CHAN_BODY, kTripmineChargeSound); + + SetThink(&CBaseEntity::SUB_Remove); + this->pev->nextthink = gpGlobals->time + 0.1; + + ALERT(at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", this->pev->origin.x, this->pev->origin.y, this->pev->origin.z); + //KillBeam(); + return; + } + } + else + { + this->DetonateIfOwnerInvalid(); + } + + if(!this->mPoweredUp) + { + if(gpGlobals->time > (this->mTimePlaced + kTripminePowerUpTime)) + { + //if(this->pev->solid == SOLID_BBOX) + //{ + // play enabled sound + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, kTripmineActivateSound, 0.5, ATTN_NORM, 1.0, 75 ); + + SetTouch(&AvHDeployedMine::ActiveTouch); + + SetThink(&AvHDeployedMine::ActiveThink); + this->pev->nextthink = gpGlobals->time + kTripmineActiveThinkTime; + + this->mPoweredUp = true; + //} + } + } + + if(!this->mPoweredUp) + { + this->pev->nextthink = gpGlobals->time + kTripminePowerUpThinkTime; + } +} + +void AvHDeployedMine::DetonateIfOwnerInvalid() +{ + if((this->mOwner == NULL) || (this->mOwner->pev->origin != this->mOwnerOrigin) || (this->mOwner->pev->angles != this->mOwnerAngles)) + { + this->Detonate(); + } +} + +// Check to see if our position is no longer valid +void AvHDeployedMine::ActiveThink() +{ + this->DetonateIfOwnerInvalid(); + this->pev->nextthink = gpGlobals->time + kTripmineActiveThinkTime; +} + +void AvHDeployedMine::Spawn(void) +{ + this->Precache(); + + this->pev->movetype = MOVETYPE_FLY; + this->pev->solid = SOLID_BBOX; + this->pev->classname = MAKE_STRING(kwsDeployedMine); + this->pev->iuser3 = AVH_USER3_MINE; + + SET_MODEL(ENT(pev), kTripmineWModel); + + UTIL_SetSize(this->pev, kMineMinSize, kMineMaxSize); + UTIL_SetOrigin(this->pev, this->pev->origin ); + + // Can't emit beep until active + this->mTimePlaced = gpGlobals->time; + this->mLastTimeTouched = this->mTimePlaced; + + SetThink(&AvHDeployedMine::PowerupThink); + this->pev->nextthink = gpGlobals->time + kTripminePowerUpThinkTime; + + // give them hit points + this->pev->takedamage = DAMAGE_YES; + this->pev->health = BALANCE_VAR(kMineHealth); + this->pev->dmg = BALANCE_VAR(kMineDamage); + + // play deploy sound + EMIT_SOUND( ENT(this->pev), CHAN_VOICE, kTripmineDeploySound, .8f, ATTN_NORM ); + EMIT_SOUND( ENT(this->pev), CHAN_BODY, kTripmineChargeSound, .5f, ATTN_NORM ); // chargeup + + UTIL_MakeAimVectors(this->pev->angles); + this->mVecDir = gpGlobals->v_forward; + this->mVecEnd = this->pev->origin + this->mVecDir * 2048; + + this->mDetonated = false; + this->mPoweredUp = false; + this->mOwner = NULL; + this->mPlacer = NULL; +} + +AvHPlayerEquipment::AvHPlayerEquipment() +{ + this->mIsPersistent = false; + this->mLifetime = -1; +} + +void AvHPlayerEquipment::KeyValue(KeyValueData* pkvd) +{ + // Any entity placed by the mapper is persistent + this->SetPersistent(); + + if(FStrEq(pkvd->szKeyName, "teamchoice")) + { + //this->mTeam = (AvHTeamNumber)(atoi(pkvd->szValue)); + this->pev->team = (AvHTeamNumber)(atoi(pkvd->szValue)); + + pkvd->fHandled = TRUE; + } + else if(FStrEq(pkvd->szKeyName, "angles")) + { + // TODO: Insert code here + //pkvd->fHandled = TRUE; + int a = 0; + } + else if(FStrEq(pkvd->szKeyName, "lifetime")) + { + this->mLifetime = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBasePlayerItem::KeyValue(pkvd); + } +} + +// 0 means never expire, -1 means no value was set so use default +int AvHPlayerEquipment::GetLifetime() const +{ + int theLifetime = this->mLifetime; + + if(theLifetime < 0) + { + theLifetime = AvHSUGetWeaponStayTime(); + } + + return theLifetime; +} + +bool AvHPlayerEquipment::GetIsPersistent() const +{ + return this->mIsPersistent; +} + +void AvHPlayerEquipment::SetPersistent() +{ + this->mIsPersistent = true; +} + +void AvHHealth::Precache(void) +{ + PRECACHE_UNMODIFIED_MODEL(kHealthModel); + PRECACHE_UNMODIFIED_SOUND(kHealthPickupSound); +} + +void AvHHealth::Spawn(void) +{ + this->Precache(); + + SET_MODEL(ENT(pev), kHealthModel); + this->pev->movetype = MOVETYPE_TOSS; + this->pev->solid = SOLID_TRIGGER; + this->pev->framerate = 1.0; + + UTIL_SetSize(pev, kHealthMinSize, kHealthMaxSize); + UTIL_SetOrigin( pev, pev->origin ); + + SetTouch(&AvHHealth::Touch); + + // Expire after a time. + int theLifetime = this->GetLifetime(); + if(theLifetime > 0) + { + SetThink(&AvHHealth::SUB_Remove); + this->pev->nextthink = gpGlobals->time + theLifetime; + } + + this->pev->iuser3 = AVH_USER3_MARINEITEM; +} + +BOOL AvHHealth::GiveHealth(CBaseEntity* inOther, float points) +{ + BOOL theSuccess = FALSE; + +// : 1017 +// Amount of health to give is now a paramater to allow us to vary the resupply amount for the armoury + +// float thePointsPerHealth = BALANCE_VAR(kPointsPerHealth) + + + AvHPlayer* thePlayer = dynamic_cast(inOther); + if(thePlayer && thePlayer->GetIsRelevant() && thePlayer->GetIsMarine()) + { + float thePlayerMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, thePlayer->GetUser3(), thePlayer->GetExperienceLevel()); + if(thePlayer->pev->health < thePlayerMaxHealth) + { + float thePointsGiven = min(points, (thePlayerMaxHealth - thePlayer->pev->health)); + + thePlayer->pev->health += thePointsGiven; + + if(ns_cvar_float(&avh_drawdamage)) + { + thePlayer->PlaybackNumericalEvent(kNumericalInfoHealthEvent, thePointsGiven); + } + + // Remove parasite if player has one + //int& theUser4 = thePlayer->pev->iuser4; + //SetUpgradeMask(&theUser4, MASK_PARASITED, false); + + EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kHealthPickupSound, 1, ATTN_NORM); + + theSuccess = TRUE; + } + } + + return theSuccess; +} + +void AvHHealth::Touch(CBaseEntity* inOther) +{ + // : 1017 medpack health amount + if(AvHHealth::GiveHealth(inOther, BALANCE_VAR(kPointsPerHealth))) + { + UTIL_Remove(this); + } +} + + +void AvHCatalyst::Precache(void) +{ + PRECACHE_UNMODIFIED_MODEL(kCatalystModel); + PRECACHE_UNMODIFIED_SOUND(kCatalystPickupSound); +} + +void AvHCatalyst::Spawn(void) +{ + this->Precache(); + + SET_MODEL(ENT(pev), kCatalystModel); + this->pev->movetype = MOVETYPE_TOSS; + this->pev->solid = SOLID_TRIGGER; + + UTIL_SetSize(pev, kCatalystMinSize, kCatalystMaxSize); + UTIL_SetOrigin( pev, pev->origin ); + + SetTouch(&AvHCatalyst::Touch); + + // Expire after a time. + int theLifetime = this->GetLifetime(); + if(theLifetime > 0) + { + SetThink(&AvHCatalyst::SUB_Remove); + this->pev->nextthink = gpGlobals->time + theLifetime; + } + + this->pev->iuser3 = AVH_USER3_MARINEITEM; +} + +BOOL AvHCatalyst::GiveCatalyst(CBaseEntity* inOther) +{ + BOOL theSuccess = FALSE; + + float theCatalystDuration = BALANCE_VAR(kCatalystDuration); + + AvHPlayer* thePlayer = dynamic_cast(inOther); + if(thePlayer && thePlayer->GetIsRelevant() && thePlayer->GetIsMarine() && !thePlayer->GetIsCatalysted()) + { + //// The player takes damage too + //float theDamagePercent = BALANCE_VAR(kCatalystDamagePercent); + //float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3); + //float theDamage = theDamagePercent*theMaxHealth; + // + //// Never kill the player + //theDamage = min(theDamage, thePlayer->pev->health - 1); + //thePlayer->TakeDamage(thePlayer->pev, thePlayer->pev, theDamage, DMG_GENERIC | DMG_IGNOREARMOR); + + EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kCatalystPickupSound, 1, ATTN_NORM); + + thePlayer->SetIsCatalysted(true, theCatalystDuration); + + theSuccess = TRUE; + } + + return theSuccess; +} + +void AvHCatalyst::Touch(CBaseEntity* inOther) +{ + if(AvHCatalyst::GiveCatalyst(inOther)) + { + UTIL_Remove(this); + } +} + +void AvHHeavyArmor::Precache(void) +{ + PRECACHE_UNMODIFIED_MODEL(kHeavyModel); + PRECACHE_UNMODIFIED_SOUND(kHeavyPickupSound); +} + +void AvHHeavyArmor::Spawn(void) +{ + this->Precache(); + + SET_MODEL(ENT(pev), kHeavyModel); + this->pev->movetype = MOVETYPE_TOSS; + this->pev->solid = SOLID_TRIGGER; + UTIL_SetSize(pev, kHeavyMinSize, kHeavyMaxSize); + UTIL_SetOrigin( pev, pev->origin ); + + SetTouch(&AvHHeavyArmor::Touch); + + this->pev->iuser3 = AVH_USER3_HEAVY; +} + +void AvHHeavyArmor::Touch(CBaseEntity* inOther) +{ + AvHPlayer* thePlayer = dynamic_cast(inOther); + if(thePlayer && thePlayer->GetIsRelevant()) + { + if(thePlayer->GetIsMarine()) + { + // Check to make sure they don't have heavy armor or jetpack already + if((!thePlayer->GetHasJetpack() || GetGameRules()->GetIsCombatMode()) && !thePlayer->GetHasHeavyArmor())//: ignore in combat mode since were trying to touch it. + { + // Needed because view model changes + if(thePlayer->HolsterWeaponToUse()) + { + // Give player heavy armor + SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_7, false); + SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_13); + + // Mark player with heavy armor + thePlayer->EffectivePlayerClassChanged(); + + // Set new armor value + thePlayer->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3); + + EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kHeavyPickupSound, 1, ATTN_NORM); + + UTIL_Remove(this); + } + } + } + } +} + +void AvHJetpack::Precache(void) +{ + PRECACHE_UNMODIFIED_MODEL(kJetpackModel); + PRECACHE_UNMODIFIED_SOUND(kJetpackPickupSound); +} + +void AvHJetpack::Spawn(void) +{ + this->Precache(); + + SET_MODEL(ENT(pev), kJetpackModel); + this->pev->movetype = MOVETYPE_TOSS; + this->pev->solid = SOLID_TRIGGER; + UTIL_SetSize(pev, kJetpackMinSize, kJetpackMaxSize); + UTIL_SetOrigin( pev, pev->origin ); + + SetTouch(&AvHJetpack::Touch); + + this->pev->iuser3 = AVH_USER3_JETPACK; +} + +void AvHJetpack::Touch(CBaseEntity* inOther) +{ + AvHPlayer* thePlayer = dynamic_cast(inOther); + if(thePlayer && thePlayer->GetIsRelevant()) + { + if(thePlayer->GetIsMarine()) + { + // Check to make sure they don't have heavy armor or jetpack already + if((!thePlayer->GetHasHeavyArmor() || GetGameRules()->GetIsCombatMode()) && !thePlayer->GetHasJetpack())//: ignore in combat mode since were trying to touch it. + { + if(thePlayer->HolsterWeaponToUse()) + { + // Give player jetpack + SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_13, false); + SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_7); + + // Mark player with jetpack + thePlayer->EffectivePlayerClassChanged(); + + EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kJetpackPickupSound, 1, ATTN_NORM); + + // Set new armor value + thePlayer->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3); + + // Set full energy to start + thePlayer->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; + + + UTIL_Remove(this); + } + } + } + } +} + + +void AvHAmmoPack :: Precache( void ) +{ + PRECACHE_UNMODIFIED_MODEL(kAmmoPackModel); + PRECACHE_UNMODIFIED_SOUND(kAmmoPackPickupSound); +} + +void AvHAmmoPack :: Spawn( void ) +{ + this->Precache(); + + SET_MODEL(ENT(pev), kAmmoPackModel); + + this->pev->movetype = MOVETYPE_TOSS; + this->pev->solid = SOLID_TRIGGER; + this->m_flNoTouch = gpGlobals->time + 0.25; + + UTIL_SetSize(pev, kAmmoPackMinSize, kAmmoPackMaxSize); + UTIL_SetOrigin( pev, pev->origin ); + + SetTouch(&AvHAmmoPack::Touch); +} + +void AvHAmmoPack :: Touch( CBaseEntity *inOther) +{ + if(this->m_flNoTouch > gpGlobals->time) + return; + + //Dont touch non-players + if(!inOther->IsPlayer()) + return; + + AvHPlayer *thePlayer = dynamic_cast(inOther); + + //if they dont have a weapon that uses this ammo, dont pick up the ammo pack. + if ( !(thePlayer->pev->weapons & (1<GiveAmmo(this->m_iAmmoAmt, this->m_szAmmoType, this->m_iMaxAmmo) != -1) + { + EMIT_SOUND(ENT(thePlayer->pev), CHAN_ITEM, kAmmoPackPickupSound, 1, ATTN_NORM); + thePlayer->PlaybackNumericalEvent(kNumericalInfoAmmoEvent, 0); + UTIL_Remove(this); + } +} + +BOOL AvHGenericAmmo::GiveAmmo(CBaseEntity* inOther) +{ + // Give ammo to the player. It will be added as generic ammo, added as one clip to + // the player's current weapon. + if (inOther->GiveAmmo( 0, kwsGenericAmmo, 0 ) != -1) + { + EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kAmmoPickupSound, 1, ATTN_NORM); + + AvHPlayer* thePlayer = dynamic_cast(inOther); + if(thePlayer) + { + // Just say "ammo received" instead of number of bullets, too confusing + thePlayer->PlaybackNumericalEvent(kNumericalInfoAmmoEvent, 0); + } + + return TRUE; + } + + return FALSE; +} + +BOOL AvHGenericAmmo::AddAmmo( CBaseEntity *pOther ) +{ + if(this->mDropped) + { + return GiveAmmo(pOther); + } + + return FALSE; +} + +void AvHGenericAmmo::Dropped(void) +{ + this->mDropped = true; + SetThink(NULL); + + // Expire after a time + SetThink(&AvHGenericAmmo::SUB_Remove); + this->pev->nextthink = gpGlobals->time + AvHSUGetWeaponStayTime(); +} + +void AvHGenericAmmo::Precache( void ) +{ + PRECACHE_UNMODIFIED_MODEL(kAmmoModel); + PRECACHE_UNMODIFIED_SOUND(kAmmoPickupSound); +} + +void AvHGenericAmmo::Spawn( void ) +{ + Precache( ); + SET_MODEL(ENT(pev), kAmmoModel); + UTIL_SetSize(pev, kAmmoMinSize, kAmmoMaxSize); + + CBasePlayerAmmo::Spawn( ); + this->mDropped = false; + SetThink(&AvHGenericAmmo::Dropped); + this->pev->nextthink = gpGlobals->time + .15f; + + this->pev->iuser3 = AVH_USER3_MARINEITEM; +} + + +const float kScanThinkTime = .5f; + +AvHScan::AvHScan() +{ +} + +void AvHScan::Precache(void) +{ + CBaseAnimating::Precache(); + + PRECACHE_UNMODIFIED_MODEL(kScanModel); + PRECACHE_UNMODIFIED_SOUND(kScanSound); +} + +void AvHScan::Spawn(void) +{ +// this->Precache(); +// CBaseAnimating::Spawn(); +// +// SET_MODEL(ENT(this->pev), kScanModel); +// +// this->pev->movetype = MOVETYPE_NONE; +// this->pev->solid = SOLID_NOT; +// +// this->pev->classname = MAKE_STRING(kwsScan); +// +// this->pev->takedamage = DAMAGE_NO; +// +// // Start animating +// this->pev->sequence = 0; +// this->pev->frame = 0; +// ResetSequenceInfo(); + + this->pev->effects |= (/*EF_BRIGHTFIELD |*/ EF_DIMLIGHT); + + this->mTimeCreated = gpGlobals->time; + + SetThink(&AvHScan::ScanThink); + this->pev->nextthink = gpGlobals->time + GetGameRules()->GetFirstScanThinkTime(); + + EMIT_SOUND(this->edict(), CHAN_AUTO, kScanSound, 1.0f, ATTN_NORM); + + AvHSUPlayParticleEvent(kpsScanEffect, this->edict(), this->pev->origin); +} + +void AvHScan::ScanThink() +{ + // Remove cloaking from nearby enemies + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team)) + { + // Check that entity is in range of scan + float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); + if(theDistance < BALANCE_VAR(kScanRadius)) + { + // Remove cloaking, if player has it + theEntity->TriggerUncloak(); + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + // Look in sphere for cloakables + CBaseEntity* theSphereEntity = NULL; + while ((theSphereEntity = UTIL_FindEntityInSphere(theSphereEntity, this->pev->origin, BALANCE_VAR(kScanRadius))) != NULL) + { + if(!AvHSUGetIsExternalClassName(STRING(theSphereEntity->pev->classname))) + { + // TODO: Check team here? + AvHCloakable* theCloakable = dynamic_cast(theSphereEntity); + if(theCloakable) + { + theCloakable->Uncloak(); + } + } + } + + float theScanTime = GetGameRules()->GetBuildTimeForMessageID(BUILD_SCAN); + if(gpGlobals->time < (this->mTimeCreated + theScanTime)) + { + this->pev->nextthink = gpGlobals->time + kScanThinkTime; + } + else + { + UTIL_Remove(this); + } +} + + + +AvHPhaseGate::AvHPhaseGate() : AvHMarineBaseBuildable(TECH_PHASE_GATE, BUILD_PHASEGATE, kwsPhaseGate, AVH_USER3_PHASEGATE) +{ + this->mEnabled = false; + this->mTimeOfLastDeparture=0.0f; + this->mHasWarmedUp=false; +} + +int AvHPhaseGate::GetSequenceForBoundingBox() const +{ + return 1; +} + +void AvHPhaseGate::Killed(entvars_t* inAttacker, int inGib) +{ + this->SetEnabled(false); + + AvHMarineBaseBuildable::Killed(inAttacker, inGib); + + GetGameRules()->MarkDramaticEvent(kPGDeathPriority, this->entindex(), inAttacker); +} + +void AvHPhaseGate::Precache(void) +{ + CBaseAnimating::Precache(); + + PRECACHE_UNMODIFIED_MODEL(kPhaseGateModel); + PRECACHE_UNMODIFIED_SOUND(kPhaseGateSound); + PRECACHE_UNMODIFIED_SOUND(kPhaseGateTransportSound); +} + +void AvHPhaseGate::SetHasBeenBuilt() +{ + AvHBuildable::SetHasBeenBuilt(); + + // Include a "warm-up" time so movement chambers don't teleport the player immediately + this->mTimeOfLastDeparture=gpGlobals->time; + SetThink(&AvHPhaseGate::IdleThink); + + this->pev->nextthink = gpGlobals->time + kBuildingUseWarmupTime; +} + +int AvHPhaseGate::GetIdleAnimation() const +{ + return 0; +} + +void AvHPhaseGate::IdleThink() +{ + bool theIsEnabled = false; + this->mHasWarmedUp=true; + // Check if there are any other phase gates on our team and set our enabled state accordingly + bool theDone = false; + bool potentialDeadlock=false; + int theNumPhaseGates=0; + if(this->GetIsBuilt() && !this->GetIsRecycling()) + { + edict_t* thePhaseGateEdict = ENT(this->pev); + + // Keep looping until we come back to ourself or we find another gate on our team to phase to + while(!theDone) + { + thePhaseGateEdict = FIND_ENTITY_BY_CLASSNAME(thePhaseGateEdict, kwsPhaseGate); + AvHPhaseGate* thePhaseGate = dynamic_cast(CBaseEntity::Instance(thePhaseGateEdict)); + + // This assert fails in normal operation, seemingly harmlessly + //ASSERT(thePhaseGate); + + if(thePhaseGateEdict == ENT(this->pev)) + { + theDone = true; + } + else if((thePhaseGateEdict->v.team == this->pev->team) && thePhaseGate && thePhaseGate->GetIsBuilt() && !thePhaseGate->GetIsRecycling() && thePhaseGate->HasWarmedUp() ) + { + theIsEnabled = true; + theDone = true; + } + theNumPhaseGates++; + } + } + + AvHBaseBuildable::AnimateThink(); + + this->SetEnabled(theIsEnabled); + + this->pev->nextthink = gpGlobals->time + kPhaseGateIdleThink; +} + +void AvHPhaseGate::ResetEntity() +{ + this->SetEnabled(false); + + AvHMarineBaseBuildable::ResetEntity(); +} + +void AvHPhaseGate::SetEnabled(bool inEnabledState) +{ + if(inEnabledState != this->mEnabled) + { + if(inEnabledState) + { + // Start animating + this->pev->sequence = 0; + this->pev->frame = 0; + ResetSequenceInfo(); + + //this->pev->effects |= EF_BRIGHTLIGHT; + this->pev->effects |= EF_DIMLIGHT; + + UTIL_EmitAmbientSound( ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, ATTN_NORM, 0, 100); + + SetUse(&AvHPhaseGate::TeleportUse); + + this->mEnabled = true; + } + else + { + // Stop animating + this->pev->sequence = 1; + this->pev->frame = 0; + ResetSequenceInfo(); + + this->pev->effects &= ~EF_DIMLIGHT; + + UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, .5, SND_STOP, 100); + + SetUse(NULL); + + this->mEnabled = false; + } + } +} + +bool AvHPhaseGate::GetAreTeammatesBlocking(AvHPlayer* inPlayer, const Vector& inOrigin) const +{ + + // This is based off the logic of AvHSUKillPlayersTouchingPlayer so that + // the results are consistent. + + bool theResult = false; + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetIsRelevant() && theEntity->pev->team == inPlayer->pev->team) + { + float theDistanceToPlayer = VectorDistance(inOrigin, theEntity->pev->origin); + if(theDistanceToPlayer < 30) + { + if (gpGlobals->time - theEntity->GetTimeOfLastTeleport() < kPhaseGateAmnestyTime) + { + theResult = true; + } + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + return theResult; + +} + +void AvHPhaseGate::KillBuildablesTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor) +{ + + FOR_ALL_BASEENTITIES(); + AvHBaseBuildable* theBuildable = dynamic_cast(theBaseEntity); + if(theBuildable) + { + if (theBuildable->pev->iuser3 != AVH_USER3_HIVE && theBuildable->pev->iuser3 != AVH_USER3_PHASEGATE) + { + float theDistanceToPlayer = VectorDistance(inPlayer->pev->origin, theBuildable->pev->origin); + if(theDistanceToPlayer < 50) + { + theBuildable->TakeDamage(inInflictor, theBuildable->pev, 10000, DMG_GENERIC); + } + } + } + END_FOR_ALL_BASEENTITIES(); + +} +bool AvHPhaseGate::HasWarmedUp() const +{ + return this->mHasWarmedUp; +} +bool AvHPhaseGate::GetIsEnabled() const +{ + return this->GetIsBuilt() && this->mEnabled && !this->GetIsRecycling(); +} +void AvHPhaseGate::SetTimeOfLastDeparture(float timeOfLastDeparture) +{ + mTimeOfLastDeparture=timeOfLastDeparture; +} +bool AvHPhaseGate::IsReadyToUse() +{ + bool theReturn=false; + if ( (gpGlobals->time - mTimeOfLastDeparture) > BALANCE_VAR(kPhaseGateDepartureInterval) ) + { + theReturn=true; + } + return theReturn; +} + +void AvHPhaseGate::TeleportUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + AvHPlayer* thePlayer = dynamic_cast(pActivator); + CBasePlayerItem* thePlayerItem = dynamic_cast(pActivator); + + // Only players on the team of the person that created the phase gate can travel through, + bool theTeleportAllowed = (thePlayer != NULL) && (thePlayer->pev->team == this->pev->team) && thePlayer->GetIsAbleToAct(); + //bool theTeleportAllowed = thePlayerItem || (thePlayer && ((pActivator->pev->team == this->pev->team) && (this->pev->team != 0))); + if(theTeleportAllowed && this->GetIsEnabled()) + { + float theLastTeleportTime = thePlayer->GetTimeOfLastTeleport(); + theTeleportAllowed = (theLastTeleportTime == -1) || ((gpGlobals->time - theLastTeleportTime) >= BALANCE_VAR(kPhaseGateDelay)); + if(theTeleportAllowed) + { + if(!GetGameRules()->GetIsTesting()) + { + // Teleport to "next" gate. If there isn't more then one gate, respawn the player + vec3_t theOrigin; + + bool theDone = false; + bool theSuccess = false; + edict_t* thePhaseGateEdict = ENT(this->pev); + AvHPhaseGate *theTargetGate=0; + // Keep looping until we come back to ourself or we find another gate on our team to phase to + while(!theDone) + { + thePhaseGateEdict = FIND_ENTITY_BY_CLASSNAME(thePhaseGateEdict, kwsPhaseGate); + AvHPhaseGate* thePhaseGate = dynamic_cast(CBaseEntity::Instance(thePhaseGateEdict)); + + // This assert fails in normal operation, seemingly harmlessly + //ASSERT(thePhaseGate); + + if(thePhaseGateEdict == ENT(this->pev)) + { + theDone = true; + } + else if((thePhaseGateEdict->v.team == this->pev->team) && thePhaseGate && thePhaseGate->GetIsEnabled() && this->IsReadyToUse() ) + { + // Players come through on top of phase gate, plus a little above + theOrigin = thePhaseGateEdict->v.origin; + + theOrigin.z +=kRespawnFudgeFactorHeight; + + // Add in proper hull size so players don't get stuck + Vector thePlayerMinSize, thePlayerMaxSize; + thePlayer->GetSize(thePlayerMinSize, thePlayerMaxSize); + theOrigin.z += -thePlayerMinSize.z; + + //Elven Thief - Changed from AvHUGetHull(false to true to allow crouching marines access to phase gates. + // check out bug 487 + if(AvHSUGetIsEnoughRoomForHull(theOrigin, AvHMUGetHull(true, thePlayer->pev->iuser3), thePlayer->edict(), true, true)) + { + theSuccess = true; + theDone = true; + } + + // Now check to see if there are any teammates blocking who just phased in. + + if (GetAreTeammatesBlocking(thePlayer, theOrigin)) + { + theSuccess = false; + theDone = true; + } + + if ( theDone == true ) + { + theTargetGate=thePhaseGate; + } + + + } + } + + if(theSuccess) + { + // Mark the player as just having teleported so he doesn't teleport immediately again + thePlayer->SetPosition(theOrigin); + + thePlayer->pev->velocity = g_vecZero; + thePlayer->SetTimeOfLastTeleport(gpGlobals->time); + + int theFlags = 0;//thePlayer ? FEV_NOTHOST : 0; + this->SetTimeOfLastDeparture(gpGlobals->time); + AvHSUPlayPhaseInEffect(theFlags, this, thePlayer); + + // AvHSUKillPlayersTouchingPlayer(thePlayer, this->pev); + AvHSUPushbackPlayersTouchingPlayer(thePlayer, this->pev); + KillBuildablesTouchingPlayer(thePlayer, this->pev); + + Vector theFadeColor; + theFadeColor.x = 235; + theFadeColor.y = 255; + theFadeColor.z = 255; + UTIL_ScreenFade(thePlayer, theFadeColor, .9f, 0.0f, 255, FFADE_IN); + + } + } + } + } +} + +void AvHPhaseGate::UpdateOnRecycle(void) +{ + this->SetEnabled(false); +} + +void AvHPhaseGate::UpdateOnRemove(void) +{ + // Make sure sound gets turned off when round ends without it being killed + if(this->pev) + { + UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, .5, SND_STOP, 100); + } +} + + +//AvHSiegeTurret::AvHSiegeTurret() +//{ +// float theStartTime = RANDOM_FLOAT(0, avh_siegerof.value); +// this->mTimeLastFired = gpGlobals->time - theStartTime; +// this->mTimeToConstruct = GetGameRules()->GetBuildTimeForMessageID(BUILD_SIEGE); +//} +// +//CBaseEntity* AvHSiegeTurret::BestVisibleEnemy(void) +//{ +// CBaseEntity *theCurrentEnemy = NULL; +// CBaseEntity *theBestEnemy = NULL; +// float theDistanceToBestEnemy = -1; +// +// while((theCurrentEnemy = UTIL_FindEntityInSphere(theCurrentEnemy, this->pev->origin, avh_siegemaxrange.value)) != NULL) +// { +// // If entity is a valid target and within our valid range +// if(this->GetIsValidTarget(theCurrentEnemy)) +// { +// // Is it closer than our current target? +// float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, theCurrentEnemy); +// if((theDistanceToBestEnemy == -1) || (theDistanceToCurrentEnemy < theDistanceToBestEnemy)) +// { +// theBestEnemy = theCurrentEnemy; +// theDistanceToBestEnemy = theDistanceToCurrentEnemy; +// } +// } +// } +// return theBestEnemy; +//} +// +//bool AvHSiegeTurret::GetIsValidTarget(CBaseEntity* inEntity) const +//{ +// bool theTargetIsValid = false; +// +// if(AvHDeployedTurret::GetIsValidTarget(inEntity)) +// { +// float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, inEntity); +// if(theDistanceToCurrentEnemy >= this->GetMinimumRange()) +// { +// // We have to see it as well +// Vector vecMid = this->pev->origin + this->pev->view_ofs; +// Vector vecMidEnemy = inEntity->BodyTarget(vecMid); +// if(FBoxVisible(this->pev, inEntity->pev, vecMidEnemy)) +// { +// theTargetIsValid = true; +// } +// } +// } +// return theTargetIsValid; +//} +// +//char* AvHSiegeTurret::GetActiveSound() const +//{ +// return kSiegeActive; +//} +// +//char* AvHSiegeTurret::GetAlertSound() const +//{ +// return kSiegeAlert; +//} +// +//char* AvHSiegeTurret::GetDeploySound() const +//{ +// return kSiegeDeploy; +//} +// +//char* AvHSiegeTurret::GetPingSound() const +//{ +// return kSiegePing; +//} +// +//int AvHSiegeTurret::GetPointValueOfTarget(void) const +//{ +// return 3; +//} +// +//int AvHSiegeTurret::GetMinimumRange() const +//{ +// return avh_siegeminrange.value; +//} +// +//bool AvHSiegeTurret::NeedsLineOfSight() const +//{ +// return true; +//} +// +//void AvHSiegeTurret::Precache(void) +//{ +// AvHDeployedTurret::Precache(); +// +// PRECACHE_MODEL(kSiegeTurretModel); +// PRECACHE_SOUND(kSiegeTurretFire1); +//} +// +// +//void AvHSiegeTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) +//{ +// // Only fire once every few seconds...this is hacky but there's no way to override think functions so it must be done +// // I wish it was easier to change the update rate but it's not so... +// if((gpGlobals->time - this->mTimeLastFired) > avh_siegerof.value) +// { +// // Find enemy player in range, ignore walls and everything else +// if(this->m_hEnemy) +// { +// if(this->GetIsValidTarget(this->m_hEnemy)) +// { +// // Apply damage, taking upgrade into account +// float theDamageMultiplier; +// AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser4, &theDamageMultiplier); +// float theDamage = theDamageMultiplier*avh_siegedamage.value; +// +// if(!GetGameRules()->GetIsTesting()) +// { +// ::RadiusDamage(this->m_hEnemy->pev->origin, this->pev, this->pev, theDamage, avh_siegesplashradius.value, CLASS_NONE, DMG_BLAST); +// } +// +// // Play fire sound +// EMIT_SOUND_DYN(ENT(this->pev), CHAN_AUTO, kSiegeTurretFire1, 1.0, ATTN_NORM, 0, PITCH_NORM); +// +// this->pev->effects &= ~EF_MUZZLEFLASH; +// +// // Send normal effect to all +// PLAYBACK_EVENT_FULL(0, this->m_hEnemy->edict(), gSiegeHitEventID, 0, this->m_hEnemy->pev->origin, this->m_hEnemy->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); +// +// if(this->m_hEnemy->IsPlayer()) +// { +// // Send personal view shake to recipient only (check for splash here, pass param to lessen effect for others?) +// // TODO: Use upgrade level to parameterize screen shake and fade? +// PLAYBACK_EVENT_FULL(FEV_HOSTONLY, this->m_hEnemy->edict(), gSiegeViewHitEventID, 0, this->m_hEnemy->pev->origin, this->m_hEnemy->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); +// +// Vector theFadeColor; +// theFadeColor.x = 255; +// theFadeColor.y = 100; +// theFadeColor.z = 100; +// UTIL_ScreenFade(this->m_hEnemy, theFadeColor, .3f, 0.0f, 255, FFADE_OUT); +// } +// } +// else +// { +// this->m_hEnemy = NULL; +// } +// } +// +// this->mTimeLastFired = gpGlobals->time; +// } +//} +// +//void AvHSiegeTurret::Spawn() +//{ +// this->Precache(); +// AvHDeployedTurret::Spawn(); +// +// SET_MODEL(ENT(this->pev), kSiegeTurretModel); +// UTIL_SetSize(pev, kSiegeMinSize, kSiegeMaxSize); +// +// this->pev->classname = MAKE_STRING(kwsSiegeTurret); +// +// this->pev->takedamage = 1; +// this->pev->health = (int)(avh_siegehealth.value); +// this->pev->armorvalue = (int)(avh_siegehealth.value); +//} + + + +AvHMarineBaseBuildable::AvHMarineBaseBuildable(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser4) : AvHBaseBuildable(inTechID, inMessageID, inClassName, inUser4) +{ + this->SetEnergy(0.0f); +} + +char* AvHMarineBaseBuildable::GetDeploySound() const +{ + return kMarineBuildingDeploy; +} + +bool AvHMarineBaseBuildable::GetIsTechnologyAvailable(AvHMessageID inMessageID) const +{ + bool theIsAvailable = AvHBaseBuildable::GetIsTechnologyAvailable(inMessageID); + + // Disable scanning if not enough energy + if(theIsAvailable && AvHSHUGetDoesTechCostEnergy(inMessageID)) + { + int theEnergyCost = GetGameRules()->GetCostForMessageID(inMessageID); + if(this->mEnergy < theEnergyCost) + { + theIsAvailable = false; + } + } + + return theIsAvailable; +} + +char* AvHMarineBaseBuildable::GetKilledSound() const +{ + return kMarineBuildingKilled; +} + +int AvHMarineBaseBuildable::GetPointValue() const +{ + return BALANCE_VAR(kScoringMarineBuildableValue); +} + +int AvHMarineBaseBuildable::GetTakeDamageAnimation() const +{ + return -1; +} + +void AvHMarineBaseBuildable::ResetEntity() +{ + AvHBaseBuildable::ResetEntity(); +} + +void AvHMarineBaseBuildable::SetEnergy(float inEnergy) +{ + this->mEnergy = max(min(inEnergy, kMarineStructureMaxEnergy), 0.0f); + + float theNormValue = this->mEnergy/kMarineStructureMaxEnergy; + bool theIsResearching=false; + + const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(this->GetTeamNumber())); + + if ( theTeam ) { + theIsResearching=theTeam->GetResearchManager().GetIsResearching(this->entindex()); + } + if(this->pev && this->GetIsBuilt() && (!theIsResearching)) + { + AvHSHUSetEnergyState(this->pev->iuser3, this->pev->fuser1, theNormValue); + } +} + +int AvHMarineBaseBuildable::TakeDamage(entvars_t* inInflictor, entvars_t* inAttacker, float inDamage, int inBitsDamageType) +{ + //UTIL_Sparks() + + return AvHBaseBuildable::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType); +} + +void AvHMarineBaseBuildable::TechnologyBuilt(AvHMessageID inMessageID) +{ + // Pay energy cost + if(AvHSHUGetDoesTechCostEnergy(inMessageID)) + { + int theEnergyCost = GetGameRules()->GetCostForMessageID(inMessageID); + float theNewEnergy = max(this->mEnergy - theEnergyCost, 0.0f); + this->SetEnergy(theNewEnergy); + + // Play animation? + this->PlayAnimationAtIndex(this->GetActiveAnimation(), true); + } +} + +// Marine buildings +const float kNukeThinkInterval = 1.5f; + +AvHNuke::AvHNuke() : AvHMarineBaseBuildable(TECH_NULL, BUILD_NUKE, kwsNuke, AVH_USER3_NUKE) +{ +} + +void AvHNuke::Precache() +{ + AvHMarineBaseBuildable::Precache(); + PRECACHE_UNMODIFIED_SOUND(kNukeActive); + PRECACHE_UNMODIFIED_SOUND(kNukeExplode); +} + +void AvHNuke::ActiveThink() +{ + float theThinkInterval = kNukeThinkInterval; + + // If not active + if(!this->mActive) + { + // Set construction complete + this->SetConstructionComplete(); + + // Play sounds + + // Set active + this->mActive = true; + + // Mark as a monster + SetBits(this->pev->flags, FL_MONSTER); + + this->mTimeActivated = gpGlobals->time; + } + + // Emit higher and higher frequency sound + float theTimeToDetonate = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID()); + + float thePercentDone = (gpGlobals->time - this->mTimeActivated)/theTimeToDetonate; + thePercentDone = min(max(0.0f, thePercentDone), 1.0f); + + int thePitch = 100 + thePercentDone*3; + EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, kNukeActive, 1.0, ATTN_NORM, 0, thePitch); + + // If active + if(this->mActive) + { + // If enough time has passed + ASSERT(this->mTimeActivated != -1); + + if(gpGlobals->time > (this->mTimeActivated + theTimeToDetonate)) + { + // Detonation visuals (any bigger magnitude then this and the explosion doesn't show up) + int theMagnitude = 300; + ExplosionCreate(this->pev->origin, this->pev->angles, this->edict(), theMagnitude, FALSE, this->pev->team); + + // Do damage (theDamage at epicenter, falls off from there, doesn't hurt people in water, only hurts people that can be seen by blast) + float theDamage = kNukeDamage; + float theRadius = kNukeRange; + ::RadiusDamage(this->pev->origin, this->pev, this->pev, theDamage, theRadius, CLASS_NONE, DMG_BLAST); + + // Play view shake here + float theShakeAmplitude = 100; + float theShakeFrequency = 150; + float theShakeDuration = 10.0f; + float theShakeRadius = 2000; + UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius); + + // Add white out effect for players in radius + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetIsRelevant() && !theEntity->GetIsInTopDownMode()) + { + // If near the epicenter, or if it's visible, we're blinded + bool theNearEpicenter = false; + float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); + if(theDistance < theRadius/2) + { + theNearEpicenter = true; + } + + bool theExplosionVisible = false; + if(theEntity->GetIsEntityInSight(this)) + { + theExplosionVisible = true; + } + + if(theNearEpicenter || theExplosionVisible) + { + Vector theFadeColor; + theFadeColor.x = 255; + theFadeColor.y = 255; + theFadeColor.z = 255; + UTIL_ScreenFade(theEntity, theFadeColor, 1.5f, .5f, 255, FFADE_IN); + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + //this->pev->flags |= EF_NODRAW; + SET_MODEL(ENT(pev), kNullModel); + this->pev->flags |= EF_BRIGHTFIELD; + this->pev->flags |= EF_BRIGHTLIGHT; + + // Remove entity + SetThink(&AvHNuke::DeathThink); + theThinkInterval = theShakeDuration; + + // Play special nuke sound (not playing for some reason) + EMIT_SOUND(this->edict(), CHAN_BODY, kNukeExplode, 1.0f, ATTN_IDLE); + } + } + + this->pev->nextthink = gpGlobals->time + theThinkInterval; +} + +void AvHNuke::DeathThink() +{ + UTIL_Remove(this); +} + +void AvHNuke::Spawn() +{ + AvHBaseBuildable::Spawn(); + + // Set ActiveThink + this->mActive = false; + this->mTimeActivated = -1; + + SetThink(&AvHNuke::ActiveThink); + this->pev->nextthink = gpGlobals->time + kNukeThinkInterval; +} + +char* AvHNuke::GetDeploySound() const +{ + return kNukeDeploy; +} + +char* AvHNuke::GetKilledSound() const +{ + return kNukeKilled; +} + + +const float kInfantryPortalThinkTime = 1.0f; +#define kInfantryPortalLightEffect EF_LIGHT + +AvHInfantryPortal::AvHInfantryPortal() : AvHMarineBaseBuildable(TECH_INFANTRYPORTAL, BUILD_INFANTRYPORTAL, kwsInfantryPortal, AVH_USER3_INFANTRYPORTAL) +{ +} + +void AvHInfantryPortal::Killed(entvars_t* inAttacker, int inGib) +{ + AvHBaseBuildable::Killed(inAttacker, inGib); + + GetGameRules()->MarkDramaticEvent(kIPDeathPriority, this->entindex(), inAttacker); +} + +float AvHInfantryPortal::GetReinforceTime() const +{ + float theReinforceTime = BALANCE_VAR(kMarineRespawnTime); + + if(GetGameRules()->GetCheatsEnabled()) + { + theReinforceTime = 2; + } + + theReinforceTime = max(0.0f, theReinforceTime); + + return theReinforceTime; +} + +void AvHInfantryPortal::PortalThink() +{ + this->UpdateReinforcements(); + + AvHBaseBuildable::AnimateThink(); + + this->pev->nextthink = gpGlobals->time + kInfantryPortalThinkTime; +} + + +void AvHInfantryPortal::ResetEntity() +{ + AvHMarineBaseBuildable::ResetEntity(); + AvHReinforceable::ResetEntity(); +} + +void AvHInfantryPortal::SetHasBeenBuilt() +{ + AvHBuildable::SetHasBeenBuilt(); + + SetThink(&AvHInfantryPortal::PortalThink); + this->pev->nextthink = gpGlobals->time + kInfantryPortalThinkTime; + // this->pev->effects |= kInfantryPortalLightEffect; +} + +void AvHInfantryPortal::Precache() +{ + AvHMarineBaseBuildable::Precache(); + + PRECACHE_UNMODIFIED_SOUND(kTransportSound); +} + + +void AvHInfantryPortal::CueRespawnEffect(AvHPlayer* inPlayer) +{ + // Playback teleporting event + PLAYBACK_EVENT_FULL(0, inPlayer->edict(), gTeleportEventID, 0, inPlayer->pev->origin, inPlayer->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + + EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kTransportSound, .8, ATTN_NORM); +} + +bool AvHInfantryPortal::GetCanReinforce() const +{ + return this->GetIsBuilt() && !GetGameRules()->GetIsCombatMode(); +} + +bool AvHInfantryPortal::GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const +{ + // Set player position to on top of gate + vec3_t thePosition = AvHSHUGetRealLocation(this->pev->origin, this->pev->mins, this->pev->maxs); + + Vector theIPMinSize, theIPMaxSize; + AvHSHUGetSizeForTech(BUILD_INFANTRYPORTAL, theIPMinSize, theIPMaxSize); + thePosition.z += theIPMaxSize.z; + + AvHPlayer* thePlayer = dynamic_cast(inPlayer); + ASSERT(thePlayer); + + // Respawn player on top of portal + Vector thePlayerMinSize, thePlayerMaxSize; + thePlayer->GetSize(thePlayerMinSize, thePlayerMaxSize); + + thePosition.z += (-thePlayerMinSize.z + kRespawnFudgeFactorHeight); + + VectorCopy(thePosition, outLocation); + + return true; +} + +AvHTeamNumber AvHInfantryPortal::GetReinforceTeamNumber() const +{ + return this->GetTeamNumber(); +} + +void AvHInfantryPortal::ResetReinforcingPlayer(bool inSuccess) +{ + // If we're respawning, we telefrag. Make sure to telefrag after the player's new location has been set + bool theTelefrag = false; + AvHPlayer* thePlayer = this->GetReinforcingPlayer(); + if(inSuccess && thePlayer) + { + theTelefrag = true; + } + + AvHReinforceable::ResetReinforcingPlayer(inSuccess); + + if(theTelefrag) + { + //AvHSUKillPlayersTouchingPlayer(thePlayer, this->pev); + AvHSUPushbackPlayersTouchingPlayer(thePlayer, this->pev); + } +} + +void AvHInfantryPortal::UpdateOnRecycle(void) +{ + this->ResetReinforcingPlayer(false); + // this->pev->effects &= ~kInfantryPortalLightEffect; +} + +void AvHInfantryPortal::UpdateOnRemove(void) +{ + this->ResetReinforcingPlayer(false); + SetThink(NULL); +} + +int AvHInfantryPortal::GetIdleAnimation() const +{ + // AvHBaseBuildable randomly shows Idle2, which we don't have in this model. + return 2; +} + +int AvHInfantryPortal::GetIdle1Animation() const +{ + // AvHBaseBuildable randomly shows Idle2, which we don't have in this model. + return 2; +} + +int AvHInfantryPortal::GetIdle2Animation() const +{ + // AvHBaseBuildable randomly shows Idle2, which we don't have in this model. + return 2; +} + +// : Uncomment for the new IP from Alpha +//int AvHInfantryPortal::GetDeployAnimation() const +//{ +// return 0; +//} + +//int AvHInfantryPortal::GetSpawnAnimation() const +//{ +// return 1; +//} +// : + +const int kCommandStationExitAnimation = 12; + +AvHCommandStation::AvHCommandStation() : AvHMarineBaseBuildable(TECH_COMMAND_CENTER, BUILD_COMMANDSTATION, kwsTeamCommand, AVH_USER3_COMMANDER_STATION) +{ + this->mCommanderAtThisStation = -1; + this->mTimeToPlayOnlineSound = -1; +} + +int AvHCommandStation::GetIdleAnimation() const +{ + int theAnimation = 2; + + // If we're in use + if(this->mCommanderAtThisStation != -1 || GetGameRules()->GetIsCombatMode()) + { + theAnimation = 3; + } + + return theAnimation; +} + +int AvHCommandStation::GetPointValue() const +{ + return BALANCE_VAR(kScoringCCValue); +} + +void AvHCommandStation::Killed( entvars_t *pevAttacker, int iGib ) +{ + this->SetInactive(); + + AvHMarineBaseBuildable::Killed(pevAttacker, iGib); +} + +int AvHCommandStation::ObjectCaps( void ) +{ + + // Recycled command stations cannot be used. + + int theObjectCaps = 0; + + if ( !(pev->effects & EF_NODRAW) ) + { + theObjectCaps |= FCAP_CONTINUOUS_USE; + } + + return theObjectCaps; + +} + +void AvHCommandStation::CommandTouch(CBaseEntity* pOther) +{ + AvHPlayer* thePlayer = dynamic_cast(pOther); + if(thePlayer) + { + AvHTeamNumber theStationTeamNumber = this->GetTeamNumber(); + if(thePlayer->pev->team == theStationTeamNumber) + { + // Check to see if we already have a commander and don't make the person a commander if so + if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0) + { + thePlayer->SendMessage(kHelpTextCSAttractMode, TOOLTIP); + } + } + } +} + +void AvHCommandStation::CommandUse( CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value ) +{ + AvHPlayer* thePlayer = dynamic_cast(pActivator); + + // Mapper-placed CCs can be killed but they don't go away + if(thePlayer && !(thePlayer->pev->flags & FL_FAKECLIENT) && !this->GetHasBeenKilled() && thePlayer->GetIsAbleToAct()) + { + AvHTeam* theTeam = thePlayer->GetTeamPointer(); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) + { + AvHTeamNumber theStationTeamNumber = this->GetTeamNumber(); + if(thePlayer->pev->team == theStationTeamNumber) + { + // Check to see if we already have a commander and don't make the person a commander if so + if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0) + { + string theErrorMessage; + if(thePlayer->GetCanCommand(theErrorMessage)) + { + if(this->pev->health > 0 && !this->GetIsRecycling()) + { + thePlayer->SetUser3(AVH_USER3_COMMANDER_PLAYER); + //thePlayer->SetSelection(this->entindex()); + + EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kCommandStationStartSound, .8, kCommandStationAttenuation); + + SetThink(&AvHCommandStation::CommanderUsingThink); + this->pev->nextthink = gpGlobals->time + kCommandStationThinkInterval; + + const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationUseTeamOne : kTargetCommandStationUseTeamTwo; + FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f); + + this->mCommanderAtThisStation = thePlayer->entindex(); + + this->PlayAnimationAtIndex(5, true); + + this->mTimeToPlayOnlineSound = this->GetTimeAnimationDone(); + + GetGameRules()->MarkDramaticEvent(kCCNewCommanderPriority, thePlayer, this); + } + else + { + thePlayer->SendMessage(kCommandStationDestroyed, TOOLTIP); + } + } + else + { + thePlayer->SendMessage(theErrorMessage.c_str()); + } + } + else + { + // The player somehow touches the command station while still a commander + if(thePlayer->GetUser3() != AVH_USER3_COMMANDER_PLAYER) + { + thePlayer->SendMessage(kCommandStationInUse, TOOLTIP); + } + } + } + else + { + thePlayer->SendMessage(kCommandStationForOtherTeam, TOOLTIP); + } + } + } +} + +void AvHCommandStation::Precache(void) +{ + AvHMarineBaseBuildable::Precache(); + + PRECACHE_UNMODIFIED_MODEL(kCommandStationModel); + PRECACHE_UNMODIFIED_SOUND(kCommandStationStartSound); + PRECACHE_UNMODIFIED_SOUND(kCommandStationEndSound); +} + +void AvHCommandStation::ActivateThink(void) +{ + // Don't allow use of the Command station in Combat mode + if(!GetGameRules()->GetIsCombatMode()) + { + SetTouch(&AvHCommandStation::CommandTouch); + SetUse(&AvHCommandStation::CommandUse); + } + + SetThink(&AvHBaseBuildable::AnimateThink); + this->pev->nextthink = gpGlobals->time + .1f; +} + +void AvHCommandStation::SetHasBeenBuilt() +{ + AvHBuildable::SetHasBeenBuilt(); + + SetThink(&AvHCommandStation::ActivateThink); + this->pev->nextthink = gpGlobals->time + 2.0f; +} + +void AvHCommandStation::EjectCommander() +{ + // If this command station had a commander, log him out! + if(this->mCommanderAtThisStation != -1) + { + // if the command station is killed, kick out any commander + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->pev->team == this->pev->team) + { + if(theEntity->GetUser3() == AVH_USER3_COMMANDER_PLAYER) + { + theEntity->StopTopDownMode(); + theEntity->SetUser3(AVH_USER3_MARINE_PLAYER); + + this->PlayAnimationAtIndex(kCommandStationExitAnimation, true); + + GetGameRules()->MarkDramaticEvent(kCCEjectPriority, theEntity, this); + + break; + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + } + + this->mCommanderAtThisStation = -1; +} + +char* AvHCommandStation::GetKilledSound() const +{ + return kCommandStationDeathSound; +} + + +void AvHCommandStation::SetInactive() +{ + AvHBaseBuildable::SetInactive(); + + this->EjectCommander(); +} + +void AvHCommandStation::Materialize() +{ + AvHMarineBaseBuildable::Materialize(); + + this->mCommanderAtThisStation = -1; + + //this->ResetEntity(); +} + +void AvHCommandStation::Spawn(void) +{ + AvHMarineBaseBuildable::Spawn(); + + this->Materialize(); +} + +int AvHCommandStation::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + int theReturnCode = 0; + + if(this->pev->health > 0) + { + theReturnCode = AvHBaseBuildable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + + if(this->pev->health <= 0) + { + AvHTeamNumber theStationTeamNumber = (AvHTeamNumber)this->pev->team; + const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationDestroyedTeamOne : kTargetCommandStationDestroyedTeamTwo; + FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f); + + GetGameRules()->MarkDramaticEvent(kCCDeathPriority, this->entindex(), false, pevAttacker); + } + } + + return theReturnCode; +} + + +void AvHCommandStation::CommanderUsingThink(void) +{ + AvHTeamNumber theStationTeamNumber = this->GetTeamNumber(); + + // Check to see if we already have a commander and don't make the person a commander if so + if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0) + { + this->mCommanderAtThisStation = -1; + this->mTimeToPlayOnlineSound = -1; + + EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kCommandStationEndSound, .8, kCommandStationAttenuation); + this->PlayAnimationAtIndex(kCommandStationExitAnimation, true); + + //SetThink(NULL); + SetThink(&AvHBaseBuildable::AnimateThink); + this->pev->nextthink = gpGlobals->time + .1f; + } + else + { + if(this->mTimeToPlayOnlineSound > 0.0f) + { + if(gpGlobals->time > this->mTimeToPlayOnlineSound) + { + ASSERT(this->mCommanderAtThisStation > 0); + + // If enough time has passed and we haven't played online sound, play it + AvHPlayer* theCommander = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCommanderAtThisStation))); + if(theCommander) + { + theCommander->PlayHUDSound(HUD_SOUND_MARINE_CCONLINE); + } + + this->mTimeToPlayOnlineSound = -1; + } + } + + AvHBaseBuildable::AnimateThink(); + + this->pev->nextthink = gpGlobals->time + kCommandStationThinkInterval; + } +} + +bool AvHCommandStation::GetIsTechnologyAvailable(AvHMessageID inMessageID) const +{ + bool theTechIsAvailable = AvHMarineBaseBuildable::GetIsTechnologyAvailable(inMessageID); + + // Only allow CC to be recycled if it's unoccupied + if(inMessageID == BUILD_RECYCLE) + { + theTechIsAvailable = (this->mCommanderAtThisStation == -1); + } + + return theTechIsAvailable; +} + +AvHTurretFactory::AvHTurretFactory() : AvHMarineBaseBuildable(TECH_TURRET_FACTORY, BUILD_TURRET_FACTORY, kwsTurretFactory, AVH_USER3_TURRET_FACTORY) +{ +} + +void AvHTurretFactory::CheckTurretEnabledState() const +{ + // Check to see if turrets should come online + FOR_ALL_ENTITIES(kwsDeployedTurret, AvHMarineTurret*) + if(theEntity->pev->team == this->pev->team) + { + theEntity->CheckEnabledState(); + } + END_FOR_ALL_ENTITIES(kwsDeployedTurret); + + FOR_ALL_ENTITIES(kwsSiegeTurret, AvHSiegeTurret*) + if(theEntity->pev->team == this->pev->team) + { + theEntity->CheckEnabledState(); + } + END_FOR_ALL_ENTITIES(kwsSiegeTurret) +} + +void AvHTurretFactory::TriggerAddTech() const +{ + AvHBuildable::TriggerAddTech(); + + this->CheckTurretEnabledState(); +} + +void AvHTurretFactory::TriggerRemoveTech() const +{ + AvHBuildable::TriggerRemoveTech(); + + this->CheckTurretEnabledState(); +} + +int AvHTurretFactory::GetIdle1Animation() const +{ + int theAnimation = 3; + + // Different animation when electrified + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_11)) + { + theAnimation = 12; + } + + return theAnimation; +} + +int AvHTurretFactory::GetIdle2Animation() const +{ + return this->GetIdle1Animation(); +} + +int AvHTurretFactory::GetResearchAnimation() const +{ + return 4; +} + +void AvHTurretFactory::SetHasBeenBuilt() +{ + AvHBuildable::SetHasBeenBuilt(); + + // Animate + SetThink(&AvHBaseBuildable::AnimateThink); + this->pev->nextthink = gpGlobals->time + .1f; +} + +bool AvHTurretFactory::GetSupportsTechID(AvHTechID inTechID) const +{ + bool theSuccess = AvHBuildable::GetSupportsTechID(inTechID); + + if(this->GetTechID() == TECH_ADVANCED_TURRET_FACTORY) + { + if(!theSuccess) + { + // Adv. turret factory also counts as a turret factory + theSuccess = (inTechID == TECH_TURRET_FACTORY); + } + } + + return theSuccess; +} + +void AvHTurretFactory::Upgrade() +{ + // Set iuser3 + this->pev->iuser3 = AVH_USER3_ADVANCED_TURRET_FACTORY; + + // Set classname + this->pev->classname = MAKE_STRING(kwsAdvancedTurretFactory); + + this->mMessageID = TURRET_FACTORY_UPGRADE; + this->SetTechID(TECH_ADVANCED_TURRET_FACTORY); + + this->SetSelectID(this->pev->iuser3); + + this->SetConstructionComplete(true); + + this->HealthChanged(); +} + + +AvHArmory::AvHArmory() : AvHMarineBaseBuildable(TECH_ARMORY, BUILD_ARMORY, kwsArmory, AVH_USER3_ARMORY) +{ +} + +int AvHArmory::GetSequenceForBoundingBox() const +{ + return 2; +} + +bool AvHArmory::GetSupportsTechID(AvHTechID inTechID) const +{ + bool theSuccess = AvHBuildable::GetSupportsTechID(inTechID); + + if(this->GetTechID() == TECH_ADVANCED_ARMORY) + { + // Adv. armory also counts as an armory + if(!theSuccess) + { + theSuccess = (inTechID == TECH_ARMORY); + } + } + + return theSuccess; +} + +int AvHArmory::GetIdle1Animation() const +{ + int theAnim = 2; + if(this->pev->iuser3 == AVH_USER3_ADVANCED_ARMORY) + { + theAnim = 3; + } + return theAnim; +} + +int AvHArmory::GetIdle2Animation() const +{ + return this->GetIdle1Animation(); +} + +int AvHArmory::GetActiveAnimation() const +{ + int theAnim = 5; + if(this->pev->iuser3 == AVH_USER3_ADVANCED_ARMORY) + { + theAnim = 12; + } + return theAnim; +} + +int AvHArmory::GetResearchAnimation() const +{ + return 5; +} + +void AvHArmory::Precache() +{ + AvHMarineBaseBuildable::Precache(); + + PRECACHE_UNMODIFIED_SOUND(kArmoryResupplySound); +} + +void AvHArmory::ResupplyUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue) +{ + AvHPlayer* thePlayer = dynamic_cast(inCaller); + + if(thePlayer && (thePlayer->pev->team == this->pev->team) && this->GetIsBuilt() && !this->GetIsRecycling() && thePlayer->GetIsAbleToAct()) + { + if(thePlayer->GetCanBeResupplied()) + { + // : 1017 +// // Give health back occasionally +// bool theGiveHealthIfNeeded = (RANDOM_LONG(0, 3) == 0); +// + // resupply gives 10 health each use + thePlayer->Resupply(true); + + // Always play "getting ammo" sound when ammo or health are needed, to indicate to player when to stop pressing +use + EMIT_SOUND(thePlayer->edict(), CHAN_WEAPON, kArmoryResupplySound, .3f, ATTN_NORM); + } + } +} + +void AvHArmory::SetHasBeenBuilt() +{ + AvHBuildable::SetHasBeenBuilt(); + + SetUse(&AvHArmory::ResupplyUse); +} + +void AvHArmory::Upgrade() +{ + // Set iuser3 + this->pev->iuser3 = AVH_USER3_ADVANCED_ARMORY; + + // Set classname + this->pev->classname = MAKE_STRING(kwsAdvancedArmory); + + this->mMessageID = ARMORY_UPGRADE; + this->SetTechID(TECH_ADVANCED_ARMORY); + + this->SetSelectID(this->pev->iuser3); + + this->SetConstructionComplete(true); + + this->HealthChanged(); +} + + +AvHArmsLab::AvHArmsLab() : AvHMarineBaseBuildable(TECH_ARMSLAB, BUILD_ARMSLAB, kwsArmsLab, AVH_USER3_ARMSLAB) +{ +} + +int AvHArmsLab::GetResearchAnimation() const +{ + return 5; +} + +int AvHArmsLab::GetSequenceForBoundingBox() const +{ + return 1; +} + +AvHPrototypeLab::AvHPrototypeLab() : AvHMarineBaseBuildable(TECH_PROTOTYPE_LAB, BUILD_PROTOTYPE_LAB, kwsPrototypeLab, AVH_USER3_PROTOTYPE_LAB) +{ +} + + +const float kObservatoryThinkTime = 1.0f; +const float kObservatoryStartEnergy = 40; + +AvHObservatory::AvHObservatory() : AvHMarineBaseBuildable(TECH_OBSERVATORY, BUILD_OBSERVATORY, kwsObservatory, AVH_USER3_OBSERVATORY) +{ +} + +int AvHObservatory::GetSequenceForBoundingBox() const +{ + return 1; +} + +void AvHObservatory::ObservatoryThink() +{ + // Remove cloaking from nearby enemies + if ( !this->GetIsRecycling() ) { + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team)) + { + // Check that entity is in range of scan + float theDistance = VectorDistance2D(theEntity->pev->origin, this->pev->origin); + if(theDistance < BALANCE_VAR(kObservatoryXYDetectionRadius)) + { + // Remove cloaking, if player has it + theEntity->TriggerUncloak(); + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + } + + AvHBaseBuildable::AnimateThink(); + + // Update scanning energy + float theRate = kMarineStructureEnergyRate; + if(GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsCheatEnabled(kcSlowResearch)) + { + theRate *= 6; + } + + this->SetEnergy(this->mEnergy + (kObservatoryThinkTime*theRate)); + + this->pev->nextthink = gpGlobals->time + kObservatoryThinkTime; +} + +void AvHObservatory::Materialize() +{ + AvHMarineBaseBuildable::Materialize(); +} + +void AvHObservatory::ResetEntity() +{ + AvHMarineBaseBuildable::ResetEntity(); + + this->SetEnergy(0); +} + +void AvHObservatory::SetHasBeenBuilt() +{ + AvHBuildable::SetHasBeenBuilt(); + + SetThink(&AvHObservatory::ObservatoryThink); + this->pev->nextthink = gpGlobals->time + kObservatoryThinkTime; + + this->SetEnergy(kObservatoryStartEnergy); +} + +void AvHObservatory::Spawn() +{ + AvHMarineBaseBuildable::Spawn(); +} + +int AvHObservatory::GetActiveAnimation() const +{ + return 2; +} + +int AvHObservatory::GetIdle1Animation() const +{ + return 3; +} + +int AvHObservatory::GetIdle2Animation() const +{ + return 3; +} + +int AvHObservatory::GetResearchAnimation() const +{ + return 4; +} diff --git a/main/source/mod/AvHMarineEquipment.h b/main/source/mod/AvHMarineEquipment.h index 7d6341f..7585223 100644 --- a/main/source/mod/AvHMarineEquipment.h +++ b/main/source/mod/AvHMarineEquipment.h @@ -1,473 +1,473 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHMarineEquipment.h $ -// $Date: 2002/11/06 01:40:17 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHMarineEquipment.h,v $ -// Revision 1.29 2002/11/06 01:40:17 Flayra -// - Turrets now need an active turret factory to keep firing -// -// Revision 1.28 2002/10/24 21:32:16 Flayra -// - All heavy armor to be given via console -// - Fix for AFKers on inf portals, also for REIN players when recycling portals -// -// Revision 1.27 2002/10/16 20:54:30 Flayra -// - Added phase gate sound -// - Fixed ghostly command station view model problem after building it -// -// Revision 1.26 2002/10/16 01:00:33 Flayra -// - Phasegates play looping sound, and stop it when they are destroyed -// -// Revision 1.25 2002/09/23 22:21:21 Flayra -// - Added jetpack and heavy armor -// - Added "cc online" sound -// - Turret factories now upgrade to advanced turret factories for siege -// - Added automatic resupply at armory, but removed it -// - Observatories scan in 2D now, to match commander range overlay -// -// Revision 1.24 2002/09/09 19:59:39 Flayra -// - Fixed up phase gates (no longer teleport you unless you have two, and they work properly now) -// - Refactored reinforcements -// - Fixed bug where secondary command station couldn't be built -// -// Revision 1.23 2002/07/26 23:05:54 Flayra -// - Numerical event feedback -// - Started to add sparks when buildings were hit but didn't know the 3D point to play it at -// -// Revision 1.22 2002/07/23 17:11:47 Flayra -// - Phase gates must be built and can be destroyed, observatories decloak aliens, hooks for fast reinforcements upgrade, nuke damage increased, commander banning -// -// Revision 1.21 2002/07/08 17:02:57 Flayra -// - Refactored reinforcements, updated entities for new artwork -// -// Revision 1.20 2002/06/25 18:04:43 Flayra -// - Renamed some buildings, armory is now upgraded to advanced armory -// -// Revision 1.19 2002/06/03 16:50:35 Flayra -// - Renamed weapons factory and armory, added ammo resupplying -// -// Revision 1.18 2002/05/28 17:51:34 Flayra -// - Tried to make nuke sound play, extended shake duration to sound length, reinforcement refactoring, mark command stations as mapper placed, so they aren't deleted on level cleanup, support for point-entity buildable command stations -// -// Revision 1.17 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifndef AVHMARINEEQUIPMENT_H -#define AVHMARINEEQUIPMENT_H - -#include "util/nowarnings.h" -#include "dlls/weapons.h" -#include "mod/AvHBasePlayerWeapon.h" -#include "mod/AvHConstants.h" -#include "mod/AvHMarineWeapons.h" -#include "dlls/turret.h" -#include "mod/AvHEntities.h" -#include "dlls/ctripmine.h" -#include "mod/AvHReinforceable.h" - -class AvHDeployedMine : public CBasePlayerItem -{ -public: - void EXPORT ActiveThink(); - void EXPORT ActiveTouch(CBaseEntity* inOther); - void Detonate(); - void Precache(void); - void EXPORT PowerupThink(); - void SetPlacer(entvars_t* inPlacer); - void Spawn(void); - - void Killed(entvars_t* inAttacker, int inGib); - int TakeDamage(entvars_t *inInflictor, entvars_t *inAttacker, float inDamage, int inBitsDamageType); - -private: - void DetonateIfOwnerInvalid(); - void Explode(TraceResult* inTrace, int inBitsDamageType); - void EXPORT Smoke(); - - Vector mVecDir; - Vector mVecEnd; - Vector mOwnerOrigin; - Vector mOwnerAngles; - - float mTimePlaced; - float mLastTimeTouched; - bool mDetonated; - bool mPoweredUp; - EHANDLE mOwner; - entvars_t* mPlacer; -}; - -class AvHPlayerEquipment : public CBasePlayerItem -{ -public: - AvHPlayerEquipment(); - - virtual int GetLifetime() const; - - virtual bool GetIsPersistent() const; - virtual void SetPersistent(); - - virtual void KeyValue(KeyValueData* pkvd); - -private: - bool mIsPersistent; - int mLifetime; -}; - -class AvHHealth : public AvHPlayerEquipment -{ -public: - // puzl: 1017 GiveHealth now takes the amount as a paramater. - static BOOL GiveHealth(CBaseEntity* inOther, float points); - - void Precache( void ); - void Spawn( void ); - void EXPORT Touch(CBaseEntity* inOther); -}; - -class AvHCatalyst : public AvHPlayerEquipment -{ -public: - static BOOL GiveCatalyst(CBaseEntity* inOther); - - void Precache( void ); - void Spawn( void ); - void EXPORT Touch(CBaseEntity* inOther); -}; - -class AvHGenericAmmo : public CBasePlayerAmmo -{ -public: - static BOOL GiveAmmo(CBaseEntity* inOther); - - BOOL AddAmmo( CBaseEntity *pOther ); - void Precache( void ); - void Spawn( void ); - - void EXPORT Dropped(void); - -private: - bool mDropped; -}; - -class AvHHeavyArmor : public AvHPlayerEquipment -{ -public: - void Precache( void ); - void Spawn( void ); - void EXPORT Touch(CBaseEntity* inOther); -}; - -class AvHJetpack : public AvHPlayerEquipment -{ -public: - void Precache( void ); - void Spawn( void ); - void EXPORT Touch(CBaseEntity* inOther); -}; - -class AvHAmmoPack : public AvHPlayerEquipment -{ -public: - char m_szAmmoType[32]; - int m_iMaxAmmo; - int m_iAmmoAmt; - int m_iWeaponID; //weapon id this is for. - float m_flNoTouch; //Dont let anyone touch it while its falling - - void Precache( void ); - void Spawn( void ); - void EXPORT Touch(CBaseEntity* inOther); -}; - -class AvHScan : public CBaseAnimating -{ -public: - AvHScan(); - void Precache(void); - void Spawn(void); - void ScanThink(); - -private: - float mTimeCreated; - -}; - -class AvHMarineBaseBuildable : public AvHBaseBuildable -{ -public: - AvHMarineBaseBuildable(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser3); - - virtual char* GetDeploySound() const; - virtual char* GetKilledSound() const; - virtual int GetPointValue() const; - virtual int GetTakeDamageAnimation() const; - virtual void ResetEntity(); - virtual int TakeDamage(entvars_t* inInflictor, entvars_t* inAttacker, float inDamage, int inBitsDamageType); - virtual bool GetIsTechnologyAvailable(AvHMessageID inMessageID) const; - virtual void TechnologyBuilt(AvHMessageID inMessageID); - -protected: - void SetEnergy(float inEnergy); - float mEnergy; - -}; - -const float kPhaseGateIdleThink = 1.0f; - -class AvHPhaseGate : public AvHMarineBaseBuildable -{ -public: - AvHPhaseGate(); - virtual int GetIdleAnimation() const; - bool GetIsEnabled() const; - virtual int GetSequenceForBoundingBox() const; - void Killed(entvars_t* inAttacker, int inGib); - void Precache(void); - void EXPORT IdleThink(); - virtual void ResetEntity(); - virtual void SetHasBeenBuilt(); - void SetTimeOfLastDeparture(float timeOfLastDeparture); - bool IsReadyToUse(); - bool HasWarmedUp() const; - void EXPORT TeleportUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); - //void EXPORT TeleportTouch(CBaseEntity *pOther); - virtual void UpdateOnRecycle(void); - virtual void UpdateOnRemove(void); - -private: - - void KillBuildablesTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor); - bool GetAreTeammatesBlocking(AvHPlayer* thePlayer, const Vector& inOrigin) const; - void SetEnabled(bool inEnabledState); - bool mEnabled; - bool mHasWarmedUp; - float mTimeOfLastDeparture; -}; - -class AvHNuke : public AvHMarineBaseBuildable -{ -public: - AvHNuke(); - - virtual void Precache(); - - void EXPORT ActiveThink(); - - void EXPORT DeathThink(); - - virtual char* GetDeploySound() const; - - virtual char* GetKilledSound() const; - - virtual void Spawn(); - -private: - bool mActive; - float mTimeActivated; - -}; - -class AvHInfantryPortal : public AvHMarineBaseBuildable, public AvHReinforceable -{ -public: - AvHInfantryPortal(); - - virtual void Killed(entvars_t* inAttacker, int inGib); - - virtual float GetReinforceTime() const; - - void EXPORT PortalThink(); - - virtual void Precache(); - - virtual void ResetEntity(); - - virtual void SetHasBeenBuilt(); - - // From AvHReinforceable - virtual void CueRespawnEffect(AvHPlayer* inPlayer); - - virtual bool GetCanReinforce() const; - - virtual bool GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const; - - virtual AvHTeamNumber GetReinforceTeamNumber() const; - - virtual void UpdateOnRecycle(void); - virtual void UpdateOnRemove(void); - - virtual int GetIdleAnimation() const; - virtual int GetIdle1Animation() const; - virtual int GetIdle2Animation() const; - - virtual int GetDeployAnimation() const; - virtual int GetSpawnAnimation() const; - -protected: - - virtual void ResetReinforcingPlayer(bool inSuccess); - -}; - -class AvHCommandStation : public AvHMarineBaseBuildable -{ -public: - AvHCommandStation(); - - void EXPORT CommandTouch( CBaseEntity *pOther ); - - void EXPORT CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - virtual int GetIdleAnimation() const; - - virtual bool GetIsTechnologyAvailable(AvHMessageID inMessageID) const; - - virtual char* GetKilledSound() const; - - virtual int GetPointValue() const; - - virtual void Killed( entvars_t *pevAttacker, int iGib ); - - virtual void Materialize(); - - virtual int ObjectCaps(void); - - virtual void Precache(void); - - virtual void SetHasBeenBuilt(); - - virtual void SetInactive(); - - virtual void Spawn(void); - - virtual int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - -private: - void EXPORT ActivateThink(void); - void EXPORT CommanderUsingThink(void); - void EjectCommander(); - - int mCommanderAtThisStation; - float mTimeToPlayOnlineSound; - -}; - - -class AvHTurretFactory : public AvHMarineBaseBuildable -{ -public: - AvHTurretFactory(); - - virtual int GetIdle1Animation() const; - - virtual int GetIdle2Animation() const; - - virtual int GetResearchAnimation() const; - - virtual bool GetSupportsTechID(AvHTechID inTechID) const; - - virtual void SetHasBeenBuilt(); - - virtual void TriggerAddTech() const; - - virtual void TriggerRemoveTech() const; - - virtual void Upgrade(); - -private: - virtual void CheckTurretEnabledState() const; - -}; - -class AvHArmory : public AvHMarineBaseBuildable -{ -public: - AvHArmory(); - - virtual int GetActiveAnimation() const; - - virtual int GetIdle1Animation() const; - - virtual int GetIdle2Animation() const; - - virtual int GetResearchAnimation() const; - - virtual int GetSequenceForBoundingBox() const; - - virtual bool GetSupportsTechID(AvHTechID inTechID) const; - - virtual void Precache(); - - //void EXPORT ResupplyThink(); - void EXPORT ResupplyUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue); - - virtual void SetHasBeenBuilt(); - - virtual void Upgrade(); - -}; - -class AvHArmsLab : public AvHMarineBaseBuildable -{ -public: - AvHArmsLab(); - - virtual int GetResearchAnimation() const; - - virtual int GetSequenceForBoundingBox() const; - -}; - -class AvHPrototypeLab : public AvHMarineBaseBuildable -{ -public: - AvHPrototypeLab(); -}; - -class AvHObservatory : public AvHMarineBaseBuildable -{ -public: - AvHObservatory(); - virtual int GetSequenceForBoundingBox() const; - virtual void Materialize(); - void EXPORT ObservatoryThink(); - virtual void ResetEntity(); - virtual void SetHasBeenBuilt(); - virtual void Spawn(); - - virtual int GetActiveAnimation() const; - virtual int GetIdle1Animation() const; - virtual int GetIdle2Animation() const; - virtual int GetResearchAnimation() const; - -}; - -//class AvHChemLab : public AvHMarineBaseBuildable -//{ -//public: -// AvHChemLab(); -//}; -// -//class AvHMedLab : public AvHMarineBaseBuildable -//{ -//public: -// AvHMedLab(); -//}; -// -//class AvHNukePlant : public AvHMarineBaseBuildable -//{ -//public: -// AvHNukePlant(); -//}; - -#endif +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHMarineEquipment.h $ +// $Date: 2002/11/06 01:40:17 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHMarineEquipment.h,v $ +// Revision 1.29 2002/11/06 01:40:17 Flayra +// - Turrets now need an active turret factory to keep firing +// +// Revision 1.28 2002/10/24 21:32:16 Flayra +// - All heavy armor to be given via console +// - Fix for AFKers on inf portals, also for REIN players when recycling portals +// +// Revision 1.27 2002/10/16 20:54:30 Flayra +// - Added phase gate sound +// - Fixed ghostly command station view model problem after building it +// +// Revision 1.26 2002/10/16 01:00:33 Flayra +// - Phasegates play looping sound, and stop it when they are destroyed +// +// Revision 1.25 2002/09/23 22:21:21 Flayra +// - Added jetpack and heavy armor +// - Added "cc online" sound +// - Turret factories now upgrade to advanced turret factories for siege +// - Added automatic resupply at armory, but removed it +// - Observatories scan in 2D now, to match commander range overlay +// +// Revision 1.24 2002/09/09 19:59:39 Flayra +// - Fixed up phase gates (no longer teleport you unless you have two, and they work properly now) +// - Refactored reinforcements +// - Fixed bug where secondary command station couldn't be built +// +// Revision 1.23 2002/07/26 23:05:54 Flayra +// - Numerical event feedback +// - Started to add sparks when buildings were hit but didn't know the 3D point to play it at +// +// Revision 1.22 2002/07/23 17:11:47 Flayra +// - Phase gates must be built and can be destroyed, observatories decloak aliens, hooks for fast reinforcements upgrade, nuke damage increased, commander banning +// +// Revision 1.21 2002/07/08 17:02:57 Flayra +// - Refactored reinforcements, updated entities for new artwork +// +// Revision 1.20 2002/06/25 18:04:43 Flayra +// - Renamed some buildings, armory is now upgraded to advanced armory +// +// Revision 1.19 2002/06/03 16:50:35 Flayra +// - Renamed weapons factory and armory, added ammo resupplying +// +// Revision 1.18 2002/05/28 17:51:34 Flayra +// - Tried to make nuke sound play, extended shake duration to sound length, reinforcement refactoring, mark command stations as mapper placed, so they aren't deleted on level cleanup, support for point-entity buildable command stations +// +// Revision 1.17 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifndef AVHMARINEEQUIPMENT_H +#define AVHMARINEEQUIPMENT_H + +#include "util/nowarnings.h" +#include "dlls/weapons.h" +#include "mod/AvHBasePlayerWeapon.h" +#include "mod/AvHConstants.h" +#include "mod/AvHMarineWeapons.h" +#include "dlls/turret.h" +#include "mod/AvHEntities.h" +#include "dlls/ctripmine.h" +#include "mod/AvHReinforceable.h" + +class AvHDeployedMine : public CBasePlayerItem +{ +public: + void EXPORT ActiveThink(); + void EXPORT ActiveTouch(CBaseEntity* inOther); + void Detonate(); + void Precache(void); + void EXPORT PowerupThink(); + void SetPlacer(entvars_t* inPlacer); + void Spawn(void); + + void Killed(entvars_t* inAttacker, int inGib); + int TakeDamage(entvars_t *inInflictor, entvars_t *inAttacker, float inDamage, int inBitsDamageType); + +private: + void DetonateIfOwnerInvalid(); + void Explode(TraceResult* inTrace, int inBitsDamageType); + void EXPORT Smoke(); + + Vector mVecDir; + Vector mVecEnd; + Vector mOwnerOrigin; + Vector mOwnerAngles; + + float mTimePlaced; + float mLastTimeTouched; + bool mDetonated; + bool mPoweredUp; + EHANDLE mOwner; + entvars_t* mPlacer; +}; + +class AvHPlayerEquipment : public CBasePlayerItem +{ +public: + AvHPlayerEquipment(); + + virtual int GetLifetime() const; + + virtual bool GetIsPersistent() const; + virtual void SetPersistent(); + + virtual void KeyValue(KeyValueData* pkvd); + +private: + bool mIsPersistent; + int mLifetime; +}; + +class AvHHealth : public AvHPlayerEquipment +{ +public: + // : 1017 GiveHealth now takes the amount as a paramater. + static BOOL GiveHealth(CBaseEntity* inOther, float points); + + void Precache( void ); + void Spawn( void ); + void EXPORT Touch(CBaseEntity* inOther); +}; + +class AvHCatalyst : public AvHPlayerEquipment +{ +public: + static BOOL GiveCatalyst(CBaseEntity* inOther); + + void Precache( void ); + void Spawn( void ); + void EXPORT Touch(CBaseEntity* inOther); +}; + +class AvHGenericAmmo : public CBasePlayerAmmo +{ +public: + static BOOL GiveAmmo(CBaseEntity* inOther); + + BOOL AddAmmo( CBaseEntity *pOther ); + void Precache( void ); + void Spawn( void ); + + void EXPORT Dropped(void); + +private: + bool mDropped; +}; + +class AvHHeavyArmor : public AvHPlayerEquipment +{ +public: + void Precache( void ); + void Spawn( void ); + void EXPORT Touch(CBaseEntity* inOther); +}; + +class AvHJetpack : public AvHPlayerEquipment +{ +public: + void Precache( void ); + void Spawn( void ); + void EXPORT Touch(CBaseEntity* inOther); +}; + +class AvHAmmoPack : public AvHPlayerEquipment +{ +public: + char m_szAmmoType[32]; + int m_iMaxAmmo; + int m_iAmmoAmt; + int m_iWeaponID; //weapon id this is for. + float m_flNoTouch; //Dont let anyone touch it while its falling + + void Precache( void ); + void Spawn( void ); + void EXPORT Touch(CBaseEntity* inOther); +}; + +class AvHScan : public CBaseAnimating +{ +public: + AvHScan(); + void Precache(void); + void Spawn(void); + void ScanThink(); + +private: + float mTimeCreated; + +}; + +class AvHMarineBaseBuildable : public AvHBaseBuildable +{ +public: + AvHMarineBaseBuildable(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser3); + + virtual char* GetDeploySound() const; + virtual char* GetKilledSound() const; + virtual int GetPointValue() const; + virtual int GetTakeDamageAnimation() const; + virtual void ResetEntity(); + virtual int TakeDamage(entvars_t* inInflictor, entvars_t* inAttacker, float inDamage, int inBitsDamageType); + virtual bool GetIsTechnologyAvailable(AvHMessageID inMessageID) const; + virtual void TechnologyBuilt(AvHMessageID inMessageID); + +protected: + void SetEnergy(float inEnergy); + float mEnergy; + +}; + +const float kPhaseGateIdleThink = 1.0f; + +class AvHPhaseGate : public AvHMarineBaseBuildable +{ +public: + AvHPhaseGate(); + virtual int GetIdleAnimation() const; + bool GetIsEnabled() const; + virtual int GetSequenceForBoundingBox() const; + void Killed(entvars_t* inAttacker, int inGib); + void Precache(void); + void EXPORT IdleThink(); + virtual void ResetEntity(); + virtual void SetHasBeenBuilt(); + void SetTimeOfLastDeparture(float timeOfLastDeparture); + bool IsReadyToUse(); + bool HasWarmedUp() const; + void EXPORT TeleportUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); + //void EXPORT TeleportTouch(CBaseEntity *pOther); + virtual void UpdateOnRecycle(void); + virtual void UpdateOnRemove(void); + +private: + + void KillBuildablesTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor); + bool GetAreTeammatesBlocking(AvHPlayer* thePlayer, const Vector& inOrigin) const; + void SetEnabled(bool inEnabledState); + bool mEnabled; + bool mHasWarmedUp; + float mTimeOfLastDeparture; +}; + +class AvHNuke : public AvHMarineBaseBuildable +{ +public: + AvHNuke(); + + virtual void Precache(); + + void EXPORT ActiveThink(); + + void EXPORT DeathThink(); + + virtual char* GetDeploySound() const; + + virtual char* GetKilledSound() const; + + virtual void Spawn(); + +private: + bool mActive; + float mTimeActivated; + +}; + +class AvHInfantryPortal : public AvHMarineBaseBuildable, public AvHReinforceable +{ +public: + AvHInfantryPortal(); + + virtual void Killed(entvars_t* inAttacker, int inGib); + + virtual float GetReinforceTime() const; + + void EXPORT PortalThink(); + + virtual void Precache(); + + virtual void ResetEntity(); + + virtual void SetHasBeenBuilt(); + + // From AvHReinforceable + virtual void CueRespawnEffect(AvHPlayer* inPlayer); + + virtual bool GetCanReinforce() const; + + virtual bool GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const; + + virtual AvHTeamNumber GetReinforceTeamNumber() const; + + virtual void UpdateOnRecycle(void); + virtual void UpdateOnRemove(void); + + virtual int GetIdleAnimation() const; + virtual int GetIdle1Animation() const; + virtual int GetIdle2Animation() const; + + // virtual int GetDeployAnimation() const; + // virtual int GetSpawnAnimation() const; + +protected: + + virtual void ResetReinforcingPlayer(bool inSuccess); + +}; + +class AvHCommandStation : public AvHMarineBaseBuildable +{ +public: + AvHCommandStation(); + + void EXPORT CommandTouch( CBaseEntity *pOther ); + + void EXPORT CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + virtual int GetIdleAnimation() const; + + virtual bool GetIsTechnologyAvailable(AvHMessageID inMessageID) const; + + virtual char* GetKilledSound() const; + + virtual int GetPointValue() const; + + virtual void Killed( entvars_t *pevAttacker, int iGib ); + + virtual void Materialize(); + + virtual int ObjectCaps(void); + + virtual void Precache(void); + + virtual void SetHasBeenBuilt(); + + virtual void SetInactive(); + + virtual void Spawn(void); + + virtual int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + +private: + void EXPORT ActivateThink(void); + void EXPORT CommanderUsingThink(void); + void EjectCommander(); + + int mCommanderAtThisStation; + float mTimeToPlayOnlineSound; + +}; + + +class AvHTurretFactory : public AvHMarineBaseBuildable +{ +public: + AvHTurretFactory(); + + virtual int GetIdle1Animation() const; + + virtual int GetIdle2Animation() const; + + virtual int GetResearchAnimation() const; + + virtual bool GetSupportsTechID(AvHTechID inTechID) const; + + virtual void SetHasBeenBuilt(); + + virtual void TriggerAddTech() const; + + virtual void TriggerRemoveTech() const; + + virtual void Upgrade(); + +private: + virtual void CheckTurretEnabledState() const; + +}; + +class AvHArmory : public AvHMarineBaseBuildable +{ +public: + AvHArmory(); + + virtual int GetActiveAnimation() const; + + virtual int GetIdle1Animation() const; + + virtual int GetIdle2Animation() const; + + virtual int GetResearchAnimation() const; + + virtual int GetSequenceForBoundingBox() const; + + virtual bool GetSupportsTechID(AvHTechID inTechID) const; + + virtual void Precache(); + + //void EXPORT ResupplyThink(); + void EXPORT ResupplyUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue); + + virtual void SetHasBeenBuilt(); + + virtual void Upgrade(); + +}; + +class AvHArmsLab : public AvHMarineBaseBuildable +{ +public: + AvHArmsLab(); + + virtual int GetResearchAnimation() const; + + virtual int GetSequenceForBoundingBox() const; + +}; + +class AvHPrototypeLab : public AvHMarineBaseBuildable +{ +public: + AvHPrototypeLab(); +}; + +class AvHObservatory : public AvHMarineBaseBuildable +{ +public: + AvHObservatory(); + virtual int GetSequenceForBoundingBox() const; + virtual void Materialize(); + void EXPORT ObservatoryThink(); + virtual void ResetEntity(); + virtual void SetHasBeenBuilt(); + virtual void Spawn(); + + virtual int GetActiveAnimation() const; + virtual int GetIdle1Animation() const; + virtual int GetIdle2Animation() const; + virtual int GetResearchAnimation() const; + +}; + +//class AvHChemLab : public AvHMarineBaseBuildable +//{ +//public: +// AvHChemLab(); +//}; +// +//class AvHMedLab : public AvHMarineBaseBuildable +//{ +//public: +// AvHMedLab(); +//}; +// +//class AvHNukePlant : public AvHMarineBaseBuildable +//{ +//public: +// AvHNukePlant(); +//}; + +#endif diff --git a/main/source/mod/AvHMarineWeapon.cpp b/main/source/mod/AvHMarineWeapon.cpp index dfab985..7f43343 100644 --- a/main/source/mod/AvHMarineWeapon.cpp +++ b/main/source/mod/AvHMarineWeapon.cpp @@ -1,247 +1,247 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHMarineWeapon.cpp $ -// $Date: 2002/11/22 21:28:16 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHMarineWeapon.cpp,v $ -// Revision 1.4 2002/11/22 21:28:16 Flayra -// - mp_consistency changes -// -// Revision 1.3 2002/10/03 18:58:15 Flayra -// - Added heavy view models -// -// Revision 1.2 2002/06/25 17:50:59 Flayra -// - Reworking for correct player animations and new enable/disable state, new view model artwork, alien weapon refactoring -// -// Revision 1.1 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHMarineWeapon.h" -#include "mod/AvHMarineWeaponConstants.h" -#include "mod/AvHSpecials.h" -#include "util/Balance.h" - -bool AvHMarineWeapon::GetAllowedForUser3(AvHUser3 inUser3) -{ - bool theAllowed = false; - - // Alien weapons for aliens. Don't take into account exact roles until needed (and until weapons have stabilized) - switch(inUser3) - { - case AVH_USER3_MARINE_PLAYER: - theAllowed = true; - break; - } - - return theAllowed; -} - -float AvHMarineWeapon::GetDeploySoundVolume() const -{ - return kDeployMarineWeaponVolume; -} - -char* AvHMarineWeapon::GetHeavyViewModel() const -{ - return NULL; -} - -float AvHMarineWeapon::ComputeAttackInterval() const -{ - float theROF = this->GetRateOfFire(); - - int theUser4 = this->m_pPlayer->pev->iuser4; - - // Speed attack if in range of primal scream - if(GetHasUpgrade(theUser4, MASK_BUFFED)) - { - float theCatalystROFFactor = 1.0f + BALANCE_VAR(kCatalystROFFactor); - theROF /= theCatalystROFFactor; - } - - return theROF; - -} - -char* AvHMarineWeapon::GetActiveViewModel() const -{ - char* theViewModel = this->GetViewModel(); - - // If we're a marine with heavy armor, use the heavy view model - if(this->m_pPlayer && (this->m_pPlayer->pev->iuser3 == AVH_USER3_MARINE_PLAYER || this->m_pPlayer->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) && (GetHasUpgrade(this->m_pPlayer->pev->iuser4, MASK_UPGRADE_13))) - { - char* theHeavyViewModel = this->GetHeavyViewModel(); - if(theHeavyViewModel) - { - theViewModel = theHeavyViewModel; - } - } - - return theViewModel; -} - -void AvHMarineWeapon::Precache() -{ - AvHBasePlayerWeapon::Precache(); - - char* theHeavyViewModel = this->GetHeavyViewModel(); - if(theHeavyViewModel) - { - PRECACHE_UNMODIFIED_MODEL(theHeavyViewModel); - } -} - - -// AvHReloadableMarineWeapon -const int kSpecialReloadNone = 0; -const int kSpecialReloadGotoReload = 1; -const int kSpecialReloadReloadShell = 2; - -void AvHReloadableMarineWeapon::DeductCostForShot(void) -{ - AvHMarineWeapon::DeductCostForShot(); - - // Stop reload if we were in the middle of one - if(this->mSpecialReload != kSpecialReloadNone) - { - this->mSpecialReload = kSpecialReloadNone; - } -} - -int AvHReloadableMarineWeapon::DefaultReload( int iClipSize, int iAnim, float fDelay ) -{ - // Needed to prevet super fast default reload - return FALSE; -} - -void AvHReloadableMarineWeapon::Holster( int skiplocal) -{ - AvHMarineWeapon::Holster(skiplocal); - - // Cancel any reload in progress. - this->mSpecialReload = kSpecialReloadNone; -} - -void AvHReloadableMarineWeapon::Init() -{ - this->mSpecialReload = kSpecialReloadNone; - this->mNextReload = 0; -} - -void AvHReloadableMarineWeapon::Reload(void) -{ - int theReloadAnimation = this->GetReloadAnimation(); - float theReloadTime = this->GetReloadTime(); - int theClipSize = this->GetClipSize(); - - if((this->m_pPlayer->m_rgAmmo[this->m_iPrimaryAmmoType] > 0) && (m_iClip < theClipSize)) - { - // don't reload until recoil is done - if(this->m_flNextPrimaryAttack <= UTIL_WeaponTimeBase()) - { - if(this->mSpecialReload == kSpecialReloadNone) - { - // Start reload - this->mSpecialReload = kSpecialReloadGotoReload; - - this->SendWeaponAnim(this->GetGotoReloadAnimation()); - - float theGotoReloadAnimationTime = this->GetGotoReloadAnimationTime(); - - this->m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + theGotoReloadAnimationTime; - this->m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + theGotoReloadAnimationTime; - - this->m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + theGotoReloadAnimationTime; // 1.0f - this->m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + theGotoReloadAnimationTime; // 1.0f - this->m_pPlayer->SetAnimation(PLAYER_RELOAD_START); - - } - else if(this->mSpecialReload == kSpecialReloadGotoReload) - { - if (m_flTimeWeaponIdle <= UTIL_WeaponTimeBase()) - { - // was waiting for gun to move to side - this->mSpecialReload = kSpecialReloadReloadShell; - - this->SendWeaponAnim(this->GetShellReloadAnimation()); - - float theShellReloadTime = this->GetShellReloadAnimationTime(); - - this->mNextReload = UTIL_WeaponTimeBase() + theShellReloadTime; - this->m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + theShellReloadTime; - - this->m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + theShellReloadTime; - this->m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + theShellReloadTime; - this->m_pPlayer->SetAnimation(PLAYER_RELOAD_INSERT); - - } - } - else if(this->mSpecialReload == kSpecialReloadReloadShell) - { - //DefaultReload(theClipSize, theReloadAnimation, theReloadTime); - - // Don't idle for a bit - //this->SetNextIdle(); - - // Add them to the clip - this->m_iClip += 1; - this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 1; - this->mSpecialReload = kSpecialReloadGotoReload; - this->m_pPlayer->SetAnimation(PLAYER_RELOAD_END); - } - - - } - - } -} - - -void AvHReloadableMarineWeapon::WeaponIdle(void) -{ - // tankefugl: 0000484 - ensures that all idle weapons can fire the empty sound - ResetEmptySound(); - - if(this->m_flTimeWeaponIdle < UTIL_WeaponTimeBase()) - { - if((this->m_iClip == 0) && (this->mSpecialReload == kSpecialReloadNone) && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) - { - this->Reload(); - } - else if(this->mSpecialReload != kSpecialReloadNone) - { - if((m_iClip != this->GetClipSize()) && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) - { - this->Reload(); - } - else - { - // reload debounce has timed out - this->mSpecialReload = kSpecialReloadNone; - - this->SendWeaponAnim(this->GetEndReloadAnimation()); - - float theEndReloadAnimationTime = this->GetEndReloadAnimationTime(); - - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + theEndReloadAnimationTime; - } - } - else - { - // Hack to prevent idle animation from playing mid-reload. Not sure how to fix this right, but all this special reloading is happening server-side, client doesn't know about it - if(m_iClip == this->GetClipSize()) - { - AvHMarineWeapon::WeaponIdle(); - } - } - } -} +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHMarineWeapon.cpp $ +// $Date: 2002/11/22 21:28:16 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHMarineWeapon.cpp,v $ +// Revision 1.4 2002/11/22 21:28:16 Flayra +// - mp_consistency changes +// +// Revision 1.3 2002/10/03 18:58:15 Flayra +// - Added heavy view models +// +// Revision 1.2 2002/06/25 17:50:59 Flayra +// - Reworking for correct player animations and new enable/disable state, new view model artwork, alien weapon refactoring +// +// Revision 1.1 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHMarineWeapon.h" +#include "mod/AvHMarineWeaponConstants.h" +#include "mod/AvHSpecials.h" +#include "util/Balance.h" + +bool AvHMarineWeapon::GetAllowedForUser3(AvHUser3 inUser3) +{ + bool theAllowed = false; + + // Alien weapons for aliens. Don't take into account exact roles until needed (and until weapons have stabilized) + switch(inUser3) + { + case AVH_USER3_MARINE_PLAYER: + theAllowed = true; + break; + } + + return theAllowed; +} + +float AvHMarineWeapon::GetDeploySoundVolume() const +{ + return kDeployMarineWeaponVolume; +} + +char* AvHMarineWeapon::GetHeavyViewModel() const +{ + return NULL; +} + +float AvHMarineWeapon::ComputeAttackInterval() const +{ + float theROF = this->GetRateOfFire(); + + int theUser4 = this->m_pPlayer->pev->iuser4; + + // Speed attack if in range of primal scream + if(GetHasUpgrade(theUser4, MASK_BUFFED)) + { + float theCatalystROFFactor = 1.0f + BALANCE_VAR(kCatalystROFFactor); + theROF /= theCatalystROFFactor; + } + + return theROF; + +} + +char* AvHMarineWeapon::GetActiveViewModel() const +{ + char* theViewModel = this->GetViewModel(); + + // If we're a marine with heavy armor, use the heavy view model + if(this->m_pPlayer && (this->m_pPlayer->pev->iuser3 == AVH_USER3_MARINE_PLAYER || this->m_pPlayer->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) && (GetHasUpgrade(this->m_pPlayer->pev->iuser4, MASK_UPGRADE_13))) + { + char* theHeavyViewModel = this->GetHeavyViewModel(); + if(theHeavyViewModel) + { + theViewModel = theHeavyViewModel; + } + } + + return theViewModel; +} + +void AvHMarineWeapon::Precache() +{ + AvHBasePlayerWeapon::Precache(); + + char* theHeavyViewModel = this->GetHeavyViewModel(); + if(theHeavyViewModel) + { + PRECACHE_UNMODIFIED_MODEL(theHeavyViewModel); + } +} + + +// AvHReloadableMarineWeapon +const int kSpecialReloadNone = 0; +const int kSpecialReloadGotoReload = 1; +const int kSpecialReloadReloadShell = 2; + +void AvHReloadableMarineWeapon::DeductCostForShot(void) +{ + AvHMarineWeapon::DeductCostForShot(); + + // Stop reload if we were in the middle of one + if(this->mSpecialReload != kSpecialReloadNone) + { + this->mSpecialReload = kSpecialReloadNone; + } +} + +int AvHReloadableMarineWeapon::DefaultReload( int iClipSize, int iAnim, float fDelay ) +{ + // Needed to prevet super fast default reload + return FALSE; +} + +void AvHReloadableMarineWeapon::Holster( int skiplocal) +{ + AvHMarineWeapon::Holster(skiplocal); + + // Cancel any reload in progress. + this->mSpecialReload = kSpecialReloadNone; +} + +void AvHReloadableMarineWeapon::Init() +{ + this->mSpecialReload = kSpecialReloadNone; + this->mNextReload = 0; +} + +void AvHReloadableMarineWeapon::Reload(void) +{ + int theReloadAnimation = this->GetReloadAnimation(); + float theReloadTime = this->GetReloadTime(); + int theClipSize = this->GetClipSize(); + + if((this->m_pPlayer->m_rgAmmo[this->m_iPrimaryAmmoType] > 0) && (m_iClip < theClipSize)) + { + // don't reload until recoil is done + if(this->m_flNextPrimaryAttack <= UTIL_WeaponTimeBase()) + { + if(this->mSpecialReload == kSpecialReloadNone) + { + // Start reload + this->mSpecialReload = kSpecialReloadGotoReload; + + this->SendWeaponAnim(this->GetGotoReloadAnimation()); + + float theGotoReloadAnimationTime = this->GetGotoReloadAnimationTime(); + + this->m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + theGotoReloadAnimationTime; + this->m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + theGotoReloadAnimationTime; + + this->m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + theGotoReloadAnimationTime; // 1.0f + this->m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + theGotoReloadAnimationTime; // 1.0f + this->m_pPlayer->SetAnimation(PLAYER_RELOAD_START); + + } + else if(this->mSpecialReload == kSpecialReloadGotoReload) + { + if (m_flTimeWeaponIdle <= UTIL_WeaponTimeBase()) + { + // was waiting for gun to move to side + this->mSpecialReload = kSpecialReloadReloadShell; + + this->SendWeaponAnim(this->GetShellReloadAnimation()); + + float theShellReloadTime = this->GetShellReloadAnimationTime(); + + this->mNextReload = UTIL_WeaponTimeBase() + theShellReloadTime; + this->m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + theShellReloadTime; + + this->m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + theShellReloadTime; + this->m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + theShellReloadTime; + this->m_pPlayer->SetAnimation(PLAYER_RELOAD_INSERT); + + } + } + else if(this->mSpecialReload == kSpecialReloadReloadShell) + { + //DefaultReload(theClipSize, theReloadAnimation, theReloadTime); + + // Don't idle for a bit + //this->SetNextIdle(); + + // Add them to the clip + this->m_iClip += 1; + this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 1; + this->mSpecialReload = kSpecialReloadGotoReload; + this->m_pPlayer->SetAnimation(PLAYER_RELOAD_END); + } + + + } + + } +} + + +void AvHReloadableMarineWeapon::WeaponIdle(void) +{ + // : 0000484 - ensures that all idle weapons can fire the empty sound + ResetEmptySound(); + + if(this->m_flTimeWeaponIdle < UTIL_WeaponTimeBase()) + { + if((this->m_iClip == 0) && (this->mSpecialReload == kSpecialReloadNone) && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) + { + this->Reload(); + } + else if(this->mSpecialReload != kSpecialReloadNone) + { + if((m_iClip != this->GetClipSize()) && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) + { + this->Reload(); + } + else + { + // reload debounce has timed out + this->mSpecialReload = kSpecialReloadNone; + + this->SendWeaponAnim(this->GetEndReloadAnimation()); + + float theEndReloadAnimationTime = this->GetEndReloadAnimationTime(); + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + theEndReloadAnimationTime; + } + } + else + { + // Hack to prevent idle animation from playing mid-reload. Not sure how to fix this right, but all this special reloading is happening server-side, client doesn't know about it + if(m_iClip == this->GetClipSize()) + { + AvHMarineWeapon::WeaponIdle(); + } + } + } +} diff --git a/main/source/mod/AvHMarineWeaponConstants.h b/main/source/mod/AvHMarineWeaponConstants.h index 35ce176..8eb94e4 100644 --- a/main/source/mod/AvHMarineWeaponConstants.h +++ b/main/source/mod/AvHMarineWeaponConstants.h @@ -152,7 +152,7 @@ const int kHGBarrelLength = 10; #define kHGSpread VECTOR_CONE_1DEGREES // Sonic/Shot gun constants. -const int kSGRange = 8192; +const int kSGRange = 700; const float kSGXPunch = .8f; #define kSGEjectModel "models/shotshell.mdl" #define kSGEventName "events/SonicGun.sc" @@ -169,7 +169,8 @@ const float kSGXPunch = .8f; #define kSGDeploySound "weapons/sg-deploy.wav" const int kSGBarrelLength = 25; #define kSGSpread VECTOR_CONE_20DEGREES -#define kSGInnerSpread VECTOR_CONE_7DEGREES +#define kSGMidSpread VECTOR_CONE_8DEGREES +#define kSGInnerSpread VECTOR_CONE_3DEGREES // Heavy machine gun const int kHMGRange = 6000; diff --git a/main/source/mod/AvHMine.cpp b/main/source/mod/AvHMine.cpp index 5c8ef8a..507d7ea 100644 --- a/main/source/mod/AvHMine.cpp +++ b/main/source/mod/AvHMine.cpp @@ -1,353 +1,355 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: This is the weapon that marines use to deploy mines (not the mines themselves) -// -// $Workfile: AvHMine.cpp $ -// $Date: 2002/10/25 21:48:21 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHMine.cpp,v $ -// Revision 1.10 2002/10/25 21:48:21 Flayra -// - Fixe for wrong player model when holding mines -// -// Revision 1.9 2002/10/16 20:53:09 Flayra -// - Removed weapon upgrade sounds -// -// Revision 1.8 2002/10/16 01:01:58 Flayra -// - Fixed mines being resupplied from armory -// -// Revision 1.7 2002/10/03 18:46:17 Flayra -// - Added heavy view model -// -// Revision 1.6 2002/07/24 19:09:17 Flayra -// - Linux issues -// -// Revision 1.5 2002/07/24 18:55:52 Flayra -// - Linux case sensitivity stuff -// -// Revision 1.4 2002/07/24 18:45:42 Flayra -// - Linux and scripting changes -// -// Revision 1.3 2002/06/25 17:47:14 Flayra -// - Fixed mine, refactored for new disabled/enabled state -// -// Revision 1.2 2002/06/03 16:37:31 Flayra -// - Constants and tweaks to make weapon anims and times correct with new artwork, added different deploy times (this should be refactored a bit more) -// -// Revision 1.1 2002/05/23 02:33:42 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHMarineWeapons.h" -#include "mod/AvHPlayer.h" - -#ifdef AVH_CLIENT -#include "cl_dll/eventscripts.h" -#include "cl_dll/in_defs.h" -#include "cl_dll/wrect.h" -#include "cl_dll/cl_dll.h" -#endif - -#include "common/hldm.h" -#include "common/event_api.h" -#include "common/event_args.h" -#include "common/vector_util.h" -#include "mod/AvHMarineWeapons.h" -#include "mod/AvHMarineEquipmentConstants.h" - -#ifdef AVH_SERVER -#include "mod/AvHGamerules.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHServerUtil.h" -#endif - -LINK_ENTITY_TO_CLASS(kwMine, AvHMine); - -void AvHMine::DeductCostForShot(void) -{ - AvHBasePlayerWeapon::DeductCostForShot(); - - //this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; - - //if(this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) - if(!this->m_iClip) - { - // no more mines! - RetireWeapon(); - } - -} - -bool AvHMine::GetCanBeResupplied() const -{ - return false; -} - -int AvHMine::GetDeployAnimation() const -{ - return 2; -} - -char* AvHMine::GetHeavyViewModel() const -{ - return kTripmineHVVModel; -} - -char* AvHMine::GetPlayerModel() const -{ - return kTripminePModel; -} - -char* AvHMine::GetWorldModel() const -{ - return kTripmineWModel; -} - -char* AvHMine::GetViewModel() const -{ - return kTripmineVModel; -} - -int AvHMine::GetShootAnimation() const -{ - // Return deploy animation for now, this should play fire animation, then a little later, play the deploy animation - return 2; -} - -void AvHMine::Holster(int skiplocal) -{ - if(!this->m_iClip) - { - // Out of mines - SetThink(&AvHMine::DestroyItem); - this->pev->nextthink = gpGlobals->time + 0.1; - } - - AvHMarineWeapon::Holster(skiplocal); -} - -void AvHMine::Init() -{ - this->mRange = kMineRange; - this->mDamage = 0; // What to do here? Is it taking damage from CGrenade? -} - -bool AvHMine::ProcessValidAttack(void) -{ - bool theSuccess = AvHMarineWeapon::ProcessValidAttack(); - - // This test is not necessary since the new collision code makes it so - // that interpenetrating objects are not a problem. - - /* - if(theSuccess) - { - #ifdef AVH_SERVER - theSuccess = false; - - Vector theDropLocation; - Vector theDropAngles; - if(this->GetDropLocation(theDropLocation, &theDropAngles)) - { - Vector theMineMinSize = Vector (kMineMinSize); - Vector theMineMaxSize = Vector (kMineMaxSize); - - // TODO: Rotate extents by theDropAngles, to test bounding box extents as the mine would be placed - - if(AvHSHUGetIsAreaFree(theDropLocation, theMineMinSize, theMineMaxSize)) - { - theSuccess = true; - } - } - #endif - } - */ -#ifdef AVH_SERVER - - if(theSuccess) - { - - Vector theMineOrigin; - Vector theMineAngles; - - theSuccess = this->GetDropLocation(theMineOrigin, &theMineAngles); - - } - -#endif - - return theSuccess; -} - -#ifdef AVH_SERVER -bool AvHMine::GetDropLocation(Vector& outLocation, Vector* outAngles) const -{ - bool theSuccess = false; - - UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); - Vector vecSrc = m_pPlayer->GetGunPosition( ); - Vector vecAiming = gpGlobals->v_forward; - - TraceResult tr; - - UTIL_TraceLine( vecSrc, vecSrc + vecAiming*this->mRange, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr ); - - if (tr.flFraction < 1.0) - { - CBaseEntity* theEntity = CBaseEntity::Instance( tr.pHit ); - - // puzl: 981 - // Mines can't be planted on players or buildings - if (!dynamic_cast(theEntity) && !dynamic_cast(theEntity) && !dynamic_cast(theEntity)) - { - - int kOffset = 8; - Vector thePotentialOrigin = tr.vecEndPos + tr.vecPlaneNormal * kOffset; - - BaseEntityListType theEntityList; - theEntityList.push_back(theEntity); - - // Make sure there isn't an entity nearby that this would block - theEntity = NULL; - const int kMineSearchRadius = 15; - while((theEntity = UTIL_FindEntityInSphere(theEntity, thePotentialOrigin, kMineSearchRadius)) != NULL) - { - theEntityList.push_back(theEntity); - } - - // For the mine placement to be valid, the entity it hit, and all the entities nearby must be valid and non-blocking - theSuccess = true; - for(BaseEntityListType::iterator theIter = theEntityList.begin(); theIter != theEntityList.end(); theIter++) - { - // puzl: 225 make sure there are no mines within kMineSearchRadius of each other ( 15 units ) - CBaseEntity* theCurrentEntity = *theIter; - if(!theCurrentEntity || (theCurrentEntity->pev->flags & FL_CONVEYOR) || AvHSUGetIsExternalClassName(STRING(theCurrentEntity->pev->classname)) || dynamic_cast(theCurrentEntity) || dynamic_cast(theCurrentEntity) - || dynamic_cast(theCurrentEntity) ) - { - theSuccess = false; - break; - } - } - - if(theSuccess) - { - VectorCopy(thePotentialOrigin, outLocation); - if(outAngles) - { - VectorCopy(UTIL_VecToAngles( tr.vecPlaneNormal ), *outAngles) - } - } - - } - - } - - return theSuccess; -} -#endif - -void AvHMine::FireProjectiles(void) -{ -#ifdef AVH_SERVER - Vector theMineOrigin; - Vector theMineAngles; - if(this->GetDropLocation(theMineOrigin, &theMineAngles)) - { - GetGameRules()->MarkDramaticEvent(kMinePlacePriority, this->m_pPlayer); - - AvHDeployedMine* theMine = dynamic_cast(CBaseEntity::Create( kwsDeployedMine, theMineOrigin, theMineAngles, m_pPlayer->edict() )); - ASSERT(theMine); - - // Set the team so it doesn't blow us up, remember the owner so proper credit can be given - theMine->pev->team = m_pPlayer->pev->team; - //theMine->pev->owner = m_pPlayer->edict(); - theMine->SetPlacer(this->m_pPlayer->pev); - - // Set it as a marine item so it gets damage upgrades - // Set any team-wide upgrades - AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(m_pPlayer->pev->team)); - ASSERT(theTeam); - theMine->pev->iuser4 |= theTeam->GetTeamWideUpgrades(); - } -#endif -} - -int AvHMine::GetBarrelLength() const -{ - return kMineBarrellLength; -} - -float AvHMine::GetRateOfFire() const -{ - return kMineROF; -} - -bool AvHMine::GetFiresUnderwater() const -{ - return true; -} - -BOOL AvHMine::PlayEmptySound() -{ - // None - return 0; -} - -void AvHMine::Precache() -{ - AvHMarineWeapon::Precache(); - - UTIL_PrecacheOther(kwsDeployedMine); - - this->mEvent = PRECACHE_EVENT(1, kWeaponAnimationEvent); -} - -bool AvHMine::Resupply() -{ - return false; -} - -void AvHMine::Spawn() -{ - this->Precache(); - - AvHMarineWeapon::Spawn(); - - this->m_iId = AVH_WEAPON_MINE; - - // Set our class name - this->pev->classname = MAKE_STRING(kwsMine); - - SET_MODEL(ENT(pev), kTripmineW2Model); - - FallInit();// get ready to fall down. - - int theNumMines = BALANCE_VAR(kMineMaxAmmo); - - #ifdef AVH_SERVER - if(GetGameRules()->GetIsCombatMode()) - { - theNumMines = BALANCE_VAR(kMineMaxAmmoCombat); - } - #endif - - this->m_iDefaultAmmo = theNumMines; -} - - -bool AvHMine::UsesAmmo(void) const -{ - return true; -} - -BOOL AvHMine::UseDecrement(void) -{ - return true; -} - +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: This is the weapon that marines use to deploy mines (not the mines themselves) +// +// $Workfile: AvHMine.cpp $ +// $Date: 2002/10/25 21:48:21 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHMine.cpp,v $ +// Revision 1.10 2002/10/25 21:48:21 Flayra +// - Fixe for wrong player model when holding mines +// +// Revision 1.9 2002/10/16 20:53:09 Flayra +// - Removed weapon upgrade sounds +// +// Revision 1.8 2002/10/16 01:01:58 Flayra +// - Fixed mines being resupplied from armory +// +// Revision 1.7 2002/10/03 18:46:17 Flayra +// - Added heavy view model +// +// Revision 1.6 2002/07/24 19:09:17 Flayra +// - Linux issues +// +// Revision 1.5 2002/07/24 18:55:52 Flayra +// - Linux case sensitivity stuff +// +// Revision 1.4 2002/07/24 18:45:42 Flayra +// - Linux and scripting changes +// +// Revision 1.3 2002/06/25 17:47:14 Flayra +// - Fixed mine, refactored for new disabled/enabled state +// +// Revision 1.2 2002/06/03 16:37:31 Flayra +// - Constants and tweaks to make weapon anims and times correct with new artwork, added different deploy times (this should be refactored a bit more) +// +// Revision 1.1 2002/05/23 02:33:42 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHMarineWeapons.h" +#include "mod/AvHPlayer.h" + +#ifdef AVH_CLIENT +#include "cl_dll/eventscripts.h" +#include "cl_dll/in_defs.h" +#include "cl_dll/wrect.h" +#include "cl_dll/cl_dll.h" +#endif + +#include "common/hldm.h" +#include "common/event_api.h" +#include "common/event_args.h" +#include "common/vector_util.h" +#include "mod/AvHMarineWeapons.h" +#include "mod/AvHMarineEquipmentConstants.h" + +#ifdef AVH_SERVER +#include "mod/AvHGamerules.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHServerUtil.h" +#endif + +LINK_ENTITY_TO_CLASS(kwMine, AvHMine); + +void AvHMine::DeductCostForShot(void) +{ + AvHBasePlayerWeapon::DeductCostForShot(); + + //this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; + + //if(this->m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + if(!this->m_iClip) + { + if ( this->m_pPlayer ) + this->m_pPlayer->EffectivePlayerClassChanged(); + // no more mines! + RetireWeapon(); + } + +} + +bool AvHMine::GetCanBeResupplied() const +{ + return false; +} + +int AvHMine::GetDeployAnimation() const +{ + return 2; +} + +char* AvHMine::GetHeavyViewModel() const +{ + return kTripmineHVVModel; +} + +char* AvHMine::GetPlayerModel() const +{ + return kTripminePModel; +} + +char* AvHMine::GetWorldModel() const +{ + return kTripmineWModel; +} + +char* AvHMine::GetViewModel() const +{ + return kTripmineVModel; +} + +int AvHMine::GetShootAnimation() const +{ + // Return deploy animation for now, this should play fire animation, then a little later, play the deploy animation + return 2; +} + +void AvHMine::Holster(int skiplocal) +{ + if(!this->m_iClip) + { + // Out of mines + SetThink(&AvHMine::DestroyItem); + this->pev->nextthink = gpGlobals->time + 0.1; + } + + AvHMarineWeapon::Holster(skiplocal); +} + +void AvHMine::Init() +{ + this->mRange = kMineRange; + this->mDamage = 0; // What to do here? Is it taking damage from CGrenade? +} + +bool AvHMine::ProcessValidAttack(void) +{ + bool theSuccess = AvHMarineWeapon::ProcessValidAttack(); + + // This test is not necessary since the new collision code makes it so + // that interpenetrating objects are not a problem. + + /* + if(theSuccess) + { + #ifdef AVH_SERVER + theSuccess = false; + + Vector theDropLocation; + Vector theDropAngles; + if(this->GetDropLocation(theDropLocation, &theDropAngles)) + { + Vector theMineMinSize = Vector (kMineMinSize); + Vector theMineMaxSize = Vector (kMineMaxSize); + + // TODO: Rotate extents by theDropAngles, to test bounding box extents as the mine would be placed + + if(AvHSHUGetIsAreaFree(theDropLocation, theMineMinSize, theMineMaxSize)) + { + theSuccess = true; + } + } + #endif + } + */ +#ifdef AVH_SERVER + + if(theSuccess) + { + + Vector theMineOrigin; + Vector theMineAngles; + + theSuccess = this->GetDropLocation(theMineOrigin, &theMineAngles); + + } + +#endif + + return theSuccess; +} + +#ifdef AVH_SERVER +bool AvHMine::GetDropLocation(Vector& outLocation, Vector* outAngles) const +{ + bool theSuccess = false; + + UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); + Vector vecSrc = m_pPlayer->GetGunPosition( ); + Vector vecAiming = gpGlobals->v_forward; + + TraceResult tr; + + UTIL_TraceLine( vecSrc, vecSrc + vecAiming*this->mRange, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr ); + + if (tr.flFraction < 1.0) + { + CBaseEntity* theEntity = CBaseEntity::Instance( tr.pHit ); + + // : 981 + // Mines can't be planted on players or buildings + if (!dynamic_cast(theEntity) && !dynamic_cast(theEntity) && !dynamic_cast(theEntity)) + { + + int kOffset = 8; + Vector thePotentialOrigin = tr.vecEndPos + tr.vecPlaneNormal * kOffset; + + BaseEntityListType theEntityList; + theEntityList.push_back(theEntity); + + // Make sure there isn't an entity nearby that this would block + theEntity = NULL; + const int kMineSearchRadius = 15; + while((theEntity = UTIL_FindEntityInSphere(theEntity, thePotentialOrigin, kMineSearchRadius)) != NULL) + { + theEntityList.push_back(theEntity); + } + + // For the mine placement to be valid, the entity it hit, and all the entities nearby must be valid and non-blocking + theSuccess = true; + for(BaseEntityListType::iterator theIter = theEntityList.begin(); theIter != theEntityList.end(); theIter++) + { + // : 225 make sure there are no mines within kMineSearchRadius of each other ( 15 units ) + CBaseEntity* theCurrentEntity = *theIter; + if(!theCurrentEntity || (theCurrentEntity->pev->flags & FL_CONVEYOR) || AvHSUGetIsExternalClassName(STRING(theCurrentEntity->pev->classname)) || dynamic_cast(theCurrentEntity) || dynamic_cast(theCurrentEntity) + || dynamic_cast(theCurrentEntity) ) + { + theSuccess = false; + break; + } + } + + if(theSuccess) + { + VectorCopy(thePotentialOrigin, outLocation); + if(outAngles) + { + VectorCopy(UTIL_VecToAngles( tr.vecPlaneNormal ), *outAngles) + } + } + + } + + } + + return theSuccess; +} +#endif + +void AvHMine::FireProjectiles(void) +{ +#ifdef AVH_SERVER + Vector theMineOrigin; + Vector theMineAngles; + if(this->GetDropLocation(theMineOrigin, &theMineAngles)) + { + GetGameRules()->MarkDramaticEvent(kMinePlacePriority, this->m_pPlayer); + + AvHDeployedMine* theMine = dynamic_cast(CBaseEntity::Create( kwsDeployedMine, theMineOrigin, theMineAngles, m_pPlayer->edict() )); + ASSERT(theMine); + + // Set the team so it doesn't blow us up, remember the owner so proper credit can be given + theMine->pev->team = m_pPlayer->pev->team; + //theMine->pev->owner = m_pPlayer->edict(); + theMine->SetPlacer(this->m_pPlayer->pev); + + // Set it as a marine item so it gets damage upgrades + // Set any team-wide upgrades + AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(m_pPlayer->pev->team)); + ASSERT(theTeam); + theMine->pev->iuser4 |= theTeam->GetTeamWideUpgrades(); + } +#endif +} + +int AvHMine::GetBarrelLength() const +{ + return kMineBarrellLength; +} + +float AvHMine::GetRateOfFire() const +{ + return kMineROF; +} + +bool AvHMine::GetFiresUnderwater() const +{ + return true; +} + +BOOL AvHMine::PlayEmptySound() +{ + // None + return 0; +} + +void AvHMine::Precache() +{ + AvHMarineWeapon::Precache(); + + UTIL_PrecacheOther(kwsDeployedMine); + + this->mEvent = PRECACHE_EVENT(1, kWeaponAnimationEvent); +} + +bool AvHMine::Resupply() +{ + return false; +} + +void AvHMine::Spawn() +{ + this->Precache(); + + AvHMarineWeapon::Spawn(); + + this->m_iId = AVH_WEAPON_MINE; + + // Set our class name + this->pev->classname = MAKE_STRING(kwsMine); + + SET_MODEL(ENT(pev), kTripmineW2Model); + + FallInit();// get ready to fall down. + + int theNumMines = BALANCE_VAR(kMineMaxAmmo); + + #ifdef AVH_SERVER + if(GetGameRules()->GetIsCombatMode()) + { + theNumMines = BALANCE_VAR(kMineMaxAmmoCombat); + } + #endif + + this->m_iDefaultAmmo = theNumMines; +} + + +bool AvHMine::UsesAmmo(void) const +{ + return true; +} + +BOOL AvHMine::UseDecrement(void) +{ + return true; +} + diff --git a/main/source/mod/AvHMiniMap.cpp b/main/source/mod/AvHMiniMap.cpp index 9c552ec..6c2e1cd 100644 --- a/main/source/mod/AvHMiniMap.cpp +++ b/main/source/mod/AvHMiniMap.cpp @@ -1,547 +1,549 @@ -#include "mod/AvHMiniMap.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHSpecials.h" -#include "mod/AvHNetworkMessages.h" - -#ifdef AVH_CLIENT - #include "util/hl/spritegn.h" -#endif - -#ifdef AVH_SERVER - #include "mod/AvHPlayer.h" -#endif - -const int kMaxScreenWidth = 1600; -const int kMaxScreenHeight = 1200; - -const int kHitWorldPaletteIndex = 0; -const int kBorderPaletteIndexStart = 1; -const int kBorderPaletteIndexEnd = 2; -const int kGroundStartPaletteIndex = 3; -const int kGroundEndPaletteIndex = 254; -const int kHitNothingPaletteIndex = 255; - -// Network message: -// 0: means start processing, pass map name then num samples to process, map width, map height -// 1: means update, pass num pixels, then data -// 2: means end processing - -void SafeWrite (FILE *f, void *buffer, int count) -{ - if (fwrite (buffer, 1, count, f) != (size_t)count) - { - ASSERT(false); - } -} - -// Init class members -AvHMiniMap::AvHMiniMap() -{ - this->mMap = NULL; - - this->mIsProcessing = false; - this->mNumSamplesProcessed = 0; - this->mNumSamplesToProcess = 0; - - this->mMinX = this->mMinY = this->mMaxX = this->mMaxY = 0; - this->mMinViewHeight = this->mMaxViewHeight = 0; - - #ifdef AVH_SERVER - this->mPlayer = NULL; - #endif -} - -AvHMiniMap::~AvHMiniMap() -{ - delete [] this->mMap; - this->mMap = NULL; -} - - -bool AvHMiniMap::GetIsProcessing(float* outPercentageDone) const -{ - bool theIsProcessing = false; - - if(this->mIsProcessing || (this->mNumSamplesProcessed == this->mNumSamplesToProcess && this->mNumSamplesProcessed > 0)) - { - if(outPercentageDone) - { - *outPercentageDone = (float)this->mNumSamplesProcessed/this->mNumSamplesToProcess; - } - theIsProcessing = true; - } - - return theIsProcessing; -} - - - - -#ifdef AVH_SERVER -void AvHMiniMap::BuildMiniMap(const char* inMapName, AvHPlayer* inPlayer, const AvHMapExtents& inMapExtents) -{ - const int kNumGroundFloorColors = 249; - - this->mMapName = inMapName; - this->mPlayer = inPlayer; - - // In case BuildMiniMap is called multiple times - delete [] this->mMap; - - // New a hi-res version of the map (enough for the 1600 version) - this->mMapWidth = kSpriteWidth; - this->mMapHeight = kSpriteWidth; - - this->mNumSamplesToProcess = this->mMapWidth*this->mMapHeight; - - this->mMap = new uint8[this->mNumSamplesToProcess]; - this->mNumSamplesProcessed = 0; - - this->mMinX = inMapExtents.GetMinMapX();//inMinX; - this->mMinY = inMapExtents.GetMinMapY();//inMinY; - this->mMaxX = inMapExtents.GetMaxMapX();//inMaxX; - this->mMaxY = inMapExtents.GetMaxMapY();//inMaxY; - - this->mMinViewHeight = inMapExtents.GetMinViewHeight();//inMinViewHeight; - this->mMaxViewHeight = inMapExtents.GetMaxViewHeight();//inMaxViewHeight; - - this->mIsProcessing = true; - - // Tell player to rebuild minimap - NetMsg_BuildMiniMap_Initialize( this->mPlayer->pev, this->mMapName, this->mNumSamplesToProcess, this->mMapWidth, this->mMapHeight ); -} - -bool AvHMiniMap::Process() -{ - bool theProcessingComplete = false; - - if(this->GetIsProcessing()) - { - // Process x pixels - // If we've calculated them all, return true - - // positive y on component is down, but that means negative y in world - float theDiffY = this->mMaxY - this->mMinY; - - // left to right - float theDiffX = this->mMaxX - this->mMinX; - - // Preserve map aspect ratio - float theMapAspectRatio = (this->mMaxX - this->mMinX)/(this->mMaxY - this->mMinY); - - float theXScale, theYScale; - if(theMapAspectRatio > 1.0f) - { - theXScale = 1.0f; - theYScale = 1.0f/theMapAspectRatio; - } - else - { - theXScale = 1.0f/theMapAspectRatio; - theYScale = 1.0f; - } - - float theMapCenterX = (this->mMinX + this->mMaxX)/2.0f; - float theMapCenterY = (this->mMinY + this->mMaxY)/2.0f; - - const int kNumPixelsPerCall = 50; - uint8 theSampleArray[kNumPixelsPerCall]; - memset(theSampleArray, 0, kNumPixelsPerCall); - - int i=0; - for(i = 0; (i < kNumPixelsPerCall) && (this->mNumSamplesProcessed < this->mNumSamplesToProcess); i++) - { - int theSampleIndex = this->mNumSamplesProcessed; - int theX = theSampleIndex % this->mMapWidth; - int theY = theSampleIndex/this->mMapWidth; - - // Initialize the value to outside the map - int theValue = kHitNothingPaletteIndex; - - // Account for map center and aspect ratio - float theXComponent = (theX/(float)this->mMapWidth) - .5f; - float theYComponent = (theY/(float)this->mMapHeight) - .5f; - float theCurrentX = theMapCenterX + theXComponent*theDiffX*theXScale; - float theCurrentY = theMapCenterY - theYComponent*theDiffY*theYScale; - - // If the point is inside our map boundaries, do the trace - if((theCurrentX >= this->mMinX) && (theCurrentX <= this->mMaxX) && (theCurrentY >= this->mMinY) && (theCurrentY <= this->mMaxY)) - { - // If we hit nothing, draw with the off map index - theValue = kHitNothingPaletteIndex; - int theUserThree = 0; - float theHitHeight; - float theHeightGradient = 0.0f; - - if(AvHSHUTraceVerticalTangible(theCurrentX, theCurrentY, this->mMaxViewHeight, theUserThree, theHitHeight)) - { - // TODO: Modify trace to return world brushes that are hit - // Set color to "world brush hit", it will be changed if an entity was hit - theValue = kHitWorldPaletteIndex; - - - theHitHeight = min(mMaxViewHeight, max(theHitHeight, mMinViewHeight)); - theHeightGradient = 1.0f - (this->mMaxViewHeight - theHitHeight)/(this->mMaxViewHeight - this->mMinViewHeight); - theValue = kGroundStartPaletteIndex + (kGroundEndPaletteIndex - kGroundStartPaletteIndex)*theHeightGradient; - - } - } - - int theIndex = theX + theY*this->mMapWidth; - ASSERT(theIndex < this->mNumSamplesToProcess); - this->mMap[theIndex] = theValue; - - theSampleArray[i] = theValue; - - this->mNumSamplesProcessed++; - } - - // This could be less than kNumPixelsPerCall if it's the last time through - int theNumSamples = i; - - // Tell player to rebuild minimap - NetMsg_BuildMiniMap_Update( this->mPlayer->pev, theNumSamples, theSampleArray ); - - if(this->mNumSamplesProcessed == this->mNumSamplesToProcess) - { - theProcessingComplete = true; - this->mIsProcessing = false; - - NetMsg_BuildMiniMap_Complete( this->mPlayer->pev ); - } - } - - return theProcessingComplete; -} - -#endif - - -#ifdef AVH_CLIENT -string AvHMiniMap::GetSpriteNameFromMap(int inSpriteWidth, const string& inMapName, int useLabels) -{ - char theWidthString[128]; - sprintf(theWidthString, "%d", inSpriteWidth); - // puzl: 1064 - // insert _labelled into the filename before ".spr" - string extraname=""; - if ( useLabels == 1 ) { - extraname="_labelled"; - } - string theMiniMapName = kMiniMapSpritesDirectory + string("/") /*+ string(theWidthString)*/ + inMapName + extraname + string(".spr"); - // :puzl - return theMiniMapName; -} - -void AvHMiniMap::InitializePalette() -{ - // // Test data - // memset(this->mMap, kTransparentPaletteIndex, theNumSamples); - // for(int i = 0; i < this->mMapHeight; i++) - // { - // char theFillChar = i % 256; - // memset(this->mMap + i*this->mMapWidth, theFillChar, this->mMapWidth); - // } - // - - // Set colors in image to use palette - memset(this->mPalette, 0, 256*3); - - float theGradient = 0.0f; - - for(int i = 0; i < 256; i++) - { - const int kHitWorldR = 29; - const int kHitWorldG = 59; - const int kHitWorldB = 121; - - const int kBorderR = 144; - const int kBorderG = 159; - const int kBorderB = 189; - - uint8* theColor = this->mPalette + i*3; - - if (i >= kGroundStartPaletteIndex && i <= kGroundEndPaletteIndex) - { - // Ground start to end - - // Set color according to height, blending to hit world color - theGradient = (float)(i - kGroundStartPaletteIndex)/(kGroundEndPaletteIndex - kGroundStartPaletteIndex); - theColor[0] = (int)(theGradient*kHitWorldR); - theColor[1] = (int)(theGradient*kHitWorldG); - theColor[2] = (int)(theGradient*kHitWorldB); - - } - else if (i >= kBorderPaletteIndexStart && i <= kBorderPaletteIndexEnd) - { - - theGradient = (float)(i - kBorderPaletteIndexStart)/(kBorderPaletteIndexEnd - kBorderPaletteIndexStart); - theColor[0] = (int)(theGradient*kBorderR); - theColor[1] = (int)(theGradient*kBorderG); - theColor[2] = (int)(theGradient*kBorderB); - - } - else - { - - switch(i) - { - // On map but inaccessible - case kHitNothingPaletteIndex: - theColor[0] = 255; - theColor[1] = 0; - theColor[2] = 0; - break; - - case kHitWorldPaletteIndex: - theColor[0] = kHitWorldR; - theColor[1] = kHitWorldG; - theColor[2] = kHitWorldB; - break; - - } - } - } -} - -int AvHMiniMap::ReceiveFromNetworkStream(void* const buffer, const int size) -{ - bool finished; - - NetMsg_BuildMiniMap( buffer, size, - this->mMapName, - this->mNumSamplesToProcess, - this->mNumSamplesProcessed, - this->mMapWidth, - this->mMapHeight, - &this->mMap, - finished - ); - - this->mIsProcessing = !finished; - - return 1; -} - -bool AvHMiniMap::WriteMapToSprite() -{ - bool theSuccess = false; - - if(this->GetIsProcessing()) - { - // Open file - // puzl: 1064 - // We always want to use the normal filename when generating a minimap - string theSpriteFileName = string(getModDirectory()) + string("/") + GetSpriteNameFromMap(0, this->mMapName, 0); - // :puzl - FILE* theFile = fopen(theSpriteFileName.c_str(), "wb"); - if(theFile) - { - // Clear sprite data to transparent - memset(this->mSpriteData, 0, kSpriteDataPixels); - - // Copy data - memcpy(this->mSpriteData, this->mMap, kSpriteWidth*kSpriteHeight); - - int theNumFrames = 1; - this->WriteMapToSprite(theFile); - - fclose(theFile); - - theSuccess = true; - } - } - - return theSuccess; -} - -void AvHMiniMap::WriteMapToSprite(FILE* inFileHandle) -{ - - // Compute the number for frames based on the size of the sprite. - - const int spriteWidth = 256; - const int spriteHeight = 256; - - int numXFrames = mMapWidth / spriteWidth; - int numYFrames = mMapHeight / spriteHeight; - - // The extra frame is the commander mode version of the map. - int numFrames = numXFrames * numYFrames + 1; - - // - // write out the sprite header - // - dsprite_t spritetemp; - spritetemp.type = SPR_SINGLE; - spritetemp.texFormat = SPR_ALPHTEST; - spritetemp.boundingradius = sqrt((float)kSpriteWidth*kSpriteWidth); - spritetemp.width = spriteWidth; - spritetemp.height = spriteHeight; - spritetemp.numframes = numFrames; - spritetemp.beamlength = 0;// LittleFloat (this->sprite.beamlength); - spritetemp.synctype = ST_SYNC; - spritetemp.version = SPRITE_VERSION; - spritetemp.ident = IDSPRITEHEADER; - - SafeWrite(inFileHandle, &spritetemp, sizeof(spritetemp)); - - short cnt = 256; - SafeWrite(inFileHandle, (void *) &cnt, sizeof(cnt)); - SafeWrite(inFileHandle, this->mPalette, cnt*3); - - for (int y = 0; y < numYFrames; ++y) - { - for (int x = 0; x < numXFrames; ++x) - { - - spriteframetype_t theType = SPR_SINGLE; - SafeWrite ( inFileHandle, &theType, sizeof(theType)); - - dspriteframe_t frametemp; - - frametemp.origin[0] = 0; - frametemp.origin[1] = 0; - frametemp.width = spriteWidth; - frametemp.height = spriteHeight; - - SafeWrite (inFileHandle, &frametemp, sizeof (frametemp)); - - for (int i = 0; i < spriteHeight; ++i) - { - SafeWrite (inFileHandle, mSpriteData + (y * spriteHeight + i) * mMapWidth + x * spriteWidth, spriteWidth); - } - - } - } - - spriteframetype_t theType = SPR_SINGLE; - SafeWrite ( inFileHandle, &theType, sizeof(theType)); - - dspriteframe_t frametemp; - - frametemp.origin[0] = 0; - frametemp.origin[1] = 0; - frametemp.width = kSpriteWidth / 2; - frametemp.height = kSpriteHeight / 2; - - SafeWrite (inFileHandle, &frametemp, sizeof (frametemp)); - SafeWrite (inFileHandle, mCommanderSpriteData, kSpriteDataPixels / 4); - - -} - -bool AvHMiniMap::WriteSpritesIfJustFinished() -{ - bool theSuccess = false; - - // test - char test[255]; - sprintf(test, "this->GetIsProcessing() = %d, this->mNumSamplesProcessed = %d, this->mNumSamplesToProcess = %d\n", this->GetIsProcessing(), this->mNumSamplesProcessed, this->mNumSamplesToProcess); - gEngfuncs.pfnConsolePrint(test); - - // :test - - - if(this->GetIsProcessing() && (this->mNumSamplesProcessed == this->mNumSamplesToProcess)) - { - this->mIsProcessing = false; - - this->InitializePalette(); - - // Create the commander mode version of the sprite. - - for (int x = 0; x < kSpriteWidth / 2; ++x) - { - for (int y = 0; y < kSpriteHeight / 2; ++y) - { - mCommanderSpriteData[x + y * (kSpriteWidth / 2)] = - mMap[(x * 2) + (y * 2) * kSpriteWidth]; - } - } - - this->DrawEdges(mMap, kSpriteWidth, kSpriteHeight); - this->DrawEdges(mCommanderSpriteData, kSpriteWidth / 2, kSpriteHeight / 2); - - if(this->WriteMapToSprite()) - { - theSuccess = true; - } - - this->mNumSamplesProcessed = this->mNumSamplesToProcess = 0; - this->mIsProcessing = false; - } - - // For each resolution - return theSuccess; -} - -void AvHMiniMap::DrawEdges(uint8* inMap, int width, int height) -{ - - const int numPixels = width * height; - uint8* newMap = new uint8[numPixels]; - - memset(newMap, kHitNothingPaletteIndex, numPixels); - - for (int y = 1; y < width - 1; ++y) - { - for (int x = 1; x < height - 1; ++x) - { - - int baseIndex = x + y * width; - int color = inMap[baseIndex]; - - if (color == kHitNothingPaletteIndex) - { - - int count = 0; - - if (inMap[(x-1) + (y-1)*width] != kHitNothingPaletteIndex) ++count; - if (inMap[(x+0) + (y-1)*width] != kHitNothingPaletteIndex) ++count; - if (inMap[(x+1) + (y-1)*width] != kHitNothingPaletteIndex) ++count; - - if (inMap[(x-1) + (y+0)*width] != kHitNothingPaletteIndex) ++count; - if (inMap[(x+1) + (y+0)*width] != kHitNothingPaletteIndex) ++count; - - if (inMap[(x-1) + (y+1)*width] != kHitNothingPaletteIndex) ++count; - if (inMap[(x+0) + (y+1)*width] != kHitNothingPaletteIndex) ++count; - if (inMap[(x+1) + (y+1)*width] != kHitNothingPaletteIndex) ++count; - - - if (count > 0) - { - float i = pow((count / 8.0f), 0.5f); - //color = i * (kBorderPaletteIndexEnd - kBorderPaletteIndexStart) + kBorderPaletteIndexStart; - color = kBorderPaletteIndexEnd; - } - - - /* - if (mMap[(x-1) + (y-1)*mMapWidth] != kHitNothingPaletteIndex || - mMap[(x+0) + (y-1)*mMapWidth] != kHitNothingPaletteIndex || - mMap[(x+1) + (y-1)*mMapWidth] != kHitNothingPaletteIndex || - mMap[(x-1) + (y+0)*mMapWidth] != kHitNothingPaletteIndex || - mMap[(x+1) + (y+0)*mMapWidth] != kHitNothingPaletteIndex || - mMap[(x-1) + (y+1)*mMapWidth] != kHitNothingPaletteIndex || - mMap[(x+0) + (y+1)*mMapWidth] != kHitNothingPaletteIndex || - mMap[(x+1) + (y+1)*mMapWidth] != kHitNothingPaletteIndex) - { - color = kBorderPaletteIndex; - } - */ - - } - - newMap[baseIndex] = color; - - } - } - - memcpy(inMap, newMap, numPixels); - delete [] newMap; - -} - -#endif - +#include "mod/AvHMiniMap.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHSpecials.h" +#include "mod/AvHNetworkMessages.h" + +#ifdef AVH_CLIENT + #include "util/hl/spritegn.h" +#endif + +#ifdef AVH_SERVER + #include "mod/AvHPlayer.h" +#endif + +const int kMaxScreenWidth = 1600; +const int kMaxScreenHeight = 1200; + +const int kHitWorldPaletteIndex = 0; +const int kBorderPaletteIndexStart = 1; +const int kBorderPaletteIndexEnd = 2; +const int kGroundStartPaletteIndex = 3; +const int kGroundEndPaletteIndex = 254; +const int kHitNothingPaletteIndex = 255; + +// Network message: +// 0: means start processing, pass map name then num samples to process, map width, map height +// 1: means update, pass num pixels, then data +// 2: means end processing + +void SafeWrite (FILE *f, void *buffer, int count) +{ + if (fwrite (buffer, 1, count, f) != (size_t)count) + { + ASSERT(false); + } +} + +// Init class members +AvHMiniMap::AvHMiniMap() +{ + this->mMap = NULL; + + this->mIsProcessing = false; + this->mNumSamplesProcessed = 0; + this->mNumSamplesToProcess = 0; + + this->mMinX = this->mMinY = this->mMaxX = this->mMaxY = 0; + this->mMinViewHeight = this->mMaxViewHeight = 0; + + #ifdef AVH_SERVER + this->mPlayer = NULL; + #endif +} + +AvHMiniMap::~AvHMiniMap() +{ + delete [] this->mMap; + this->mMap = NULL; +} + + +bool AvHMiniMap::GetIsProcessing(float* outPercentageDone) const +{ + bool theIsProcessing = false; + + if(this->mIsProcessing || (this->mNumSamplesProcessed == this->mNumSamplesToProcess && this->mNumSamplesProcessed > 0)) + { + if(outPercentageDone) + { + *outPercentageDone = (float)this->mNumSamplesProcessed/this->mNumSamplesToProcess; + } + theIsProcessing = true; + } + + return theIsProcessing; +} + + + + +#ifdef AVH_SERVER +void AvHMiniMap::BuildMiniMap(const char* inMapName, AvHPlayer* inPlayer, const AvHMapExtents& inMapExtents) +{ + const int kNumGroundFloorColors = 249; + + this->mMapName = inMapName; + this->mPlayer = inPlayer; + + // In case BuildMiniMap is called multiple times + delete [] this->mMap; + + // New a hi-res version of the map (enough for the 1600 version) + this->mMapWidth = kSpriteWidth; + this->mMapHeight = kSpriteWidth; + + this->mNumSamplesToProcess = this->mMapWidth*this->mMapHeight; + + this->mMap = new uint8[this->mNumSamplesToProcess]; + this->mNumSamplesProcessed = 0; + + this->mMinX = inMapExtents.GetMinMapX();//inMinX; + this->mMinY = inMapExtents.GetMinMapY();//inMinY; + this->mMaxX = inMapExtents.GetMaxMapX();//inMaxX; + this->mMaxY = inMapExtents.GetMaxMapY();//inMaxY; + + this->mMinViewHeight = inMapExtents.GetMinViewHeight();//inMinViewHeight; + this->mMaxViewHeight = inMapExtents.GetMaxViewHeight();//inMaxViewHeight; + + this->mIsProcessing = true; + + // Tell player to rebuild minimap + NetMsg_BuildMiniMap_Initialize( this->mPlayer->pev, this->mMapName, this->mNumSamplesToProcess, this->mMapWidth, this->mMapHeight ); +} + +bool AvHMiniMap::Process() +{ + bool theProcessingComplete = false; + + if(this->GetIsProcessing()) + { + // Process x pixels + // If we've calculated them all, return true + + // positive y on component is down, but that means negative y in world + float theDiffY = this->mMaxY - this->mMinY; + + // left to right + float theDiffX = this->mMaxX - this->mMinX; + + // Preserve map aspect ratio + float theMapAspectRatio = (this->mMaxX - this->mMinX)/(this->mMaxY - this->mMinY); + + float theXScale, theYScale; + if(theMapAspectRatio > 1.0f) + { + theXScale = 1.0f; + theYScale = 1.0f/theMapAspectRatio; + } + else + { + theXScale = 1.0f/theMapAspectRatio; + theYScale = 1.0f; + } + + float theMapCenterX = (this->mMinX + this->mMaxX)/2.0f; + float theMapCenterY = (this->mMinY + this->mMaxY)/2.0f; + + const int kNumPixelsPerCall = 50; + uint8 theSampleArray[kNumPixelsPerCall]; + memset(theSampleArray, 0, kNumPixelsPerCall); + + int i=0; + for(i = 0; (i < kNumPixelsPerCall) && (this->mNumSamplesProcessed < this->mNumSamplesToProcess); i++) + { + int theSampleIndex = this->mNumSamplesProcessed; + int theX = theSampleIndex % this->mMapWidth; + int theY = theSampleIndex/this->mMapWidth; + + // Initialize the value to outside the map + int theValue = kHitNothingPaletteIndex; + + // Account for map center and aspect ratio + float theXComponent = (theX/(float)this->mMapWidth) - .5f; + float theYComponent = (theY/(float)this->mMapHeight) - .5f; + float theCurrentX = theMapCenterX + theXComponent*theDiffX*theXScale; + float theCurrentY = theMapCenterY - theYComponent*theDiffY*theYScale; + + // If the point is inside our map boundaries, do the trace + if((theCurrentX >= this->mMinX) && (theCurrentX <= this->mMaxX) && (theCurrentY >= this->mMinY) && (theCurrentY <= this->mMaxY)) + { + // If we hit nothing, draw with the off map index + theValue = kHitNothingPaletteIndex; + int theUserThree = 0; + float theHitHeight; + float theHeightGradient = 0.0f; + + if(AvHSHUTraceVerticalTangible(theCurrentX, theCurrentY, this->mMaxViewHeight, theUserThree, theHitHeight)) + { + // TODO: Modify trace to return world brushes that are hit + // Set color to "world brush hit", it will be changed if an entity was hit + theValue = kHitWorldPaletteIndex; + + + theHitHeight = min(mMaxViewHeight, max(theHitHeight, mMinViewHeight)); + theHeightGradient = 1.0f - (this->mMaxViewHeight - theHitHeight)/(this->mMaxViewHeight - this->mMinViewHeight); + theValue = kGroundStartPaletteIndex + (kGroundEndPaletteIndex - kGroundStartPaletteIndex)*theHeightGradient; + + } + } + + int theIndex = theX + theY*this->mMapWidth; + ASSERT(theIndex < this->mNumSamplesToProcess); + this->mMap[theIndex] = theValue; + + theSampleArray[i] = theValue; + + this->mNumSamplesProcessed++; + } + + // This could be less than kNumPixelsPerCall if it's the last time through + int theNumSamples = i; + + // Tell player to rebuild minimap + NetMsg_BuildMiniMap_Update( this->mPlayer->pev, theNumSamples, theSampleArray ); + + if(this->mNumSamplesProcessed == this->mNumSamplesToProcess) + { + theProcessingComplete = true; + this->mIsProcessing = false; + + NetMsg_BuildMiniMap_Complete( this->mPlayer->pev ); + } + } + + return theProcessingComplete; +} + +#endif + + +#ifdef AVH_CLIENT +string AvHMiniMap::GetSpriteNameFromMap(int inSpriteWidth, const string& inMapName, int useLabels) +{ + char theWidthString[128]; + sprintf(theWidthString, "%d", inSpriteWidth); + // : 1064 + // insert _labelled into the filename before ".spr" + string extraname=""; + switch ( useLabels ) { + case 1: + extraname="_1"; + break; + case 2: + extraname="_2"; + break; + case 3: + extraname="_3"; + break; + default: + break; + } + string theMiniMapName = kMiniMapSpritesDirectory + string("/") /*+ string(theWidthString)*/ + inMapName + extraname + string(".spr"); + // : + return theMiniMapName; +} + +void AvHMiniMap::InitializePalette() +{ + // // Test data + // memset(this->mMap, kTransparentPaletteIndex, theNumSamples); + // for(int i = 0; i < this->mMapHeight; i++) + // { + // char theFillChar = i % 256; + // memset(this->mMap + i*this->mMapWidth, theFillChar, this->mMapWidth); + // } + // + + // Set colors in image to use palette + memset(this->mPalette, 0, 256*3); + + float theGradient = 0.0f; + + for(int i = 0; i < 256; i++) + { + const int kHitWorldR = 29; + const int kHitWorldG = 59; + const int kHitWorldB = 121; + + const int kBorderR = 144; + const int kBorderG = 159; + const int kBorderB = 189; + + uint8* theColor = this->mPalette + i*3; + + if (i >= kGroundStartPaletteIndex && i <= kGroundEndPaletteIndex) + { + // Ground start to end + + // Set color according to height, blending to hit world color + theGradient = (float)(i - kGroundStartPaletteIndex)/(kGroundEndPaletteIndex - kGroundStartPaletteIndex); + theColor[0] = (int)(theGradient*kHitWorldR); + theColor[1] = (int)(theGradient*kHitWorldG); + theColor[2] = (int)(theGradient*kHitWorldB); + + } + else if (i >= kBorderPaletteIndexStart && i <= kBorderPaletteIndexEnd) + { + + theGradient = (float)(i - kBorderPaletteIndexStart)/(kBorderPaletteIndexEnd - kBorderPaletteIndexStart); + theColor[0] = (int)(theGradient*kBorderR); + theColor[1] = (int)(theGradient*kBorderG); + theColor[2] = (int)(theGradient*kBorderB); + + } + else + { + + switch(i) + { + // On map but inaccessible + case kHitNothingPaletteIndex: + theColor[0] = 255; + theColor[1] = 0; + theColor[2] = 0; + break; + + case kHitWorldPaletteIndex: + theColor[0] = kHitWorldR; + theColor[1] = kHitWorldG; + theColor[2] = kHitWorldB; + break; + + } + } + } +} + +int AvHMiniMap::ReceiveFromNetworkStream(void* const buffer, const int size) +{ + bool finished; + + NetMsg_BuildMiniMap( buffer, size, + this->mMapName, + this->mNumSamplesToProcess, + this->mNumSamplesProcessed, + this->mMapWidth, + this->mMapHeight, + &this->mMap, + finished + ); + + this->mIsProcessing = !finished; + + return 1; +} + +bool AvHMiniMap::WriteMapToSprite() +{ + bool theSuccess = false; + + if(this->GetIsProcessing()) + { + // Open file + // : 1064 + // We always want to use the normal filename when generating a minimap + string theSpriteFileName = string(getModDirectory()) + string("/") + GetSpriteNameFromMap(0, this->mMapName, 0); + // : + FILE* theFile = fopen(theSpriteFileName.c_str(), "wb"); + if(theFile) + { + // Clear sprite data to transparent + memset(this->mSpriteData, 0, kSpriteDataPixels); + + // Copy data + memcpy(this->mSpriteData, this->mMap, kSpriteWidth*kSpriteHeight); + + int theNumFrames = 1; + this->WriteMapToSprite(theFile); + + fclose(theFile); + + theSuccess = true; + } + } + + return theSuccess; +} + +void AvHMiniMap::WriteMapToSprite(FILE* inFileHandle) +{ + + // Compute the number for frames based on the size of the sprite. + + const int spriteWidth = 256; + const int spriteHeight = 256; + + int numXFrames = mMapWidth / spriteWidth; + int numYFrames = mMapHeight / spriteHeight; + + // The extra frame is the commander mode version of the map. + int numFrames = numXFrames * numYFrames + 1; + + // + // write out the sprite header + // + dsprite_t spritetemp; + spritetemp.type = SPR_SINGLE; + spritetemp.texFormat = SPR_ALPHTEST; + spritetemp.boundingradius = sqrt((float)kSpriteWidth*kSpriteWidth); + spritetemp.width = spriteWidth; + spritetemp.height = spriteHeight; + spritetemp.numframes = numFrames; + spritetemp.beamlength = 0;// LittleFloat (this->sprite.beamlength); + spritetemp.synctype = ST_SYNC; + spritetemp.version = SPRITE_VERSION; + spritetemp.ident = IDSPRITEHEADER; + + SafeWrite(inFileHandle, &spritetemp, sizeof(spritetemp)); + + short cnt = 256; + SafeWrite(inFileHandle, (void *) &cnt, sizeof(cnt)); + SafeWrite(inFileHandle, this->mPalette, cnt*3); + + for (int y = 0; y < numYFrames; ++y) + { + for (int x = 0; x < numXFrames; ++x) + { + + spriteframetype_t theType = SPR_SINGLE; + SafeWrite ( inFileHandle, &theType, sizeof(theType)); + + dspriteframe_t frametemp; + + frametemp.origin[0] = 0; + frametemp.origin[1] = 0; + frametemp.width = spriteWidth; + frametemp.height = spriteHeight; + + SafeWrite (inFileHandle, &frametemp, sizeof (frametemp)); + + for (int i = 0; i < spriteHeight; ++i) + { + SafeWrite (inFileHandle, mSpriteData + (y * spriteHeight + i) * mMapWidth + x * spriteWidth, spriteWidth); + } + + } + } + + spriteframetype_t theType = SPR_SINGLE; + SafeWrite ( inFileHandle, &theType, sizeof(theType)); + + dspriteframe_t frametemp; + + frametemp.origin[0] = 0; + frametemp.origin[1] = 0; + frametemp.width = kSpriteWidth / 2; + frametemp.height = kSpriteHeight / 2; + + SafeWrite (inFileHandle, &frametemp, sizeof (frametemp)); + SafeWrite (inFileHandle, mCommanderSpriteData, kSpriteDataPixels / 4); + + +} + +bool AvHMiniMap::WriteSpritesIfJustFinished() +{ + bool theSuccess = false; + + if(this->GetIsProcessing() && (this->mNumSamplesProcessed == this->mNumSamplesToProcess)) + { + this->mIsProcessing = false; + + this->InitializePalette(); + + // Create the commander mode version of the sprite. + + for (int x = 0; x < kSpriteWidth / 2; ++x) + { + for (int y = 0; y < kSpriteHeight / 2; ++y) + { + mCommanderSpriteData[x + y * (kSpriteWidth / 2)] = + mMap[(x * 2) + (y * 2) * kSpriteWidth]; + } + } + + this->DrawEdges(mMap, kSpriteWidth, kSpriteHeight); + this->DrawEdges(mCommanderSpriteData, kSpriteWidth / 2, kSpriteHeight / 2); + + if(this->WriteMapToSprite()) + { + theSuccess = true; + } + + this->mNumSamplesProcessed = this->mNumSamplesToProcess = 0; + this->mIsProcessing = false; + } + + // For each resolution + return theSuccess; +} + +void AvHMiniMap::DrawEdges(uint8* inMap, int width, int height) +{ + + const int numPixels = width * height; + uint8* newMap = new uint8[numPixels]; + + memset(newMap, kHitNothingPaletteIndex, numPixels); + + for (int y = 1; y < width - 1; ++y) + { + for (int x = 1; x < height - 1; ++x) + { + + int baseIndex = x + y * width; + int color = inMap[baseIndex]; + + if (color == kHitNothingPaletteIndex) + { + + int count = 0; + + if (inMap[(x-1) + (y-1)*width] != kHitNothingPaletteIndex) ++count; + if (inMap[(x+0) + (y-1)*width] != kHitNothingPaletteIndex) ++count; + if (inMap[(x+1) + (y-1)*width] != kHitNothingPaletteIndex) ++count; + + if (inMap[(x-1) + (y+0)*width] != kHitNothingPaletteIndex) ++count; + if (inMap[(x+1) + (y+0)*width] != kHitNothingPaletteIndex) ++count; + + if (inMap[(x-1) + (y+1)*width] != kHitNothingPaletteIndex) ++count; + if (inMap[(x+0) + (y+1)*width] != kHitNothingPaletteIndex) ++count; + if (inMap[(x+1) + (y+1)*width] != kHitNothingPaletteIndex) ++count; + + + if (count > 0) + { + float i = pow((count / 8.0f), 0.5f); + //color = i * (kBorderPaletteIndexEnd - kBorderPaletteIndexStart) + kBorderPaletteIndexStart; + color = kBorderPaletteIndexEnd; + } + + + /* + if (mMap[(x-1) + (y-1)*mMapWidth] != kHitNothingPaletteIndex || + mMap[(x+0) + (y-1)*mMapWidth] != kHitNothingPaletteIndex || + mMap[(x+1) + (y-1)*mMapWidth] != kHitNothingPaletteIndex || + mMap[(x-1) + (y+0)*mMapWidth] != kHitNothingPaletteIndex || + mMap[(x+1) + (y+0)*mMapWidth] != kHitNothingPaletteIndex || + mMap[(x-1) + (y+1)*mMapWidth] != kHitNothingPaletteIndex || + mMap[(x+0) + (y+1)*mMapWidth] != kHitNothingPaletteIndex || + mMap[(x+1) + (y+1)*mMapWidth] != kHitNothingPaletteIndex) + { + color = kBorderPaletteIndex; + } + */ + + } + + newMap[baseIndex] = color; + + } + } + + memcpy(inMap, newMap, numPixels); + delete [] newMap; + +} + +#endif + diff --git a/main/source/mod/AvHMiniMap.h b/main/source/mod/AvHMiniMap.h index ee500ab..f86de5d 100644 --- a/main/source/mod/AvHMiniMap.h +++ b/main/source/mod/AvHMiniMap.h @@ -1,91 +1,91 @@ -#ifndef AVH_MINIMAP_H -#define AVH_MINIMAP_H - -#include "types.h" - -#ifdef AVH_CLIENT -#include "cl_dll/hud.h" -#endif - -#include "mod/AvHMapExtents.h" - -const int kSpriteWidth = 512; -const int kSpriteHeight = 512; -const int kSpriteDataPixels = kSpriteWidth*kSpriteHeight; - -class AvHPlayer; - -class AvHMiniMap -{ -public: - AvHMiniMap(); - virtual ~AvHMiniMap(); - - bool GetIsProcessing(float* outPercentageDone = NULL) const; - - #ifdef AVH_SERVER - void BuildMiniMap(const char* inMapName, AvHPlayer* inPlayer, const AvHMapExtents& inMapExtents); - bool Process(); - #endif - - #ifdef AVH_CLIENT - // puzl: 1064 - // Allow the caller to specify the use of the labelled minimap - static string GetSpriteNameFromMap(int inSpriteWidth, const string& inMapName, int useLabels); - int ReceiveFromNetworkStream(void* const buffer, const int size); - bool WriteSpritesIfJustFinished(); - #endif - - -private: - #ifdef AVH_CLIENT - void DrawEdges(uint8* inMap, int width, int height); - void InitializePalette(); - bool WriteMapToSprite(); - void WriteMapToSprite(FILE* inFileHandle); - #endif - - uint8* mMap; - int mMapWidth; - int mMapHeight; - - #ifdef AVH_SERVER - AvHPlayer* mPlayer; - #endif - - #ifdef AVH_CLIENT - uint8 mPalette[256*3]; - uint8 mSpriteData[kSpriteDataPixels]; - uint8 mCommanderSpriteData[kSpriteDataPixels / 4]; - #endif - - float mMinX; - float mMinY; - float mMaxX; - float mMaxY; - float mMinViewHeight; - float mMaxViewHeight; - - int mNumSamplesToProcess; - int mNumSamplesProcessed; - bool mIsProcessing; - - string mMapName; - - #ifdef AVH_CLIENT - // Sprite stuff - byte* byteimage; - byte* lbmpalette; - int byteimagewidth; - int byteimageheight; - byte* lumpbuffer; - byte* plump; - char spritedir[1024]; - char spriteoutname[1024]; - int framesmaxs[2]; - int framecount; - #endif - -}; - +#ifndef AVH_MINIMAP_H +#define AVH_MINIMAP_H + +#include "types.h" + +#ifdef AVH_CLIENT +#include "cl_dll/hud.h" +#endif + +#include "mod/AvHMapExtents.h" + +const int kSpriteWidth = 512; +const int kSpriteHeight = 512; +const int kSpriteDataPixels = kSpriteWidth*kSpriteHeight; + +class AvHPlayer; + +class AvHMiniMap +{ +public: + AvHMiniMap(); + virtual ~AvHMiniMap(); + + bool GetIsProcessing(float* outPercentageDone = NULL) const; + + #ifdef AVH_SERVER + void BuildMiniMap(const char* inMapName, AvHPlayer* inPlayer, const AvHMapExtents& inMapExtents); + bool Process(); + #endif + + #ifdef AVH_CLIENT + // : 1064 + // Allow the caller to specify the use of the labelled minimap + static string GetSpriteNameFromMap(int inSpriteWidth, const string& inMapName, int useLabels); + int ReceiveFromNetworkStream(void* const buffer, const int size); + bool WriteSpritesIfJustFinished(); + #endif + + +private: + #ifdef AVH_CLIENT + void DrawEdges(uint8* inMap, int width, int height); + void InitializePalette(); + bool WriteMapToSprite(); + void WriteMapToSprite(FILE* inFileHandle); + #endif + + uint8* mMap; + int mMapWidth; + int mMapHeight; + + #ifdef AVH_SERVER + AvHPlayer* mPlayer; + #endif + + #ifdef AVH_CLIENT + uint8 mPalette[256*3]; + uint8 mSpriteData[kSpriteDataPixels]; + uint8 mCommanderSpriteData[kSpriteDataPixels / 4]; + #endif + + float mMinX; + float mMinY; + float mMaxX; + float mMaxY; + float mMinViewHeight; + float mMaxViewHeight; + + int mNumSamplesToProcess; + int mNumSamplesProcessed; + bool mIsProcessing; + + string mMapName; + + #ifdef AVH_CLIENT + // Sprite stuff + byte* byteimage; + byte* lbmpalette; + int byteimagewidth; + int byteimageheight; + byte* lumpbuffer; + byte* plump; + char spritedir[1024]; + char spriteoutname[1024]; + int framesmaxs[2]; + int framecount; + #endif + +}; + #endif \ No newline at end of file diff --git a/main/source/mod/AvHMovementUtil.cpp b/main/source/mod/AvHMovementUtil.cpp index 0aa4876..b831072 100644 --- a/main/source/mod/AvHMovementUtil.cpp +++ b/main/source/mod/AvHMovementUtil.cpp @@ -1,364 +1,373 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHMovementUtil.cpp $ -// $Date: 2002/10/24 21:34:02 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHMovementUtil.cpp,v $ -// Revision 1.20 2002/10/24 21:34:02 Flayra -// - Less rockets -// -// Revision 1.19 2002/10/04 18:04:27 Flayra -// - Fixed floating gestation sacs -// - Aliens now fall all the way to ground during countdown (instead of floating and shaking) -// -// Revision 1.18 2002/10/03 18:59:04 Flayra -// - Refactored energy -// -// Revision 1.17 2002/09/09 20:00:10 Flayra -// - Balance changes -// -// Revision 1.16 2002/08/16 02:40:14 Flayra -// - Regular balance update -// -// Revision 1.15 2002/08/09 01:08:30 Flayra -// - Regular update -// -// Revision 1.14 2002/08/02 21:53:47 Flayra -// - Various energy tweaks, so melee attacks don't run out when attacking buildings as often, and so fliers can fly and bite/spike -// -// Revision 1.13 2002/07/23 17:14:45 Flayra -// - Energy updates -// -// Revision 1.12 2002/07/08 17:12:05 Flayra -// - Regular update -// -// Revision 1.11 2002/07/01 21:38:46 Flayra -// - Primal scream gives energy back faster, added energy usage for new weapons -// -// Revision 1.10 2002/06/25 18:08:40 Flayra -// - Energy costs tweaking, added new weapons, added charging -// -// Revision 1.9 2002/06/03 16:52:36 Flayra -// - Tweaked spike energy cost -// -// Revision 1.8 2002/05/28 17:54:45 Flayra -// - Tweaked costs for swipe and web -// -// Revision 1.7 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHMovementUtil.h" -#include "mod/AvHSpecials.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHHulls.h" -#include "mod/AvHConstants.h" -#include "util/Balance.h" -#include "types.h" - -int AvHMUGetHull(bool inIsDucking, int inUserVar) -{ - int theHull = 0; - - // For marines and level 4 - theHull = inIsDucking ? kHLCrouchHullIndex : kHLStandHullIndex; - - // Set the hull for our special sized players - if(inUserVar == AVH_USER3_ALIEN_PLAYER1) - { - // Always use small hull, even when ducking - theHull = kHLCrouchHullIndex; - } - else if(inUserVar == AVH_USER3_ALIEN_PLAYER2) - { - theHull = kHLCrouchHullIndex; - } - else if(inUserVar == AVH_USER3_ALIEN_PLAYER3) - { - theHull = kHLCrouchHullIndex; - } - else if(inUserVar == AVH_USER3_ALIEN_EMBRYO) - { - theHull = kHLCrouchHullIndex; - } - else if(inUserVar == AVH_USER3_ALIEN_PLAYER5) - { - // Use human hull when ducking, largest otherwise - theHull = inIsDucking ? kHLStandHullIndex : kNSHugeHullIndex; - } - - return theHull; -} - -int AvHMUGetOriginOffsetForUser3(AvHUser3 inUser3) -{ - int theOffset = 20; - - if(inUser3 == AVH_USER3_ALIEN_PLAYER5) - { - theOffset = 40; - } - - return theOffset; -} - -int AvHMUGetOriginOffsetForMessageID(AvHMessageID inMessageID) -{ - AvHUser3 theUser3 = AVH_USER3_NONE; - - if(inMessageID == ALIEN_LIFEFORM_FIVE) - { - theUser3 = AVH_USER3_ALIEN_PLAYER5; - } - - return AvHMUGetOriginOffsetForUser3(theUser3); -} - -bool AvHMUGetCanDuck(int inUser3) -{ - bool theCanDuck = true; - - if((inUser3 == AVH_USER3_ALIEN_PLAYER1) || (inUser3 == AVH_USER3_ALIEN_PLAYER2) || (inUser3 == AVH_USER3_ALIEN_PLAYER3) || (inUser3 == AVH_USER3_ALIEN_EMBRYO) ) - { - theCanDuck = false; - } - - return theCanDuck; -} - -bool AvHMUDeductAlienEnergy(float& ioFuser, float inNormAmount) -{ - bool theSuccess = false; - - if(AvHMUHasEnoughAlienEnergy(ioFuser, inNormAmount)) - { - float theCurrentEnergy = ioFuser/kNormalizationNetworkFactor; - - theCurrentEnergy -= inNormAmount; - theCurrentEnergy = max(theCurrentEnergy, 0.0f); - ioFuser = theCurrentEnergy*kNormalizationNetworkFactor; - - theSuccess = true; - } - - return theSuccess; -} - -bool AvHMUGiveAlienEnergy(float& ioFuser, float inNormAmount) -{ - bool theSuccess = false; - - float theCurrentEnergy = ioFuser/kNormalizationNetworkFactor; - - if(theCurrentEnergy < 1.0f) - { - theCurrentEnergy += inNormAmount; - theCurrentEnergy = min(max(theCurrentEnergy, 0.0f), 1.0f); - ioFuser = theCurrentEnergy*kNormalizationNetworkFactor; - theSuccess = true; - } - - return theSuccess; -} - -bool AvHMUGetEnergyCost(AvHWeaponID inWeaponID, float& outEnergyCost) -{ - bool theSuccess = false; - - float theCost = 0.0f; - switch(inWeaponID) - { - case AVH_WEAPON_CLAWS: - theCost = (float)BALANCE_VAR(kClawsEnergyCost); - break; - case AVH_WEAPON_SPIT: - theCost = (float)BALANCE_VAR(kSpitEnergyCost); - break; - case AVH_WEAPON_SPORES: - theCost = (float)BALANCE_VAR(kSporesEnergyCost); - break; - case AVH_WEAPON_SPIKE: - theCost = (float)BALANCE_VAR(kSpikeEnergyCost); - break; - case AVH_WEAPON_BITE: - theCost = (float)BALANCE_VAR(kBiteEnergyCost); - break; - case AVH_WEAPON_BITE2: - theCost = (float)BALANCE_VAR(kBite2EnergyCost); - break; - case AVH_WEAPON_SWIPE: - theCost = (float)BALANCE_VAR(kSwipeEnergyCost); - break; - case AVH_WEAPON_BLINK: - theCost = (float)BALANCE_VAR(kBlinkEnergyCost); - break; - case AVH_WEAPON_WEBSPINNER: - theCost = (float)BALANCE_VAR(kWebEnergyCost); - break; - case AVH_WEAPON_PARASITE: - theCost = (float)BALANCE_VAR(kParasiteEnergyCost); - break; - case AVH_WEAPON_DIVINEWIND: - theCost = (float)BALANCE_VAR(kDivineWindEnergyCost); - break; - case AVH_WEAPON_HEALINGSPRAY: - theCost = (float)BALANCE_VAR(kHealingSprayEnergyCost); - break; - case AVH_WEAPON_METABOLIZE: - theCost = (float)BALANCE_VAR(kMetabolizeEnergyCost); - break; - case AVH_WEAPON_UMBRA: - theCost = (float)BALANCE_VAR(kUmbraEnergyCost); - break; - case AVH_WEAPON_PRIMALSCREAM: - theCost = (float)BALANCE_VAR(kPrimalScreamEnergyCost); - break; - case AVH_WEAPON_BILEBOMB: - theCost = (float)BALANCE_VAR(kBileBombEnergyCost); - break; - case AVH_WEAPON_ACIDROCKET: - theCost = (float)BALANCE_VAR(kAcidRocketEnergyCost); - break; - case AVH_WEAPON_STOMP: - theCost = (float)BALANCE_VAR(kStompEnergyCost); - break; - case AVH_WEAPON_DEVOUR: - theCost = (float)BALANCE_VAR(kDevourEnergyCost); - break; - - // Abilities - case AVH_ABILITY_LEAP: - theCost = (float)BALANCE_VAR(kLeapEnergyCost); - break; - case AVH_ABILITY_CHARGE: - theCost = (float)BALANCE_VAR(kChargeEnergyCost); - break; - - } - - outEnergyCost = theCost; - - if(theCost > 0.0f) - { - theSuccess = true; - } - - return theSuccess; -} - -float AvHMUGetWalkSpeedFactor(AvHUser3 inUser3) -{ - float theMoveSpeed = .1f; - - switch(inUser3) - { - case AVH_USER3_MARINE_PLAYER: - theMoveSpeed = .095f; - break; - case AVH_USER3_ALIEN_PLAYER1: - //theMoveSpeed = .04f; - theMoveSpeed = .14f; - break; - case AVH_USER3_ALIEN_PLAYER2: - theMoveSpeed = .08f; - break; - case AVH_USER3_ALIEN_PLAYER3: - theMoveSpeed = .11f; - break; - case AVH_USER3_ALIEN_PLAYER4: - theMoveSpeed = .09f; - break; - case AVH_USER3_ALIEN_PLAYER5: - theMoveSpeed = .09f; - break; - } - - return theMoveSpeed; -} - -bool AvHMUHasEnoughAlienEnergy(float& ioFuser, float inNormAmount) -{ - bool theSuccess = false; - - float theCurrentEnergy = ioFuser/kNormalizationNetworkFactor; - if(theCurrentEnergy >= inNormAmount) - { - theSuccess = true; - } - - return theSuccess; -} - -void AvHMUUpdateAlienEnergy(float inTimePassed, int inUser3, int inUser4, float& ioFuser) -{ - if( (inUser3 == AVH_USER3_ALIEN_PLAYER1) || - (inUser3 == AVH_USER3_ALIEN_PLAYER2) || - (inUser3 == AVH_USER3_ALIEN_PLAYER3) || - (inUser3 == AVH_USER3_ALIEN_PLAYER4) || - (inUser3 == AVH_USER3_ALIEN_PLAYER5)) - { - if(!GetHasUpgrade(inUser4, MASK_PLAYER_STUNNED)) - { - // Percentage (0-1) per second - float theAlienEnergyRate = (float)BALANCE_VAR(kAlienEnergyRate); - //float kFadeChargingDeplectionRate = -2.8f*kAlienEnergyRate; - float kChargingDepletionRate = -BALANCE_VAR(kChargingEnergyScalar)*theAlienEnergyRate; - - const float kMultiplier = GetHasUpgrade(inUser4, MASK_BUFFED) ? (1.0f + BALANCE_VAR(kPrimalScreamEnergyFactor)) : 1.0f; - float theEnergyRate = theAlienEnergyRate*kMultiplier; - - float theUpgradeFactor = 1.0f; - int theNumLevels = AvHGetAlienUpgradeLevel(inUser4, MASK_UPGRADE_5); - if(theNumLevels > 0) - { - - theUpgradeFactor += theNumLevels*BALANCE_VAR(kAdrenalineEnergyPercentPerLevel); - } - - float theCurrentEnergy = ioFuser/kNormalizationNetworkFactor; - - float theNewEnergy = theCurrentEnergy + inTimePassed*theAlienEnergyRate*theUpgradeFactor; - - // If we're charging, reduce energy - if(GetHasUpgrade(inUser4, MASK_ALIEN_MOVEMENT)) - { - if(inUser3 == AVH_USER3_ALIEN_PLAYER4) - { -// theNewEnergy += inTimePassed*kFadeChargingDeplectionRate; - } - else - { - theNewEnergy += inTimePassed*kChargingDepletionRate; - } - } - - theNewEnergy = min(max(theNewEnergy, 0.0f), 1.0f); - - ioFuser = theNewEnergy*kNormalizationNetworkFactor; - } - } -} - -void AvHMUUpdateJetpackEnergy(bool inIsJetpacking, float theTimePassed, float& ioJetpackEnergy) -{ - if(inIsJetpacking) - { - ioJetpackEnergy -= theTimePassed*kJetpackEnergyLossRate; - } - else - { - ioJetpackEnergy += theTimePassed*kJetpackEnergyGainRate; - } - - ioJetpackEnergy = min(1.0f, ioJetpackEnergy); - ioJetpackEnergy = max(0.0f, ioJetpackEnergy); -} +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHMovementUtil.cpp $ +// $Date: 2002/10/24 21:34:02 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHMovementUtil.cpp,v $ +// Revision 1.20 2002/10/24 21:34:02 Flayra +// - Less rockets +// +// Revision 1.19 2002/10/04 18:04:27 Flayra +// - Fixed floating gestation sacs +// - Aliens now fall all the way to ground during countdown (instead of floating and shaking) +// +// Revision 1.18 2002/10/03 18:59:04 Flayra +// - Refactored energy +// +// Revision 1.17 2002/09/09 20:00:10 Flayra +// - Balance changes +// +// Revision 1.16 2002/08/16 02:40:14 Flayra +// - Regular balance update +// +// Revision 1.15 2002/08/09 01:08:30 Flayra +// - Regular update +// +// Revision 1.14 2002/08/02 21:53:47 Flayra +// - Various energy tweaks, so melee attacks don't run out when attacking buildings as often, and so fliers can fly and bite/spike +// +// Revision 1.13 2002/07/23 17:14:45 Flayra +// - Energy updates +// +// Revision 1.12 2002/07/08 17:12:05 Flayra +// - Regular update +// +// Revision 1.11 2002/07/01 21:38:46 Flayra +// - Primal scream gives energy back faster, added energy usage for new weapons +// +// Revision 1.10 2002/06/25 18:08:40 Flayra +// - Energy costs tweaking, added new weapons, added charging +// +// Revision 1.9 2002/06/03 16:52:36 Flayra +// - Tweaked spike energy cost +// +// Revision 1.8 2002/05/28 17:54:45 Flayra +// - Tweaked costs for swipe and web +// +// Revision 1.7 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHMovementUtil.h" +#include "mod/AvHSpecials.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHHulls.h" +#include "mod/AvHConstants.h" +#include "util/Balance.h" +#include "types.h" + +int AvHMUGetHull(bool inIsDucking, int inUserVar) +{ + int theHull = 0; + + // For marines and level 4 + theHull = inIsDucking ? kHLCrouchHullIndex : kHLStandHullIndex; + + // Set the hull for our special sized players + if(inUserVar == AVH_USER3_ALIEN_PLAYER1) + { + // Always use small hull, even when ducking + theHull = kHLCrouchHullIndex; + } + else if(inUserVar == AVH_USER3_ALIEN_PLAYER2) + { + theHull = kHLCrouchHullIndex; + } + else if(inUserVar == AVH_USER3_ALIEN_PLAYER3) + { + theHull = kHLCrouchHullIndex; + } + else if(inUserVar == AVH_USER3_ALIEN_EMBRYO) + { + theHull = kHLCrouchHullIndex; + } + else if(inUserVar == AVH_USER3_ALIEN_PLAYER5) + { + // Use human hull when ducking, largest otherwise + theHull = inIsDucking ? kHLStandHullIndex : kNSHugeHullIndex; + } + + return theHull; +} + +int AvHMUGetOriginOffsetForUser3(AvHUser3 inUser3) +{ + int theOffset = 20; + + if(inUser3 == AVH_USER3_ALIEN_PLAYER5) + { + theOffset = 40; + } + + return theOffset; +} + +int AvHMUGetOriginOffsetForMessageID(AvHMessageID inMessageID) +{ + AvHUser3 theUser3 = AVH_USER3_NONE; + + if(inMessageID == ALIEN_LIFEFORM_FIVE) + { + theUser3 = AVH_USER3_ALIEN_PLAYER5; + } + + return AvHMUGetOriginOffsetForUser3(theUser3); +} + +bool AvHMUGetCanDuck(int inUser3) +{ + bool theCanDuck = true; + + if((inUser3 == AVH_USER3_ALIEN_PLAYER1) || (inUser3 == AVH_USER3_ALIEN_PLAYER2) || (inUser3 == AVH_USER3_ALIEN_PLAYER3) || (inUser3 == AVH_USER3_ALIEN_EMBRYO) ) + { + theCanDuck = false; + } + + return theCanDuck; +} + +bool AvHMUDeductAlienEnergy(float& ioFuser, float inNormAmount) +{ + bool theSuccess = false; + + if(AvHMUHasEnoughAlienEnergy(ioFuser, inNormAmount)) + { + float theCurrentEnergy = ioFuser/kNormalizationNetworkFactor; + + theCurrentEnergy -= inNormAmount; + theCurrentEnergy = max(theCurrentEnergy, 0.0f); + ioFuser = theCurrentEnergy*kNormalizationNetworkFactor; + + theSuccess = true; + } + + return theSuccess; +} + +bool AvHMUGiveAlienEnergy(float& ioFuser, float inNormAmount) +{ + bool theSuccess = false; + + float theCurrentEnergy = ioFuser/kNormalizationNetworkFactor; + + if(theCurrentEnergy < 1.0f) + { + theCurrentEnergy += inNormAmount; + theCurrentEnergy = min(max(theCurrentEnergy, 0.0f), 1.0f); + ioFuser = theCurrentEnergy*kNormalizationNetworkFactor; + theSuccess = true; + } + + return theSuccess; +} + +bool AvHMUGetEnergyCost(AvHWeaponID inWeaponID, float& outEnergyCost) +{ + bool theSuccess = false; + + float theCost = 0.0f; + switch(inWeaponID) + { + case AVH_WEAPON_CLAWS: + theCost = (float)BALANCE_VAR(kClawsEnergyCost); + break; + case AVH_WEAPON_SPIT: + theCost = (float)BALANCE_VAR(kSpitEnergyCost); + break; + case AVH_WEAPON_SPORES: + theCost = (float)BALANCE_VAR(kSporesEnergyCost); + break; + case AVH_WEAPON_SPIKE: + theCost = (float)BALANCE_VAR(kSpikeEnergyCost); + break; + case AVH_WEAPON_BITE: + theCost = (float)BALANCE_VAR(kBiteEnergyCost); + break; + case AVH_WEAPON_BITE2: + theCost = (float)BALANCE_VAR(kBite2EnergyCost); + break; + case AVH_WEAPON_SWIPE: + theCost = (float)BALANCE_VAR(kSwipeEnergyCost); + break; + case AVH_WEAPON_BLINK: + theCost = (float)BALANCE_VAR(kBlinkEnergyCost); + break; + case AVH_WEAPON_WEBSPINNER: + theCost = (float)BALANCE_VAR(kWebEnergyCost); + break; + case AVH_WEAPON_PARASITE: + theCost = (float)BALANCE_VAR(kParasiteEnergyCost); + break; + case AVH_WEAPON_DIVINEWIND: + theCost = (float)BALANCE_VAR(kDivineWindEnergyCost); + break; + case AVH_WEAPON_HEALINGSPRAY: + theCost = (float)BALANCE_VAR(kHealingSprayEnergyCost); + break; + case AVH_WEAPON_METABOLIZE: + theCost = (float)BALANCE_VAR(kMetabolizeEnergyCost); + break; + case AVH_WEAPON_UMBRA: + theCost = (float)BALANCE_VAR(kUmbraEnergyCost); + break; + case AVH_WEAPON_PRIMALSCREAM: + theCost = (float)BALANCE_VAR(kPrimalScreamEnergyCost); + break; + case AVH_WEAPON_BILEBOMB: + theCost = (float)BALANCE_VAR(kBileBombEnergyCost); + break; + case AVH_WEAPON_ACIDROCKET: + theCost = (float)BALANCE_VAR(kAcidRocketEnergyCost); + break; + case AVH_WEAPON_STOMP: + theCost = (float)BALANCE_VAR(kStompEnergyCost); + break; + case AVH_WEAPON_DEVOUR: + theCost = (float)BALANCE_VAR(kDevourEnergyCost); + break; + + // Abilities + case AVH_ABILITY_LEAP: + theCost = (float)BALANCE_VAR(kLeapEnergyCost); + break; + case AVH_ABILITY_CHARGE: + // Charge cost deducted in pm_shared now + theCost = 0.0f; // (float)BALANCE_VAR(kChargeEnergyCost); + break; + + } + + outEnergyCost = theCost; + + if(theCost > 0.0f) + { + theSuccess = true; + } + + return theSuccess; +} + +float AvHMUGetWalkSpeedFactor(AvHUser3 inUser3) +{ + float theMoveSpeed = .1f; + + switch(inUser3) + { + case AVH_USER3_MARINE_PLAYER: + theMoveSpeed = .095f; + break; + case AVH_USER3_ALIEN_PLAYER1: + //theMoveSpeed = .04f; + theMoveSpeed = .14f; + break; + case AVH_USER3_ALIEN_PLAYER2: + theMoveSpeed = .08f; + break; + case AVH_USER3_ALIEN_PLAYER3: + theMoveSpeed = .11f; + break; + case AVH_USER3_ALIEN_PLAYER4: + theMoveSpeed = .09f; + break; + case AVH_USER3_ALIEN_PLAYER5: + theMoveSpeed = .09f; + break; + } + + return theMoveSpeed; +} + +// : 991 -- added latency-based prediction for the ammount of energy available to the alien +bool AvHMUHasEnoughAlienEnergy(float& ioFuser, float inNormAmount, float latency) +{ + bool theSuccess = false; + + float theCurrentEnergy = ioFuser/kNormalizationNetworkFactor; + float thePredictedByLatency = 0.0f; + +#ifdef AVH_CLIENT + float theAlienEnergyRate = (float)BALANCE_VAR(kAlienEnergyRate); + float theUpgradeFactor = 1.0f; + thePredictedByLatency = (latency / 1000) * theAlienEnergyRate * theUpgradeFactor; +#endif + if((theCurrentEnergy + thePredictedByLatency) >= inNormAmount) + { + theSuccess = true; + } + + return theSuccess; +} + +void AvHMUUpdateAlienEnergy(float inTimePassed, int inUser3, int inUser4, float& ioFuser) +{ + if( (inUser3 == AVH_USER3_ALIEN_PLAYER1) || + (inUser3 == AVH_USER3_ALIEN_PLAYER2) || + (inUser3 == AVH_USER3_ALIEN_PLAYER3) || + (inUser3 == AVH_USER3_ALIEN_PLAYER4) || + (inUser3 == AVH_USER3_ALIEN_PLAYER5)) + { + if(!GetHasUpgrade(inUser4, MASK_PLAYER_STUNNED)) + { + // Percentage (0-1) per second + float theAlienEnergyRate = (float)BALANCE_VAR(kAlienEnergyRate); + //float kFadeChargingDeplectionRate = -2.8f*kAlienEnergyRate; + float kChargingDepletionRate = -BALANCE_VAR(kChargingEnergyScalar)*theAlienEnergyRate; + + const float kMultiplier = GetHasUpgrade(inUser4, MASK_BUFFED) ? (1.0f + BALANCE_VAR(kPrimalScreamEnergyFactor)) : 1.0f; + float theEnergyRate = theAlienEnergyRate*kMultiplier; + + float theUpgradeFactor = 1.0f; + int theNumLevels = AvHGetAlienUpgradeLevel(inUser4, MASK_UPGRADE_5); + if(theNumLevels > 0) + { + + theUpgradeFactor += theNumLevels*BALANCE_VAR(kAdrenalineEnergyPercentPerLevel); + } + + float theCurrentEnergy = ioFuser/kNormalizationNetworkFactor; + + float theNewEnergy = theCurrentEnergy + inTimePassed*theAlienEnergyRate*theUpgradeFactor; + + // If we're charging, reduce energy + if(GetHasUpgrade(inUser4, MASK_ALIEN_MOVEMENT)) + { + if(inUser3 == AVH_USER3_ALIEN_PLAYER4) + { +// theNewEnergy += inTimePassed*kFadeChargingDeplectionRate; + } + else + { + theNewEnergy += inTimePassed*kChargingDepletionRate; + } + } + + theNewEnergy = min(max(theNewEnergy, 0.0f), 1.0f); + + ioFuser = theNewEnergy*kNormalizationNetworkFactor; + } + } +} + +void AvHMUUpdateJetpackEnergy(bool inIsJetpacking, float theTimePassed, float& ioJetpackEnergy) +{ + if(inIsJetpacking) + { + ioJetpackEnergy -= theTimePassed*kJetpackEnergyLossRate; + } + else + { + ioJetpackEnergy += theTimePassed*kJetpackEnergyGainRate; + } + + ioJetpackEnergy = min(1.0f, ioJetpackEnergy); + ioJetpackEnergy = max(0.0f, ioJetpackEnergy); +} diff --git a/main/source/mod/AvHMovementUtil.h b/main/source/mod/AvHMovementUtil.h index ea5199f..49e9665 100644 --- a/main/source/mod/AvHMovementUtil.h +++ b/main/source/mod/AvHMovementUtil.h @@ -35,7 +35,7 @@ bool AvHMUGiveAlienEnergy(float& ioFuser, float inNormAmount); bool AvHMUGetEnergyCost(AvHWeaponID inWeaponID, float& outEnergyCost); float AvHMUGetWalkSpeedFactor(AvHUser3 inUser3); -bool AvHMUHasEnoughAlienEnergy(float& ioFuser, float inNormAmount); +bool AvHMUHasEnoughAlienEnergy(float& ioFuser, float inNormAmount, float latency = 0.0f); void AvHMUUpdateAlienEnergy(float inTimePassed, int inUser3, int inUser4, float& ioFuser); void AvHMUUpdateJetpackEnergy(bool inIsJetpacking, float theTimePassed, float& ioJetpackEnergy); diff --git a/main/source/mod/AvHNetworkMessages.cpp b/main/source/mod/AvHNetworkMessages.cpp index c291c4c..08d89aa 100644 --- a/main/source/mod/AvHNetworkMessages.cpp +++ b/main/source/mod/AvHNetworkMessages.cpp @@ -1,2241 +1,2277 @@ -#include "AvHNetworkMessages.h" -#include "NetworkMeter.h" -#include "util/MathUtil.h" //for WrapFloat -#include "util/STLUtil.h" //for MakeBytesFromHexPairs -#include "cl_dll/parsemsg.h" -#ifndef AVH_SERVER -#include "cl_dll/chudmisc.h" -#endif -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// MESSAGE CODES - NEED TO BE INITIALIZED BEFORE CLIENT CONNECTION, OR THEY'D -// BE LOCAL STATICS INSIDE OF THE FUNCTIONS USING LAZY INSTANTIATION -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifdef AVH_SERVER -int g_msgAmmoPickup = 0, g_msgAmmoX, g_msgBattery, g_msgCurWeapon, g_msgDamage, //5 - g_msgDeathMsg, g_msgFade, g_msgFlashlight, g_msgGeigerRange, g_msgHealth, //10 - g_msgHideWeapon, g_msgHudText, g_msgHudText2, g_msgInitHUD, g_msgItemPickup, - g_msgMOTD, g_msgResetHUD, g_msgSayText, g_msgScoreInfo, g_msgServerName, - g_msgSetFOV, g_msgShake, g_msgShowGameTitle, g_msgShowMenu, g_msgStatusText, - g_msgStatusValue, g_msgTeamInfo, g_msgTeamNames, g_msgTeamScore, g_msgTextMsg, - g_msgTrain, g_msgWeaponList, g_msgWeapPickup, g_msgAlienInfo, g_msgBalanceVar, - g_msgBlipList, g_msgBuildMiniMap, g_msgClientScripts, g_msgDebugCSP, g_msgEditPS, - g_msgFog, g_msgGameStatus, g_msgListPS, g_msgPlayHUDNotification, g_msgProgressBar, - g_msgServerVar, g_msgSetGammaRamp, g_msgSetOrder, g_msgSetParticleTemplates, - g_msgSetSelect, g_msgSetRequest, g_msgSetSoundNames, g_msgSetTechNodes, g_msgSetTechSlots, - g_msgSetTopDown, g_msgSetupMap, g_msgUpdateCountdown, g_msgUpdateEntityHierarchy, - g_msgProfileInfo, g_msgNexusBytes, g_msgIssueOrder, g_msgLUAMessage; - -void Net_InitializeMessages(void) -{ - if( g_msgAmmoPickup != 0 ) { return; } - g_msgAmmoPickup = REG_USER_MSG("AmmoPickup", 2 ); - g_msgAmmoX = REG_USER_MSG("AmmoX", 2 ); - g_msgBattery = REG_USER_MSG( "Battery", 2 ); - g_msgCurWeapon = REG_USER_MSG( "CurWeapon", 3 ); - g_msgDamage = REG_USER_MSG( "Damage", 12 ); - g_msgDeathMsg = REG_USER_MSG( "DeathMsg", -1 ); - g_msgFade = REG_USER_MSG( "ScreenFade", sizeof(ScreenFade) ); - g_msgFlashlight = REG_USER_MSG( "FLashlight", 2 ); - g_msgGeigerRange = REG_USER_MSG( "Geiger", 1 ); - g_msgHealth = REG_USER_MSG( "Health", 2 ); - g_msgHideWeapon = REG_USER_MSG( "HideWeapon", 1 ); - g_msgHudText = REG_USER_MSG( "HudText", -1 ); - g_msgHudText2 = REG_USER_MSG( "HudText2", -1 ); - g_msgInitHUD = REG_USER_MSG( "InitHUD", 0 ); - g_msgItemPickup = REG_USER_MSG( "ItemPickup", -1 ); - g_msgMOTD = REG_USER_MSG( "MOTD", -1 ); - g_msgResetHUD = REG_USER_MSG( "ResetHUD", 0 ); - g_msgSayText = REG_USER_MSG( "SayText", -1 ); - g_msgScoreInfo = REG_USER_MSG( "ScoreInfo", -1 ); - g_msgServerName = REG_USER_MSG( "ServerName", -1 ); - g_msgSetFOV = REG_USER_MSG( "SetFOV", 1 ); - g_msgShake = REG_USER_MSG( "ScreenShake", 6 ); - g_msgShowGameTitle = REG_USER_MSG( "GameTitle", 1 ); - g_msgShowMenu = REG_USER_MSG( "ShowMenu", -1 ); - g_msgStatusText = REG_USER_MSG( "StatusText", -1 ); - g_msgStatusValue = REG_USER_MSG( "StatusValue", 3 ); - g_msgTeamInfo = REG_USER_MSG( "TeamInfo", -1 ); - g_msgTeamNames = REG_USER_MSG( "TeamNames", -1 ); - g_msgTeamScore = REG_USER_MSG( "TeamScore", -1 ); - g_msgTextMsg = REG_USER_MSG( "TextMsg", -1 ); - g_msgTrain = REG_USER_MSG( "Train", 1 ); - g_msgWeaponList = REG_USER_MSG( "WeaponList", -1 ); - g_msgWeapPickup = REG_USER_MSG( "WeapPickup", 1 ); - g_msgAlienInfo = REG_USER_MSG( "AlienInfo", -1 ); - g_msgBalanceVar = REG_USER_MSG( "BalanceVar", -1 ); - g_msgBlipList = REG_USER_MSG( "BlipList", -1 ); - g_msgBuildMiniMap = REG_USER_MSG( "MiniMap", -1 ); - g_msgClientScripts = REG_USER_MSG( "ClScript", -1 ); - g_msgDebugCSP = REG_USER_MSG( "DebugCSP", 14 ); - g_msgEditPS = REG_USER_MSG( "EditPS", 2 ); - g_msgFog = REG_USER_MSG( "Fog", -1 ); - g_msgGameStatus = REG_USER_MSG( "GameStatus", -1 ); - g_msgListPS = REG_USER_MSG( "ListPS", -1 ); - g_msgPlayHUDNotification = REG_USER_MSG( "PlayHUDNot", 6 ); - g_msgProgressBar = REG_USER_MSG( "Progress", 3 ); - g_msgServerVar = REG_USER_MSG( "ServerVar", -1 ); - g_msgSetGammaRamp = REG_USER_MSG( "SetGmma", 2 ); - g_msgSetOrder = REG_USER_MSG( "SetOrder", -1 ); - g_msgSetParticleTemplates = REG_USER_MSG( "Particles", -1 ); - g_msgSetSelect = REG_USER_MSG( "SetSelect", -1 ); - g_msgSetRequest = REG_USER_MSG( "SetRequest", 2 ); - g_msgSetSoundNames = REG_USER_MSG( "SoundNames", -1 ); - g_msgSetTechNodes = REG_USER_MSG( "SetTech", 9 ); - g_msgSetTechSlots = REG_USER_MSG( "TechSlots", 1 + kNumTechSlots ); - g_msgSetTopDown = REG_USER_MSG( "SetTopDown", -1 ); - g_msgSetupMap = REG_USER_MSG( "SetupMap", -1 ); - g_msgUpdateCountdown = REG_USER_MSG( "Countdown", 1 ); - g_msgUpdateEntityHierarchy = REG_USER_MSG( "EntHier", -1 ); - g_msgProfileInfo = REG_USER_MSG( "ProfileInfo", 8 ); - g_msgNexusBytes = REG_USER_MSG( "NexusBytes", -1 ); - g_msgIssueOrder = REG_USER_MSG( "IssueOrder", 9); - g_msgLUAMessage = REG_USER_MSG( "LUAmsg", -1); -} -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// MESSAGE FUNCTIONS - READ/WRITE PAIRS FOR NETWORK MESSAGES -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -#define END_READ() ; - -#ifndef AVH_SERVER - void NetMsg_AmmoPickup( void* const buffer, const int size, int& index, int& count ) - { - BEGIN_READ( buffer, size ); - index = READ_BYTE(); - count = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_AmmoPickup( entvars_t* const pev, const int index, const int count ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgAmmoPickup, NULL, pev ); - WRITE_BYTE( index ); - WRITE_BYTE( count ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_AmmoX( void* const buffer, const int size, int& index, int& count ) - { - BEGIN_READ( buffer, size ); - index = READ_BYTE(); - count = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_AmmoX( entvars_t *pev, const int index, const int count ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgAmmoX, NULL, pev ); - WRITE_BYTE( index ); - WRITE_BYTE( count ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_Battery( void* const buffer, const int size, int& armor_amount ) - { - BEGIN_READ( buffer, size ); - armor_amount = READ_SHORT(); - END_READ(); - } -#else - void NetMsg_Battery( entvars_t* const pev, const int armor_amount ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBattery, NULL, pev ); - WRITE_SHORT( armor_amount ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_CurWeapon( void* const buffer, const int size, int& state, int& id, int& clip ) - { - BEGIN_READ( buffer, size ); - state = READ_BYTE(); - id = READ_BYTE(); - clip = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_CurWeapon( entvars_t* const pev, const int state, const int id, const int clip ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgCurWeapon, NULL, pev ); - WRITE_BYTE( state ); - WRITE_BYTE( id ); - WRITE_BYTE( clip ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_Damage( void* const buffer, const int size, int& dmg_save, int& dmg_take, long& bits, float* origin ) - { - BEGIN_READ( buffer, size ); - dmg_save = READ_BYTE(); - dmg_take = READ_BYTE(); - bits = READ_LONG(); - origin[0] = READ_COORD(); - origin[1] = READ_COORD(); - origin[2] = READ_COORD(); - END_READ(); - } -#else - void NetMsg_Damage( entvars_t* const pev, const int dmg_save, const int dmg_take, const long bits, const float* origin ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgDamage, NULL, pev ); - WRITE_BYTE( dmg_save ); - WRITE_BYTE( dmg_take ); - WRITE_LONG( bits ); - WRITE_COORD( origin[0] ); - WRITE_COORD( origin[1] ); - WRITE_COORD( origin[2] ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_DeathMsg( void* const buffer, const int size, int& killer_index, int& victim_index, string& weapon_name ) - { - BEGIN_READ( buffer, size ); - killer_index = READ_BYTE(); - victim_index = READ_BYTE(); - weapon_name = READ_STRING(); - END_READ(); - } -#else - void NetMsg_DeathMsg( const int killer_index, const int victim_index, string& weapon_name ) - { - MESSAGE_BEGIN( MSG_ALL, g_msgDeathMsg ); - WRITE_BYTE( killer_index ); - WRITE_BYTE( victim_index ); - WRITE_STRING( weapon_name.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_Fade( void* const buffer, const int size, ScreenFade& fade ) - { - BEGIN_READ( buffer, size ); - fade.duration = READ_SHORT(); - fade.holdTime = READ_SHORT(); - fade.fadeFlags = READ_SHORT(); - fade.r = READ_BYTE(); - fade.g = READ_BYTE(); - fade.b = READ_BYTE(); - fade.a = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_Fade( entvars_t* const pev, const ScreenFade& fade ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgFade, NULL, pev ); // use the magic #1 for "one client" - WRITE_SHORT( fade.duration ); - WRITE_SHORT( fade.holdTime ); - WRITE_SHORT( fade.fadeFlags ); - WRITE_BYTE( fade.r ); - WRITE_BYTE( fade.g ); - WRITE_BYTE( fade.b ); - WRITE_BYTE( fade.a ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_Flashlight( void* const buffer, const int size, int& is_on, int& flash_battery ) - { - BEGIN_READ( buffer, size ); - is_on = READ_BYTE(); - flash_battery = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_Flashlight( entvars_t* const pev, const int is_on, const int flash_battery ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgFlashlight, NULL, pev ); - WRITE_BYTE( is_on ); - WRITE_BYTE( flash_battery ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_GeigerRange( void* const buffer, const int size, int& range ) - { - BEGIN_READ( buffer, size ); - range = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_GeigerRange( entvars_t* const pev, const int range ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgGeigerRange, NULL, pev ); - WRITE_BYTE( range ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_Health( void* const buffer, const int size, int& health ) - { - BEGIN_READ( buffer, size ); - health = READ_SHORT(); - END_READ(); - } -#else - void NetMsg_Health( entvars_t* const pev, const int health ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgHealth, NULL, pev ); - WRITE_SHORT( health ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_HideWeapon( void* const buffer, const int size, int& hide ) - { - BEGIN_READ( buffer, size ); - hide = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_HideWeapon( entvars_t* const pev, const int hide ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgHideWeapon, NULL, pev ); - WRITE_BYTE( hide ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_HudText( void* const buffer, const int size, string& text ) - { - BEGIN_READ( buffer, size ); - text = READ_STRING(); - END_READ(); - } -#else - void NetMsg_HudText( entvars_t* const pev, const string& text ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgHudText, NULL, pev ); - WRITE_STRING( text.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_HudText2( void* const buffer, const int size, string& text, int& flags ) - { - BEGIN_READ( buffer, size ); - text = READ_STRING(); - flags = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_HudText2( entvars_t* const pev, const string& text, const int flags ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgHudText2, NULL, pev ); - WRITE_STRING( text.c_str() ); - WRITE_BYTE( flags ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_InitHUD( void* const buffer, const int size ) - { - BEGIN_READ( buffer, size ); - END_READ(); - } -#else - void NetMsg_InitHUD( entvars_t* const pev ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgInitHUD, NULL, pev ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ItemPickup( void* const buffer, const int size, string& item_name ) - { - BEGIN_READ( buffer, size ); - item_name = READ_STRING(); - END_READ(); - } -#else - void NetMsg_ItemPickup( entvars_t* const pev, const string& item_name ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgItemPickup, NULL, pev ); - WRITE_STRING( item_name.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_MOTD( void* const buffer, const int size, bool& is_finished, string& MOTD ) - { - BEGIN_READ( buffer, size ); - is_finished = (READ_BYTE() == 1); - MOTD = READ_STRING(); - END_READ(); - } -#else - void NetMsg_MOTD( entvars_t* const pev, const bool is_finished, const string& MOTD ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgMOTD, NULL, pev ); - WRITE_BYTE( is_finished ? 1 : 0); - WRITE_STRING( MOTD.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ResetHUD( void* const buffer, const int size ) - { - BEGIN_READ( buffer, size ); - END_READ(); - } -#else - void NetMsg_ResetHUD( entvars_t* const pev ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgResetHUD, NULL, pev ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SayText( void* const buffer, const int size, int& entity_index, string& text, string& location ) - { - BEGIN_READ( buffer, size ); - entity_index = READ_BYTE(); - text = READ_STRING(); - location = READ_STRING(); - END_READ(); - } -#else - //MESSAGE TO EVERYBODY - void NetMsg_SayText( const int entity_index, const string& text, const string& location ) - { - MESSAGE_BEGIN( MSG_ALL, g_msgSayText, NULL ); - WRITE_BYTE( entity_index ); - WRITE_STRING( text.c_str() ); - WRITE_STRING( location.c_str() ); - MESSAGE_END(); - } - - //MESSAGE TO ONE PERSON - void NetMsg_SayText( entvars_t* const pev, const int entity_index, const string& text, const string& location ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSayText, NULL, pev ); - WRITE_BYTE( entity_index ); - WRITE_STRING( text.c_str() ); - WRITE_STRING( location.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// puzl: 0001073 -#ifndef AVH_SERVER - void NetMsg_ScoreInfo( void* const buffer, const int size, ScoreInfo& info ) - { - BEGIN_READ( buffer, size ); - info.player_index = READ_BYTE(); - info.score = READ_SHORT(); - info.frags = READ_SHORT(); - info.deaths = READ_SHORT(); - info.player_class = READ_BYTE(); - info.auth = READ_SHORT(); - info.team = READ_SHORT(); - char* theString = READ_STRING(); -#ifdef USE_OLDAUTH - if(info.auth & PLAYERAUTH_CUSTOM) - { - //clear the string (I dont think this array is reset anywhere else (since everything is set when the score info message is sent anyways) - //so just memset it here to prevent any possible problems. - memset(&g_PlayerExtraInfo[info.player_index].customicon, 0, sizeof(g_PlayerExtraInfo[info.player_index].customicon)); - - // Read custom icon - - - if(theString && strlen(theString) >= 4 && strlen(theString) <= CUSTOM_ICON_LENGTH+2)//make sure the string is within the right size. - strncpy(g_PlayerExtraInfo[info.player_index].customicon, theString, sizeof(g_PlayerExtraInfo[info.player_index].customicon)-1); - } -#endif - END_READ(); - } -#else - void NetMsg_ScoreInfo( const ScoreInfo& info ) - { - MESSAGE_BEGIN( MSG_ALL, g_msgScoreInfo ); - WRITE_BYTE( info.player_index ); - WRITE_SHORT( info.score ); - WRITE_SHORT( info.frags ); - WRITE_SHORT( info.deaths ); - WRITE_BYTE( info.player_class ); - WRITE_SHORT( info.auth ); - WRITE_SHORT( info.team ); - WRITE_STRING("0"); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ServerName( void* const buffer, const int size, string& name ) - { - BEGIN_READ( buffer, size ); - name = READ_STRING(); - END_READ(); - } -#else - void NetMsg_ServerName( entvars_t* const pev, const string& name ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgServerName, NULL, pev ); - WRITE_STRING( name.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetFOV( void* const buffer, const int size, int& fov ) - { - BEGIN_READ( buffer, size ); - fov = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_SetFOV( entvars_t* const pev, const int fov ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetFOV, NULL, pev ); - WRITE_BYTE( fov ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_Shake( void* const buffer, const int size, ScreenShake& shake ) - { - BEGIN_READ( buffer, size ); - shake.amplitude = READ_SHORT(); - shake.duration = READ_SHORT(); - shake.frequency = READ_SHORT(); - END_READ(); - } -#else - void NetMsg_Shake( entvars_t* const pev, const ScreenShake& shake ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgShake, NULL, pev ); - WRITE_SHORT( shake.amplitude ); - WRITE_SHORT( shake.duration ); - WRITE_SHORT( shake.frequency ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ShowGameTitle( void* const buffer, const int size ) - { - BEGIN_READ( buffer, size ); - END_READ(); - } -#else - void NetMsg_ShowGameTitle( entvars_t* const pev ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgShowGameTitle, NULL, pev ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ShowMenu( void* const buffer, const int size, int& valid_slots, int& display_time, int& flags, string& content ) - { - BEGIN_READ( buffer, size ); - valid_slots = READ_SHORT(); - display_time = READ_CHAR(); - flags = READ_BYTE(); - content = READ_STRING(); - END_READ(); - } -#else - void NetMsg_ShowMenu( entvars_t* const pev, const int valid_slots, const int display_time, const int flags, const string& content ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgShowMenu, NULL, pev ); - WRITE_SHORT( valid_slots ); - WRITE_CHAR( display_time ); - WRITE_BYTE( flags ); - WRITE_STRING( content.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_StatusText( void* const buffer, const int size, int& location, string& content ) - { - BEGIN_READ( buffer, size ); - location = READ_BYTE(); - content = READ_STRING(); - END_READ(); - } -#else - void NetMsg_StatusText( entvars_t* const pev, const int location, const string& content ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgStatusText, NULL, pev ); - WRITE_BYTE( location ); - WRITE_STRING( content.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_StatusValue( void* const buffer, const int size, int& location, int& state ) - { - BEGIN_READ( buffer, size ); - location = READ_BYTE(); - state = READ_SHORT(); - END_READ(); - } -#else - void NetMsg_StatusValue( entvars_t* const pev, const int location, const int state ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgStatusValue, NULL, pev ); - WRITE_BYTE( location ); - WRITE_SHORT( state ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_TeamInfo( void* const buffer, const int size, int& player_index, string& team_id ) - { - BEGIN_READ( buffer, size ); - player_index = READ_BYTE(); - team_id = READ_STRING(); - END_READ(); - } -#else - //MESSAGE TO EVERYBODY - void NetMsg_TeamInfo( const int player_index, const string& team_id ) - { - MESSAGE_BEGIN( MSG_ALL, g_msgTeamInfo ); - WRITE_BYTE( player_index ); - WRITE_STRING( team_id.c_str() ); - MESSAGE_END(); - } - - //MESSAGE TO SPECTATORS - void NetMsgSpec_TeamInfo( const int player_index, const string& team_id ) - { - MESSAGE_BEGIN( MSG_SPEC, g_msgTeamInfo ); - WRITE_BYTE( player_index ); - WRITE_STRING( team_id.c_str() ); - MESSAGE_END(); - } - - //MESSAGE TO ONE PERSON - void NetMsg_TeamInfo( entvars_t* const pev, const int player_index, const string& team_id ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgTeamInfo, NULL, pev ); - WRITE_BYTE( player_index ); - WRITE_STRING( team_id.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_TeamNames( void* const buffer, const int size, StringList& team_names ) - { - team_names.clear(); - BEGIN_READ( buffer, size ); - int num_teams = READ_BYTE(); - for( int counter = 0; counter < num_teams; counter++ ) - { - string name(READ_STRING()); - team_names.push_back(name); - } - END_READ(); - } -#else - void NetMsg_TeamNames( entvars_t* const pev, const StringList& team_names ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgTeamNames, NULL, pev ); - WRITE_BYTE( team_names.size() ); - for( int counter = 0; counter < team_names.size(); counter++ ) - { - WRITE_STRING( team_names[counter].c_str() ); - } - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_TeamScore( void* const buffer, const int size, string& team_name, int& score, int& deaths ) - { - BEGIN_READ( buffer, size ); - team_name = READ_STRING(); - score = READ_SHORT(); - deaths = READ_SHORT(); - END_READ(); - } -#else - void NetMsg_TeamScore( entvars_t* const pev, const string& team_name, const int score, const int deaths ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgTeamScore, NULL, pev ); - WRITE_STRING( team_name.c_str() ); - WRITE_SHORT( score ); - WRITE_SHORT( deaths ); - MESSAGE_END(); - } - - void NetMsg_TeamScore( const string& team_name, const int score, const int deaths ) - { - MESSAGE_BEGIN( MSG_ALL, g_msgTeamScore ); - WRITE_STRING( team_name.c_str() ); - WRITE_SHORT( score ); - WRITE_SHORT( deaths ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_TextMsg( void* const buffer, const int size, int& destination, StringList& message ) - { - message.clear(); - BEGIN_READ( buffer, size ); - destination = READ_BYTE(); - message.push_back( string( READ_STRING() ) ); - message.push_back( string( READ_STRING() ) ); - message.push_back( string( READ_STRING() ) ); - message.push_back( string( READ_STRING() ) ); - message.push_back( string( READ_STRING() ) ); - END_READ(); - } -#else - //MESSAGE TO EVERYBODY - void NetMsg_TextMsg( const int destination, const StringList& message ) - { - MESSAGE_BEGIN( MSG_ALL, g_msgTextMsg ); - WRITE_BYTE( destination ); - WRITE_STRING( message[0].c_str() ); - if( message.size() > 1 ) - WRITE_STRING( message[1].c_str() ); - if( message.size() > 2 ) - WRITE_STRING( message[2].c_str() ); - if( message.size() > 3 ) - WRITE_STRING( message[3].c_str() ); - if( message.size() > 4 ) - WRITE_STRING( message[4].c_str() ); - MESSAGE_END(); - } - - //MESSAGE TO SPECTATORS - void NetMsgSpec_TextMsg( const int destination, const StringList& message ) - { - MESSAGE_BEGIN( MSG_SPEC, g_msgTextMsg ); - WRITE_BYTE( destination ); - WRITE_STRING( message[0].c_str() ); - if( message.size() > 1 ) - WRITE_STRING( message[1].c_str() ); - if( message.size() > 2 ) - WRITE_STRING( message[2].c_str() ); - if( message.size() > 3 ) - WRITE_STRING( message[3].c_str() ); - if( message.size() > 4 ) - WRITE_STRING( message[4].c_str() ); - MESSAGE_END(); - } - - //MESSAGE TO ONE PERSON - void NetMsg_TextMsg( entvars_t* const pev, const int destination, const StringList& message ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgTextMsg, NULL, pev ); - WRITE_BYTE( destination ); - WRITE_STRING( message[0].c_str() ); - if( message.size() > 1 ) - WRITE_STRING( message[1].c_str() ); - if( message.size() > 2 ) - WRITE_STRING( message[2].c_str() ); - if( message.size() > 3 ) - WRITE_STRING( message[3].c_str() ); - if( message.size() > 4 ) - WRITE_STRING( message[4].c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_Train( void* const buffer, const int size, int& state ) - { - BEGIN_READ( buffer, size ); - state = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_Train( entvars_t* const pev, const int state ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgTrain, NULL, pev ); - WRITE_BYTE( state ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_WeaponList( void* const buffer, const int size, WeaponList& weapon ) - { - BEGIN_READ( buffer, size ); - weapon.weapon_name = READ_STRING(); - weapon.ammo1_type = READ_CHAR(); - weapon.ammo1_max_amnt = READ_BYTE(); - weapon.ammo2_type = READ_CHAR(); - weapon.ammo2_max_amnt = READ_BYTE(); - weapon.bucket = READ_CHAR(); - weapon.bucket_pos = READ_CHAR(); - weapon.bit_index = READ_CHAR(); - weapon.flags = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_WeaponList( entvars_t* const pev, const WeaponList& weapon ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgWeaponList, NULL, pev ); - WRITE_STRING( weapon.weapon_name.c_str() ); - WRITE_CHAR( weapon.ammo1_type ); - WRITE_BYTE( weapon.ammo1_max_amnt ); - WRITE_CHAR( weapon.ammo2_type ); - WRITE_BYTE( weapon.ammo2_max_amnt ); - WRITE_CHAR( weapon.bucket ); - WRITE_CHAR( weapon.bucket_pos ); - WRITE_CHAR( weapon.bit_index ); - WRITE_BYTE( weapon.flags ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_WeapPickup( void* const buffer, const int size, int& weapon_id ) - { - BEGIN_READ( buffer, size ); - weapon_id = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_WeapPickup( entvars_t* const pev , const int weapon_id ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgWeapPickup, NULL, pev ); - WRITE_BYTE( weapon_id ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -enum AlienInfo_ChangeFlags -{ - NO_CHANGE = 0, - COORDS_CHANGED = 1, - STATUS_CHANGED = 2, - HEALTH_CHANGED = 4 -}; - -#ifndef AVH_SERVER - void NetMsg_AlienInfo( void* const buffer, const int size, bool& was_hive_info, AvHAlienUpgradeListType& upgrades, HiveInfoListType& hives ) - { - BEGIN_READ( buffer, size ); - int status, type, header = READ_BYTE(); - was_hive_info = (header & 0x80) == 0; - if( was_hive_info ) - { - int num_hives = header; - AlienInfo_ChangeFlags changes; - for( int counter = 0; counter < num_hives; counter++ ) - { - if( hives.size() <= counter ) - { - AvHHiveInfo hive; - hives.push_back( hive ); - } - - changes = (AlienInfo_ChangeFlags)READ_BYTE(); - if( changes & COORDS_CHANGED ) - { - hives[counter].mPosX = READ_COORD(); - hives[counter].mPosY = READ_COORD(); - hives[counter].mPosZ = READ_COORD(); - } - if( changes & STATUS_CHANGED ) - { - status = READ_BYTE(); - type = (status >> 3) & 0x03; - hives[counter].mUnderAttack = (status & 0x80) != 0; - hives[counter].mStatus = status & 0x07; - switch(type) - { - case 0: hives[counter].mTechnology = MESSAGE_NULL; break; - case 1: hives[counter].mTechnology = ALIEN_BUILD_DEFENSE_CHAMBER; break; - case 2: hives[counter].mTechnology = ALIEN_BUILD_SENSORY_CHAMBER; break; - case 3: hives[counter].mTechnology = ALIEN_BUILD_MOVEMENT_CHAMBER; break; - } - } - if( changes & HEALTH_CHANGED ) - { - hives[counter].mHealthPercentage = READ_BYTE(); - } - } - } - else - { - int num_upgrades = READ_BYTE(); - upgrades.clear(); - for( int counter = 0; counter < num_upgrades; counter++ ) - { - AvHAlienUpgradeCategory theUpgradeCategory = AvHAlienUpgradeCategory(READ_BYTE()); - upgrades.push_back(theUpgradeCategory); - } - } - END_READ(); - } -#else - void NetMsg_AlienInfo_Upgrades( entvars_t* const pev, const AvHAlienUpgradeListType& upgrades ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgAlienInfo, NULL, pev ); - WRITE_BYTE( 0x80 ); //magic number for hive size field, upgrade info - WRITE_BYTE( upgrades.size() ); - AvHAlienUpgradeListType::const_iterator current, end = upgrades.end(); - for( current = upgrades.begin(); current != end; ++current ) - { - WRITE_BYTE( *current ); - } - MESSAGE_END(); - } - - void NetMsg_AlienInfo_Hives( entvars_t* const pev, const HiveInfoListType& hives, const HiveInfoListType& client_hives ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgAlienInfo, NULL, pev ); - WRITE_BYTE( hives.size() ); - HiveInfoListType::const_iterator current, end = hives.end(); - int status, tech, index = 0; - int change_flags = NO_CHANGE; - for( current = hives.begin(); current != end; ++current, ++index ) - { - //put together change bitfield - if( client_hives.size() <= index || client_hives[index].mPosX != current->mPosX || - client_hives[index].mPosY != current->mPosY || client_hives[index].mPosZ != current->mPosZ ) - { change_flags |= COORDS_CHANGED; } - - if( client_hives.size() <= index || client_hives[index].mStatus != current->mStatus || - client_hives[index].mUnderAttack != current->mUnderAttack || client_hives[index].mTechnology != current->mTechnology ) - { change_flags |= STATUS_CHANGED; } - - if( client_hives.size() <= index || client_hives[index].mHealthPercentage != current->mHealthPercentage ) - { change_flags |= HEALTH_CHANGED; } - WRITE_BYTE(change_flags); - - //send change data - if( change_flags & COORDS_CHANGED ) - { - WRITE_COORD(current->mPosX); - WRITE_COORD(current->mPosY); - WRITE_COORD(current->mPosZ); - } - if( change_flags & STATUS_CHANGED ) - { - status = current->mStatus & 0x07; // 3 bits - switch( current->mTechnology ) // 2 bits - { - case MESSAGE_NULL: tech = 0; break; - case ALIEN_BUILD_DEFENSE_CHAMBER: tech = 1; break; - case ALIEN_BUILD_SENSORY_CHAMBER: tech = 2; break; - case ALIEN_BUILD_MOVEMENT_CHAMBER: tech = 3; break; - default: tech = 0; break; - } - status |= tech << 3; - status |= current->mUnderAttack ? 0x80 : 0x00; // 1 bit - WRITE_BYTE(status); - } - if( change_flags & HEALTH_CHANGED ) - { - WRITE_BYTE(current->mHealthPercentage); - } - } - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -//TODO: sub rapid conversion as described in Game Programming Gems... -union float_converter -{ - float f; - long l; -}; - -#ifndef AVH_SERVER - void NetMsg_BalanceVar( void* const buffer, const int size, string& name, BalanceMessageAction& action, int& ivalue, float& fvalue, string& svalue ) - { - BEGIN_READ( buffer, size ); - action = static_cast(READ_BYTE()); - switch( action ) - { - case BALANCE_ACTION_INSERT_INT: - { - name = READ_STRING(); - ivalue = READ_LONG(); - break; - } - case BALANCE_ACTION_INSERT_FLOAT: - { - float_converter c; - name = READ_STRING(); - c.l = READ_LONG(); - fvalue = c.f; - break; - } - case BALANCE_ACTION_INSERT_STRING: - { - name = READ_STRING(); - svalue = READ_STRING(); - break; - } - case BALANCE_ACTION_REMOVE: - { - name = READ_STRING(); - break; - } - case BALANCE_ACTION_CLEAR: - case BALANCE_ACTION_NOTIFY_PENDING: - case BALANCE_ACTION_NOTIFY_FINISHED: - { - break; - } - default: - break; - //todo: error condition here? - } - END_READ(); - } -#else - void NetMsg_BalanceVarChangesPending( entvars_t* const pev, const bool pending ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); - WRITE_BYTE( pending ? BALANCE_ACTION_NOTIFY_PENDING : BALANCE_ACTION_NOTIFY_FINISHED ); - MESSAGE_END(); - } - - void NetMsg_BalanceVarInsertInt( entvars_t* const pev, const string& name, const int data ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); - WRITE_BYTE( BALANCE_ACTION_INSERT_INT ); - WRITE_STRING( name.c_str() ); - WRITE_LONG( data ); - MESSAGE_END(); - } - - void NetMsg_BalanceVarInsertFloat( entvars_t* const pev, const string& name, const float data ) - { - float_converter c; - c.f = data; - MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); - WRITE_BYTE( BALANCE_ACTION_INSERT_FLOAT ); - WRITE_STRING( name.c_str() ); - WRITE_LONG( c.l ); - MESSAGE_END(); - } - - void NetMsg_BalanceVarInsertString( entvars_t* const pev, const string& name, const string& data ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); - WRITE_BYTE( BALANCE_ACTION_INSERT_STRING ); - WRITE_STRING( name.c_str() ); - WRITE_STRING( data.c_str() ); - MESSAGE_END(); - } - - void NetMsg_BalanceVarRemove( entvars_t* const pev, const string& name ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); - WRITE_BYTE( BALANCE_ACTION_REMOVE ); - WRITE_STRING( name.c_str() ); - MESSAGE_END(); - } - - void NetMsg_BalanceVarClear( entvars_t* const pev ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); - WRITE_BYTE( BALANCE_ACTION_CLEAR ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_BlipList( void* const buffer, const int size, bool& friendly_blips, AvHVisibleBlipList& list ) - { - int blip_count; - int8 status, info; - float X, Y, Z; - list.Clear(); - BEGIN_READ( buffer, size ); - int list_info = READ_BYTE(); - friendly_blips = (list_info & 0x80) != 0; - blip_count = (list_info & 0x7F); - for( int counter = 0; counter < blip_count; counter++ ) - { - X = READ_COORD(); - Y = READ_COORD(); - Z = READ_COORD(); - status = READ_BYTE(); - info = friendly_blips ? READ_BYTE() : 0; - list.AddBlip( X, Y, Z, status, info ); - } - END_READ(); - } -#else - void NetMsg_BlipList( entvars_t* const pev, const bool friendly_blips, const AvHVisibleBlipList& list ) - { - MESSAGE_BEGIN( MSG_ONE_UNRELIABLE, g_msgBlipList, NULL, pev ); - //pack header - 7 bits for blip count (doesn't go over 40 in practice), 1 bit for Friend or Foe - unsigned char list_info = list.mNumBlips | (friendly_blips ? 0x80 : 0); - WRITE_BYTE( list_info ); - //pack each blip - this could be optimized as follows once bit packer is implemented: - // convert X, Y to integer values ranging from 0 to 2047 (11 bits each) based on map extents - // convert Z to integer value ranging from 0 to 511 (9 bits) - // 4 bits for status (range 0-15, 1-9 currently used) - // 5 bits for info (range 1-32, refers to player number) - // total is 40 bits = 5 bytes for friendly, 35 bits for foe. - // savings would be 37.5% for friendly bytes. - // blip precision would be equal to double large minimap precision, with worst case of 4 unit X,Y separation for MT. - // because maps are much smaller vertically than horizontally as a rule, the worst case of 16 unit Z separation - // will very rarely occur. - for( int counter = 0; counter < list.mNumBlips; counter++ ) - { - WRITE_COORD( list.mBlipPositions[counter][0] ); - WRITE_COORD( list.mBlipPositions[counter][1] ); - WRITE_COORD( list.mBlipPositions[counter][2] ); - WRITE_BYTE( list.mBlipStatus[counter] ); - if( friendly_blips ) { WRITE_BYTE( list.mBlipInfo[counter] ); } - } - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_BuildMiniMap( void* const buffer, const int size, string& name, int& num_samples, int& processed_samples, int& width, int& height, uint8** map, bool& finished ) - { - BEGIN_READ( buffer, size ); - switch( READ_BYTE() ) - { - case 0: - name = READ_STRING(); - num_samples = READ_LONG(); - processed_samples = 0; - width = READ_LONG(); - height = READ_LONG(); - *map = new uint8[num_samples]; - finished = false; - break; - case 1: - { - int packet_samples = READ_BYTE(); - for( int counter = 0; counter < packet_samples; counter++ ) - { - (*map)[processed_samples++] = READ_BYTE(); - } - finished = false; - break; - } - case 2: - finished = true; - break; - } - END_READ(); - } -#else - void NetMsg_BuildMiniMap_Initialize( entvars_t* const pev, const string& name, const int num_samples, const int width, const int height ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBuildMiniMap, NULL, pev ); - WRITE_BYTE( 0 ); - WRITE_STRING( name.c_str() ); - WRITE_LONG( num_samples ); - WRITE_LONG( width ); - WRITE_LONG( height ); - MESSAGE_END(); - } - - void NetMsg_BuildMiniMap_Update( entvars_t* const pev, const int num_samples, const uint8* const samples ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBuildMiniMap, NULL, pev ); - WRITE_BYTE( 1 ); - WRITE_BYTE( num_samples ); - for( int counter = 0; counter < num_samples; counter++ ) - { - WRITE_BYTE( samples[counter] ); - } - MESSAGE_END(); - } - - void NetMsg_BuildMiniMap_Complete( entvars_t* const pev ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgBuildMiniMap, NULL, pev ); - WRITE_BYTE( 2 ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ClientScripts( void* const buffer, const int size, StringList& script_names ) - { - script_names.clear(); - BEGIN_READ( buffer, size ); - int num_scripts = READ_BYTE(); - while( script_names.size() < num_scripts ) - { - script_names.push_back( string( READ_STRING() ) ); - } - END_READ(); - } -#else - void NetMsg_ClientScripts( entvars_t* const pev, const StringList& script_names ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgClientScripts, NULL, pev ); - WRITE_BYTE( script_names.size() ); - StringList::const_iterator current, end = script_names.end(); - for( current = script_names.begin(); current != end; ++current ) - { - WRITE_STRING( current->c_str() ); - } - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_DebugCSP( void* const buffer, const int size, weapon_data_t& weapon_data, float& next_attack ) - { - BEGIN_READ( buffer, size ); - weapon_data.m_iId = READ_LONG(); - weapon_data.m_iClip = READ_LONG(); - weapon_data.m_flNextPrimaryAttack = READ_COORD(); - weapon_data.m_flTimeWeaponIdle = READ_COORD(); - next_attack = READ_COORD(); - END_READ(); - } -#else - void NetMsg_DebugCSP( entvars_t* const pev, const weapon_data_t& weapon_data, const float next_attack ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgDebugCSP, NULL, pev ); - WRITE_LONG( weapon_data.m_iId ); - WRITE_LONG( weapon_data.m_iClip ); - WRITE_COORD( weapon_data.m_flNextPrimaryAttack ); - WRITE_COORD( weapon_data.m_flTimeWeaponIdle ); - WRITE_COORD( next_attack ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_EditPS( void* const buffer, const int size, int& particle_index ) - { - BEGIN_READ( buffer, size ); - particle_index = READ_SHORT(); - END_READ(); - } -#else - void NetMsg_EditPS( entvars_t* const pev, const int particle_index ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgEditPS, NULL, pev ); - WRITE_SHORT( particle_index ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_Fog( void* const buffer, const int size, bool& enabled, int& R, int& G, int& B, float& start, float& end ) - { - BEGIN_READ( buffer, size ); - enabled = (READ_BYTE() != 0); - if( enabled ) - { - R = READ_BYTE(); - G = READ_BYTE(); - B = READ_BYTE(); - start = READ_COORD(); - end = READ_COORD(); - } - END_READ(); - } -#else - void NetMsg_Fog( entvars_t* const pev, const bool enabled, const int R, const int G, const int B, const float start, const float end ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgFog, NULL, pev ); - WRITE_BYTE( enabled ? 1 : 0 ); - if( enabled ) - { - WRITE_BYTE( R ); - WRITE_BYTE( G ); - WRITE_BYTE( B ); - WRITE_COORD( start ); - WRITE_COORD( end ); - } - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_GameStatus( void* const buffer, const int size, int& status_code, AvHMapMode& map_mode, int& game_time, int& timelimit, int& misc_data ) - { - BEGIN_READ( buffer, size ); - status_code = READ_BYTE(); - map_mode = (AvHMapMode)READ_BYTE(); - switch( status_code ) - { - case kGameStatusReset: - case kGameStatusResetNewMap: - case kGameStatusEnded: - break; - case kGameStatusGameTime: - game_time = READ_SHORT(); - timelimit = READ_SHORT(); - misc_data = READ_BYTE(); - break; - case kGameStatusUnspentLevels: - misc_data = READ_BYTE(); - break; - } - END_READ(); - } -#else - void NetMsg_GameStatus_State( const int status_code, const int map_mode ) - { - MESSAGE_BEGIN( MSG_ALL, g_msgGameStatus ); - WRITE_BYTE( status_code ); - WRITE_BYTE( map_mode ); - MESSAGE_END(); - } - - void NetMsg_GameStatus_State( entvars_t* const pev, const int status_code, const int map_mode ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgGameStatus, NULL, pev ); - WRITE_BYTE( status_code ); - WRITE_BYTE( map_mode ); - MESSAGE_END(); - } - - void NetMsg_GameStatus_Time( const int status_code, const int map_mode, const int game_time, const int timelimit, const int attacking_team_number, const bool is_reliable ) - { - int message_type = is_reliable ? MSG_ALL : MSG_BROADCAST; - MESSAGE_BEGIN( message_type, g_msgGameStatus ); - WRITE_BYTE( status_code ); - WRITE_BYTE( map_mode ); - WRITE_SHORT( game_time ); - WRITE_SHORT( timelimit ); - WRITE_BYTE( attacking_team_number ); - MESSAGE_END(); - } - - void NetMsg_GameStatus_UnspentLevels( entvars_t* const pev, const int status_code, const int map_mode, const int unspent_levels ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgGameStatus, NULL, pev ); - WRITE_BYTE( status_code ); - WRITE_BYTE( map_mode ); - WRITE_BYTE( unspent_levels ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ListPS( void* const buffer, const int size, string& system_name ) - { - BEGIN_READ( buffer, size ); - system_name = READ_STRING(); - END_READ(); - } -#else - void NetMsg_ListPS( entvars_t* const pev, const string& system_name ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgListPS, NULL, pev ); - WRITE_STRING( system_name.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_PlayHUDNotification( void* const buffer, const int size, int& flags, int& sound, float& location_x, float& location_y ) - { - BEGIN_READ( buffer, size ); - flags = READ_BYTE(); - sound = READ_BYTE(); - location_x = READ_COORD(); - location_y = READ_COORD(); - END_READ(); - } -#else - void NetMsg_PlayHUDNotification( entvars_t* const pev, const int flags, const int sound, const float location_x, const float location_y ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgPlayHUDNotification, NULL, pev ); - WRITE_BYTE( flags ); - WRITE_BYTE( sound ); - WRITE_COORD( location_x ); - WRITE_COORD( location_y ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ProgressBar( void* const buffer, const int size, int& entity_number, int& progress ) - { - BEGIN_READ( buffer, size ); - entity_number = READ_SHORT(); - progress = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_ProgressBar( entvars_t* const pev, const int entity_number, const int progress ) - { - MESSAGE_BEGIN( MSG_ONE_UNRELIABLE, g_msgProgressBar, NULL, pev ); - WRITE_SHORT( entity_number ); - WRITE_BYTE( progress ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_ServerVar( void* const buffer, const int size, string& name, string& value ) - { - BEGIN_READ( buffer, size ); - name = READ_STRING(); - value = READ_STRING(); - END_READ(); - } -#else - void NetMsg_ServerVar( entvars_t* const pev, const string& name, const string& value ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgServerVar, NULL, pev ); - WRITE_STRING( name.c_str() ); - WRITE_STRING( value.c_str() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetGammaRamp( void* const buffer, const int size, float& gamma ) - { - BEGIN_READ( buffer, size ); - gamma = READ_COORD(); - END_READ(); - } -#else - void NetMsg_SetGammaRamp( entvars_t* const pev, const float gamma ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetGammaRamp, NULL, pev ); - WRITE_COORD( gamma ); - MESSAGE_END(); - } - - void NetMsgSpec_SetGammaRamp( const float gamma ) - { - MESSAGE_BEGIN( MSG_SPEC, g_msgSetGammaRamp ); - WRITE_COORD( gamma ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetOrder( void* const buffer, const int size, AvHOrder& order ) - { - EntityListType players; - BEGIN_READ( buffer, size ); - order.SetReceiver( READ_BYTE() ); - order.SetOrderType( (AvHOrderType)READ_BYTE() ); - //order.SetOrderTargetType((AvHOrderTargetType)READ_BYTE()); //this is a redundant byte because SetOrderType automatically sets the target type as well. - switch( order.GetOrderTargetType() ) - { - case ORDERTARGETTYPE_LOCATION: - { - vec3_t location; - location.x = READ_COORD(); - location.y = READ_COORD(); - location.z = READ_COORD(); - order.SetLocation( location ); - break; - } - case ORDERTARGETTYPE_TARGET: - order.SetTargetIndex( READ_SHORT() ); - break; - } - order.SetUser3TargetType( (AvHUser3)READ_BYTE() ); - order.SetOrderCompleted( READ_BYTE() ); - // puzl: 1050 - // Need to sync the order status as it is only manipulated by the serverside state machine - order.SetOrderStatus( READ_BYTE() ); - END_READ(); - } -#else - void NetMsg_SetOrder( entvars_t* const pev, const AvHOrder& order ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetOrder, NULL, pev ); - WRITE_BYTE( order.GetReceiver() ); - WRITE_BYTE( order.GetOrderType() ); - //WRITE_BYTE( order.GetOrderTargetType() ); //this is a redundant byte because SetOrderType automatically sets the target type as well. - switch( order.GetOrderTargetType() ) - { - case ORDERTARGETTYPE_LOCATION: - { - vec3_t location; - order.GetLocation( location ); - WRITE_COORD( location.x ); - WRITE_COORD( location.y ); - WRITE_COORD( location.z ); - break; - } - case ORDERTARGETTYPE_TARGET: - WRITE_SHORT( order.GetTargetIndex() ); - break; - } - WRITE_BYTE( order.GetTargetUser3Type() ); - WRITE_BYTE( order.GetOrderCompleted() ); - // puzl: 1050 - // Need to sync the order status as it is only manipulated by the serverside state machine - WRITE_BYTE( order.GetOrderStatus() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetParticleTemplate( void* const buffer, const int size, AvHParticleTemplate& particle_template ) - { - ParticleParams gen_params, vel_params; - PSVector gravity; - BEGIN_READ( buffer, size ); - particle_template.SetName( string( READ_STRING() ) ); - particle_template.SetMaxParticles( READ_LONG() ); - particle_template.SetParticleSize( READ_COORD() ); - particle_template.SetSprite( string( READ_STRING() ) ); - particle_template.SetParticleSystemLifetime( READ_COORD() ); - particle_template.SetParticleLifetime( READ_COORD() ); - particle_template.SetAnimationSpeed( READ_COORD() ); - particle_template.SetNumSpriteFrames( READ_BYTE() ); - particle_template.SetParticleScaling( READ_COORD() ); - particle_template.SetRenderMode( READ_BYTE() ); - particle_template.SetGenerationRate( READ_LONG() ); - particle_template.SetGenerationShape( READ_BYTE() ); - for( int counter = 0; counter < 8; counter++ ) { gen_params[counter] = READ_LONG(); } - particle_template.SetGenerationParams( gen_params ); - particle_template.SetGenerationEntityIndex( READ_LONG() ); - particle_template.SetGenerationEntityParameter( READ_COORD() ); - particle_template.SetStartingVelocityShape( READ_BYTE() ); - for( int counter = 0; counter < 8; counter++ ) { vel_params[counter] = READ_LONG(); } - particle_template.SetStartingVelocityParams( vel_params ); - for( int counter = 0; counter < 3; counter++ ) { gravity[counter] = READ_COORD(); } - particle_template.SetGravity( gravity ); - particle_template.SetMaxAlpha( READ_COORD() ); - particle_template.SetParticleSystemIndexToGenerate( READ_LONG() ); - particle_template.SetFlags( READ_LONG() ); - END_READ(); - } -#else - void NetMsg_SetParticleTemplate( entvars_t* const pev, const AvHParticleTemplate& particle_template ) - { - ParticleParams gen_params, vel_params; - PSVector gravity; - MESSAGE_BEGIN( MSG_ONE, g_msgSetParticleTemplates, NULL, pev ); - WRITE_STRING( particle_template.GetName().c_str() ); - WRITE_LONG( particle_template.GetMaxParticles() ); - WRITE_COORD( particle_template.GetParticleSize() ); - WRITE_STRING( particle_template.GetSprite().c_str() ); - WRITE_COORD( particle_template.GetParticleSystemLifetime() ); - WRITE_COORD( particle_template.GetParticleLifetime() ); - WRITE_COORD( particle_template.GetAnimationSpeed() ); - WRITE_BYTE( particle_template.GetNumSpriteFrames() ); - WRITE_COORD( particle_template.GetParticleScaling() ); - WRITE_BYTE( particle_template.GetRenderMode() ); - WRITE_LONG( particle_template.GetGenerationRate() ); - WRITE_BYTE( particle_template.GetGenerationShape() ); - particle_template.GetGenerationParams( gen_params ); - for( int counter = 0; counter < 8; counter++ ) { WRITE_LONG( gen_params[counter] ); } - WRITE_LONG( particle_template.GetGenerationEntityIndex() ); - WRITE_COORD( particle_template.GetGenerationEntityParameter() ); - WRITE_BYTE( particle_template.GetStartingVelocityShape() ); - particle_template.GetStartingVelocityParams( vel_params ); - for( int counter = 0; counter < 8; counter++ ) { WRITE_LONG( vel_params[counter] ); } - particle_template.GetGravity( gravity ); - for( int counter = 0; counter < 3; counter++ ) { WRITE_COORD( gravity[counter] ); } - WRITE_COORD( particle_template.GetMaxAlpha() ); - WRITE_LONG( particle_template.GetParticleSystemIndexToGenerate() ); - WRITE_LONG( particle_template.GetFlags() ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetSelect( void* const buffer, const int size, Selection& selection ) - { - selection.selected_entities.clear(); - BEGIN_READ( buffer, size ); - selection.group_number = READ_BYTE(); - int num_entities = READ_BYTE(); - for(int counter = 0; counter < num_entities; counter++ ) - { selection.selected_entities.push_back( READ_SHORT() ); } - switch( selection.group_number ) - { - case 0: - selection.tracking_entity = (READ_BYTE() == 0) ? 0 : READ_SHORT(); - break; - case kSelectAllHotGroup: - break; - default: - selection.group_type = (AvHUser3)READ_BYTE(); - selection.group_alert = (AvHAlertType)READ_BYTE(); - break; - } - END_READ(); - } -#else - void NetMsg_SetSelect( entvars_t* const pev, Selection& selection ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetSelect, NULL, pev ); - WRITE_BYTE( selection.group_number ); - WRITE_BYTE( selection.selected_entities.size() ); - EntityListType::const_iterator current, end = selection.selected_entities.end(); - for( current = selection.selected_entities.begin(); current != end; ++current ) - { WRITE_SHORT( *current ); } - switch( selection.group_number ) - { - case 0: - if( selection.tracking_entity != 0 ) - { - WRITE_BYTE( 1 ); - WRITE_SHORT( selection.tracking_entity ); - } - else - { - WRITE_BYTE( 0 ); - } - break; - case kSelectAllHotGroup: - break; - default: - WRITE_BYTE( selection.group_type ); - WRITE_BYTE( selection.group_alert ); - break; - } - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetRequest( void* const buffer, const int size, int& request_type, int& request_count ) - { - BEGIN_READ( buffer, size ); - request_type = READ_BYTE(); - request_count = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_SetRequest( entvars_t* pev, const int request_type, const int request_count ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetRequest, NULL, pev ); - WRITE_BYTE( request_type ); - WRITE_BYTE( request_count ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetSoundNames( void* const buffer, const int size, bool& reset, string& sound_name ) - { - BEGIN_READ( buffer, size ); - reset = (READ_BYTE() != 0 ) ? true : false; - if( !reset ) - { sound_name = READ_STRING(); } - END_READ(); - } -#else - void NetMsg_SetSoundNames( entvars_t* pev, const bool reset, const string& sound_name ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetSoundNames, NULL, pev ); - WRITE_BYTE( reset ? 1 : 0 ); - if( !reset ) - { WRITE_STRING( sound_name.c_str() ); } - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetTechNode( void* const buffer, const int size, AvHTechNode*& node ) - { - BEGIN_READ( buffer, size ); - node = new AvHTechNode( (AvHMessageID)READ_BYTE() ); - node->setTechID( (AvHTechID)READ_BYTE() ); - node->setPrereqTechID1( (AvHTechID)READ_BYTE() ); - node->setPrereqTechID2( (AvHTechID)READ_BYTE() ); - node->setCost( READ_SHORT() ); - node->setBuildTime( READ_SHORT() ); - int flags = READ_BYTE(); - node->setAllowMultiples( (flags & 0x01) != 0 ); - node->setResearchState( (flags & 0x04) != 0 ); - node->setResearchable( (flags & 0x02) != 0 ); - END_READ(); - } -#else - void NetMsg_SetTechNode( entvars_t* pev, const AvHTechNode* node ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetTechNodes, NULL, pev ); - WRITE_BYTE( node->getMessageID() ); - WRITE_BYTE( node->getTechID() ); - WRITE_BYTE( node->getPrereqTechID1() ); - WRITE_BYTE( node->getPrereqTechID2() ); - WRITE_SHORT( node->getCost() ); - WRITE_SHORT( node->getBuildTime() ); - int flags = 0; - if( node->getAllowMultiples() ) { flags |= 0x01; } - if( node->getIsResearchable() ) { flags |= 0x02; } - if( node->getIsResearched() ) { flags |= 0x04; } - WRITE_BYTE( flags ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetTechSlots( void* const buffer, const int size, AvHTechSlots& tech_slots ) - { - BEGIN_READ( buffer, size ); - tech_slots.mUser3 = (AvHUser3)READ_BYTE(); - for( int counter = 0; counter < kNumTechSlots; counter++ ) - { tech_slots.mTechSlots[counter] = (AvHMessageID)READ_BYTE(); } - END_READ(); - } -#else - void NetMsg_SetTechSlots( entvars_t* pev, const AvHTechSlots& tech_slots ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetTechSlots, NULL, pev ); - WRITE_BYTE( tech_slots.mUser3 ); - for( int counter = 0; counter < kNumTechSlots; counter++ ) - { WRITE_BYTE( tech_slots.mTechSlots[counter] ); } - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetTopDown( void* const buffer, const int size, bool& is_menu_tech, bool& is_top_down, float* position, int& tech_slots ) - { - BEGIN_READ( buffer, size ); - is_menu_tech = (READ_BYTE() != 0); - if( is_menu_tech ) - { tech_slots = READ_LONG(); } - else - { - is_top_down = (READ_BYTE() != 0); - position[0] = READ_COORD(); - position[1] = READ_COORD(); - position[2] = READ_COORD(); - } - END_READ(); - } -#else - void NetMsg_SetTopDown_Position( entvars_t* const pev, const bool is_top_down, const float* const position ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetTopDown, NULL, pev ); - WRITE_BYTE( 0 ); - WRITE_BYTE( is_top_down ? 1 : 0 ); - WRITE_COORD( position[0] ); - WRITE_COORD( position[1] ); - WRITE_COORD( position[2] ); - MESSAGE_END(); - } - - void NetMsg_SetTopDown_TechSlots( entvars_t* const pev, const int tech_slots ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetTopDown, NULL, pev ); - WRITE_BYTE( 1 ); - WRITE_LONG( tech_slots ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_SetupMap( void* const buffer, const int size, bool& is_location, string& name, float* min_extents, float* max_extents, bool& draw_background ) - { - BEGIN_READ( buffer, size ); - is_location = (READ_BYTE() != 0); - name = READ_STRING(); - if( is_location ) - { - max_extents[0] = READ_COORD(); - max_extents[1] = READ_COORD(); - min_extents[0] = READ_COORD(); - min_extents[1] = READ_COORD(); - } - else - { - min_extents[2] = READ_COORD(); - max_extents[2] = READ_COORD(); - min_extents[0] = READ_COORD(); - min_extents[1] = READ_COORD(); - max_extents[0] = READ_COORD(); - max_extents[1] = READ_COORD(); - draw_background = (READ_BYTE() != 0); - } - } -#else - void NetMsg_SetupMap_Extents( entvars_t* const pev, const string& name, const float* const min_extents, const float* const max_extents, const bool draw_background ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetupMap, NULL, pev ); - WRITE_BYTE( 0 ); - WRITE_STRING( name.c_str() ); - WRITE_COORD( min_extents[2] ); - WRITE_COORD( max_extents[2] ); - WRITE_COORD( min_extents[0] ); - WRITE_COORD( min_extents[1] ); - WRITE_COORD( max_extents[0] ); - WRITE_COORD( max_extents[1] ); - WRITE_BYTE( draw_background ? 1 : 0 ); - MESSAGE_END(); - } - - void NetMsg_SetupMap_Location( entvars_t* const pev, const string& name, const float* const min_extents, const float* const max_extents ) - { - MESSAGE_BEGIN( MSG_ONE, g_msgSetupMap, NULL, pev ); - WRITE_BYTE( 1 ); - WRITE_STRING( name.c_str() ); - WRITE_COORD( max_extents[0] ); - WRITE_COORD( max_extents[1] ); - WRITE_COORD( min_extents[0] ); - WRITE_COORD( min_extents[1] ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_UpdateCountdown( void* const buffer, const int size, int& countdown ) - { - BEGIN_READ( buffer, size ); - countdown = READ_BYTE(); - END_READ(); - } -#else - void NetMsg_UpdateCountdown( const int countdown ) - { - MESSAGE_BEGIN( MSG_ALL, g_msgUpdateCountdown ); - WRITE_BYTE( countdown ); - MESSAGE_END(); - } -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -const int kNumStatusBits = 6; -const int kStatusMask = 0x3F; -const int kNumTeamBits = 2; -const int kTeamMask = 0x03; -const int kNumPositionCoordinateBits = 12; -const int kPositionCoordinateMask = 0xFFF; -const int kPositionCoordinateOffset = 4096; -const float kPositionCoordinateScale = 0.5f; -const int kNumPositionBits = kNumPositionCoordinateBits*2; - -const int kNumSquadBits = 3; -const int kSquadMask = 0x07; -const int kNumAngleBits = 4; -const int kAngleMask = 0x0F; -const int kNumPlayerIndexBits = 6; -const int kPlayerIndexMask = 0x3F; -const int kNumIndexBits = 14; -const int kIndexMask = 0x3FFF; -const int kNumFlagBits = 2; -const int kFlagMask = 0x03; -const int kEntHierFlagPlayer = 0x01; -const int kEntHierFlagDeletion = 0x02; - -#ifndef AVH_SERVER - //TODO : replace OldItems with vector - void ReadEntHier( MapEntityMap& NewItems, EntityListType& OldItems, int short_data, int long_data ); - float UnpackageCoord( const int packaged_coord ); - void NetMsg_UpdateEntityHierarchy( void* const buffer, const int size, MapEntityMap& NewItems, EntityListType& OldItems ) - { - NewItems.clear(); - OldItems.clear(); - int amnt_read = 0; - int short_data, long_data = 0; - BEGIN_READ( buffer, size ); - while( amnt_read < size ) - { - short_data = READ_SHORT(); - amnt_read += 2; - if( (short_data & kEntHierFlagDeletion) == 0 ) - { - long_data = READ_LONG(); - amnt_read += 4; - } - ReadEntHier( NewItems, OldItems, short_data, long_data ); - } - END_READ(); - } - - void ReadEntHier( MapEntityMap& NewItems, EntityListType& OldItems, int short_data, int long_data ) - { - int flags = short_data & kFlagMask; - short_data >>= kNumFlagBits; - - if( (flags & kEntHierFlagDeletion) == kEntHierFlagDeletion ) // Deletion (player or otherwise) - { - OldItems.push_back( short_data & kIndexMask ); - return; - } - - MapEntity ent; - int index = 0; - - ent.mUser3 = (AvHUser3)(long_data & kStatusMask); - long_data >>= kNumStatusBits; - ent.mTeam = (AvHTeamNumber)(long_data & kTeamMask); - long_data >>= kNumTeamBits; - ent.mY = UnpackageCoord(long_data & kPositionCoordinateMask); - long_data >>= kNumPositionCoordinateBits; - ent.mX = UnpackageCoord(long_data & kPositionCoordinateMask); - - if( (flags & kEntHierFlagPlayer) == kEntHierFlagPlayer ) // Player added/changed - { - index = short_data & kPlayerIndexMask; - short_data >>= kNumPlayerIndexBits; - ent.mAngle = (short_data & kAngleMask) * 22.5f; - short_data >>= kNumAngleBits; - ent.mSquadNumber = short_data & kSquadMask; - } - else // Other item added/changed - { - index = short_data & kIndexMask; - ent.mSquadNumber = 0; - ent.mAngle = 0; - } - - NewItems.insert( MapEntityMap::value_type( index, ent ) ); - } - - float UnpackageCoord( const int packaged_coord ) - { - float returnVal = packaged_coord; - returnVal /= kPositionCoordinateScale; - returnVal -= kPositionCoordinateOffset; - return returnVal; - } - -#else - void WriteEntHier( const int index, const MapEntity& ent, bool delete_flag, int& short_data, int& long_data ); - int PackageCoord( const float coord ); - void NetMsg_UpdateEntityHierarchy( entvars_t* const pev, const MapEntityMap& NewItems, const EntityListType& OldItems ) - { - const int kMaxUpdatesPerPacket = 30; - if( NewItems.empty() && OldItems.empty() ) { return; } //nothing to send! - - MapEntityMap::const_iterator new_current, new_end = NewItems.end(); - MapEntity temp; - EntityListType::const_iterator old_current, old_end = OldItems.end(); - int short_data, long_data, count = 1; - MESSAGE_BEGIN( MSG_ONE, g_msgUpdateEntityHierarchy, NULL, pev ); - for( new_current = NewItems.begin(); new_current != new_end; ++new_current, ++count ) - { - if( count % kMaxUpdatesPerPacket == 0 ) - { - MESSAGE_END(); - MESSAGE_BEGIN( MSG_ONE, g_msgUpdateEntityHierarchy, NULL, pev ); - } - WriteEntHier( new_current->first, new_current->second, false, short_data, long_data ); - WRITE_SHORT(short_data); - WRITE_LONG(long_data); - } - for( old_current = OldItems.begin(); old_current != old_end; ++old_current, ++count ) - { - if( count % kMaxUpdatesPerPacket == 0 ) - { - MESSAGE_END(); - MESSAGE_BEGIN( MSG_ONE, g_msgUpdateEntityHierarchy, NULL, pev ); - } - WriteEntHier( *old_current, temp, true, short_data, long_data ); - WRITE_SHORT(short_data); - } - MESSAGE_END(); - } - - void WriteEntHier( const int index, const MapEntity& ent, bool delete_flag, int& short_data, int& long_data ) - { - if( delete_flag ) - { - ASSERT( (index & ~kIndexMask) == 0 ); - short_data = index; - short_data <<= kNumFlagBits; - ASSERT( (short_data & kFlagMask) == 0 ); - short_data |= kEntHierFlagDeletion; - return; - } - - long_data = PackageCoord(ent.mX); - long_data <<= kNumPositionCoordinateBits; - ASSERT((long_data & kPositionCoordinateMask) == 0); - long_data |= PackageCoord(ent.mY); - long_data <<= kNumTeamBits; - ASSERT((long_data & kTeamMask) == 0); - ASSERT((ent.mTeam & ~kTeamMask) == 0); - long_data |= ent.mTeam & kTeamMask; - long_data <<= kNumStatusBits; - ASSERT((long_data & kStatusMask) == 0); - ASSERT((ent.mUser3 & ~kStatusMask) == 0); - long_data |= ent.mUser3 & kStatusMask; - - switch( ent.mUser3 ) - { - case AVH_USER3_MARINE_PLAYER: case AVH_USER3_COMMANDER_PLAYER: - case AVH_USER3_ALIEN_PLAYER1: case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: case AVH_USER3_ALIEN_EMBRYO: - case AVH_USER3_HEAVY: - { - ASSERT( (ent.mSquadNumber & ~kSquadMask) == 0 ); - short_data = ent.mSquadNumber; - short_data <<= kNumAngleBits; - int angle = WrapFloat(ent.mAngle,0,360); - angle /= 22.5f; - ASSERT( (short_data & kAngleMask) == 0); - ASSERT( (angle & ~kAngleMask) == 0); - short_data |= angle & kAngleMask; - short_data <<= kNumPlayerIndexBits; - ASSERT( ( short_data & kPlayerIndexMask ) == 0 ); - ASSERT( ( index & ~kPlayerIndexMask ) == 0 ); - short_data |= index & kIndexMask; - short_data <<= kNumFlagBits; - ASSERT( ( short_data & kFlagMask ) == 0 ); - short_data |= kEntHierFlagPlayer; - break; - } - default: - ASSERT( ( index & ~kIndexMask ) == 0 ); - short_data = index & kIndexMask; - short_data <<= kNumFlagBits; - } - } - - int PackageCoord( const float coord ) - { - float adjustedCoord = coord; - adjustedCoord += kPositionCoordinateOffset; - adjustedCoord *= kPositionCoordinateScale; - int returnVal = adjustedCoord; - ASSERT( (returnVal & ~kPositionCoordinateMask) == 0); - returnVal &= kPositionCoordinateMask; - return returnVal; - } - -#endif - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// tankefugl: 0000971 -#ifndef AVH_SERVER - void NetMsg_IssueOrder( void* const buffer, const int size, int& ordertype, int& ordersource, int& ordertarget ) - { - BEGIN_READ( buffer, size ); - ordertype = READ_BYTE(); - ordersource = READ_LONG(); - ordertarget = READ_LONG(); - END_READ(); - } -#else - void NetMsg_IssueOrder( entvars_t* const pev, const int ordertype, const int ordersource, const int ordertarget) - { - MESSAGE_BEGIN( MSG_ONE, g_msgIssueOrder, NULL, pev ); - WRITE_BYTE( ordertype ); - WRITE_LONG( ordersource ); - WRITE_LONG( ordertarget ); - MESSAGE_END(); - } -#endif -// :tankefugl - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifndef AVH_SERVER - void NetMsg_LUAMessage( void* const buffer, const int size, lua_State *L, int &arguments) - { - BEGIN_READ( buffer, size ); - arguments = READ_BYTE(); - for (int i = arguments; i > 0; i--) - { - if (i == arguments) - { - std::string temp = READ_STRING(); - lua_getglobal(L, temp.c_str()); - continue; - } - - int theLuaType = READ_BYTE(); - switch (theLuaType) - { - case LUA_TBOOLEAN: - lua_pushboolean(L, READ_BYTE()); - break; - case LUA_TNUMBER: - lua_pushnumber(L, (float)(READ_LONG())); - break; - case LUA_TSTRING: - lua_pushstring(L, READ_STRING()); - break; - default: - ASSERT(false); - break; - } - } - END_READ(); - arguments--; - } -#else - void NetMsg_LUAMessage(entvars_t* const pev, lua_State *L) - { - luaL_checktype(L, 2, LUA_TSTRING); - int arguments = lua_gettop(L); - for (int i = 3; i <= arguments; i++) - { - int theLuaType = lua_type(L, i); - if (!(theLuaType == LUA_TBOOLEAN || - theLuaType == LUA_TNUMBER || - theLuaType == LUA_TSTRING)) - luaL_typerror(L, i, "boolean|number|string"); - } - - MESSAGE_BEGIN( MSG_ONE, g_msgLUAMessage, NULL, pev ); - WRITE_BYTE(arguments - 1); - WRITE_STRING(lua_tostring(L, 2)); - int top = lua_gettop(L); - int current = 3; - while (current <= top) - { - int theLuaType = lua_type(L, current); - WRITE_BYTE(theLuaType); - switch (theLuaType) - { - case LUA_TBOOLEAN: - WRITE_BYTE(lua_toboolean(L, current)); - break; - case LUA_TNUMBER: - WRITE_LONG((float)(lua_tonumber(L, current))); - break; - case LUA_TSTRING: - WRITE_STRING(lua_tostring(L, current)); - break; - default: - ASSERT(false); - break; - } - current++; - } - MESSAGE_END(); - - } -#endif +#include "AvHNetworkMessages.h" +#include "NetworkMeter.h" +#include "util/MathUtil.h" //for WrapFloat +#include "util/STLUtil.h" //for MakeBytesFromHexPairs +#include "cl_dll/parsemsg.h" +#ifndef AVH_SERVER +#include "cl_dll/chudmisc.h" +#endif +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// MESSAGE CODES - NEED TO BE INITIALIZED BEFORE CLIENT CONNECTION, OR THEY'D +// BE LOCAL STATICS INSIDE OF THE FUNCTIONS USING LAZY INSTANTIATION +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifdef AVH_SERVER +int g_msgAmmoPickup = 0, g_msgAmmoX, g_msgBattery, g_msgCurWeapon, g_msgDamage, //5 + g_msgDeathMsg, g_msgFade, g_msgFlashlight, g_msgGeigerRange, g_msgHealth, //10 + g_msgHideWeapon, g_msgHudText, g_msgHudText2, g_msgInitHUD, g_msgItemPickup, + g_msgMOTD, g_msgResetHUD, g_msgSayText, g_msgScoreInfo, g_msgServerName, + g_msgSetFOV, g_msgShake, g_msgShowGameTitle, g_msgShowMenu, g_msgStatusText, + g_msgStatusValue, g_msgTeamInfo, g_msgTeamNames, g_msgTeamScore, g_msgTextMsg, + g_msgTrain, g_msgWeaponList, g_msgWeapPickup, g_msgAlienInfo, g_msgBalanceVar, + g_msgBlipList, g_msgBuildMiniMap, g_msgClientScripts, g_msgDebugCSP, g_msgEditPS, + g_msgFog, g_msgGameStatus, g_msgListPS, g_msgPlayHUDNotification, g_msgProgressBar, + g_msgServerVar, g_msgSetGammaRamp, g_msgSetOrder, g_msgSetParticleTemplates,g_msgDelParts, + g_msgSetSelect, g_msgSetRequest, g_msgSetSoundNames, g_msgSetTechNodes, g_msgSetTechSlots, + g_msgSetTopDown, g_msgSetupMap, g_msgUpdateCountdown, g_msgUpdateEntityHierarchy, g_msgDelEntityHierarchy, + g_msgProfileInfo, g_msgNexusBytes, g_msgIssueOrder, g_msgHUDSetUpgrades; + +void Net_InitializeMessages(void) +{ + if( g_msgAmmoPickup != 0 ) { return; } + g_msgAmmoPickup = REG_USER_MSG("AmmoPickup", 2 ); + g_msgAmmoX = REG_USER_MSG("AmmoX", 2 ); + g_msgBattery = REG_USER_MSG( "Battery", 2 ); + g_msgCurWeapon = REG_USER_MSG( "CurWeapon", 3 ); + g_msgDamage = REG_USER_MSG( "Damage", 12 ); + g_msgDeathMsg = REG_USER_MSG( "DeathMsg", -1 ); + g_msgFade = REG_USER_MSG( "ScreenFade", sizeof(ScreenFade) ); + g_msgFlashlight = REG_USER_MSG( "FLashlight", 2 ); + g_msgGeigerRange = REG_USER_MSG( "Geiger", 1 ); + g_msgHealth = REG_USER_MSG( "Health", 2 ); + g_msgHideWeapon = REG_USER_MSG( "HideWeapon", 1 ); + g_msgHudText = REG_USER_MSG( "HudText", -1 ); + g_msgHudText2 = REG_USER_MSG( "HudText2", -1 ); + g_msgInitHUD = REG_USER_MSG( "InitHUD", 0 ); + g_msgItemPickup = REG_USER_MSG( "ItemPickup", -1 ); + g_msgMOTD = REG_USER_MSG( "MOTD", -1 ); + g_msgResetHUD = REG_USER_MSG( "ResetHUD", 0 ); + g_msgSayText = REG_USER_MSG( "SayText", -1 ); + // : 0001073 + g_msgScoreInfo = REG_USER_MSG( "ScoreInfo", -1 ); + g_msgServerName = REG_USER_MSG( "ServerName", -1 ); + g_msgSetFOV = REG_USER_MSG( "SetFOV", 1 ); + g_msgShake = REG_USER_MSG( "ScreenShake", 6 ); + g_msgShowGameTitle = REG_USER_MSG( "GameTitle", 1 ); + g_msgShowMenu = REG_USER_MSG( "ShowMenu", -1 ); + g_msgStatusText = REG_USER_MSG( "StatusText", -1 ); + g_msgStatusValue = REG_USER_MSG( "StatusValue", 3 ); + g_msgTeamInfo = REG_USER_MSG( "TeamInfo", -1 ); + g_msgTeamNames = REG_USER_MSG( "TeamNames", -1 ); + g_msgTeamScore = REG_USER_MSG( "TeamScore", -1 ); + g_msgTextMsg = REG_USER_MSG( "TextMsg", -1 ); + g_msgTrain = REG_USER_MSG( "Train", 1 ); + g_msgWeaponList = REG_USER_MSG( "WeaponList", -1 ); + g_msgWeapPickup = REG_USER_MSG( "WeapPickup", 1 ); + g_msgAlienInfo = REG_USER_MSG( "AlienInfo", -1 ); + g_msgBalanceVar = REG_USER_MSG( "BalanceVar", -1 ); + g_msgBlipList = REG_USER_MSG( "BlipList", -1 ); + g_msgBuildMiniMap = REG_USER_MSG( "MiniMap", -1 ); + g_msgClientScripts = REG_USER_MSG( "ClScript", -1 ); + g_msgDebugCSP = REG_USER_MSG( "DebugCSP", 14 ); + g_msgEditPS = REG_USER_MSG( "EditPS", 2 ); + g_msgFog = REG_USER_MSG( "Fog", -1 ); + g_msgGameStatus = REG_USER_MSG( "GameStatus", -1 ); + g_msgListPS = REG_USER_MSG( "ListPS", -1 ); + g_msgPlayHUDNotification = REG_USER_MSG( "PlayHUDNot", 6 ); + g_msgHUDSetUpgrades = REG_USER_MSG( "SetUpgrades", 1); + g_msgProgressBar = REG_USER_MSG( "Progress", -1 ); + g_msgServerVar = REG_USER_MSG( "ServerVar", -1 ); + g_msgSetGammaRamp = REG_USER_MSG( "SetGmma", 1 ); + g_msgSetOrder = REG_USER_MSG( "SetOrder", -1 ); + g_msgSetParticleTemplates = REG_USER_MSG( "Particles", -1 ); + g_msgDelParts = REG_USER_MSG( "DelParts", 0); + g_msgSetSelect = REG_USER_MSG( "SetSelect", -1 ); + g_msgSetRequest = REG_USER_MSG( "SetRequest", 2 ); + g_msgSetSoundNames = REG_USER_MSG( "SoundNames", -1 ); + g_msgSetTechNodes = REG_USER_MSG( "SetTech", 9 ); + g_msgSetTechSlots = REG_USER_MSG( "TechSlots", 1 + kNumTechSlots ); + g_msgSetTopDown = REG_USER_MSG( "SetTopDown", -1 ); + g_msgSetupMap = REG_USER_MSG( "SetupMap", -1 ); + g_msgUpdateCountdown = REG_USER_MSG( "Countdown", 1 ); + g_msgUpdateEntityHierarchy = REG_USER_MSG( "EntHier", -1 ); + g_msgDelEntityHierarchy = REG_USER_MSG( "DelEntHier", 0); + g_msgProfileInfo = REG_USER_MSG( "ProfileInfo", 8 ); + g_msgNexusBytes = REG_USER_MSG( "NexusBytes", -1 ); + // : 0000971 + g_msgIssueOrder = REG_USER_MSG( "IssueOrder", 9); + // : +} +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// MESSAGE FUNCTIONS - READ/WRITE PAIRS FOR NETWORK MESSAGES +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#define END_READ() ; + +#ifndef AVH_SERVER + void NetMsg_AmmoPickup( void* const buffer, const int size, int& index, int& count ) + { + BEGIN_READ( buffer, size ); + index = READ_BYTE(); + count = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_AmmoPickup( entvars_t* const pev, const int index, const int count ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgAmmoPickup, NULL, pev ); + WRITE_BYTE( index ); + WRITE_BYTE( count ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_AmmoX( void* const buffer, const int size, int& index, int& count ) + { + BEGIN_READ( buffer, size ); + index = READ_BYTE(); + count = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_AmmoX( entvars_t *pev, const int index, const int count ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgAmmoX, NULL, pev ); + WRITE_BYTE( index ); + WRITE_BYTE( count ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_Battery( void* const buffer, const int size, int& armor_amount ) + { + BEGIN_READ( buffer, size ); + armor_amount = READ_SHORT(); + END_READ(); + } +#else + void NetMsg_Battery( entvars_t* const pev, const int armor_amount ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBattery, NULL, pev ); + WRITE_SHORT( armor_amount ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_CurWeapon( void* const buffer, const int size, int& state, int& id, int& clip ) + { + BEGIN_READ( buffer, size ); + state = READ_BYTE(); + id = READ_BYTE(); + clip = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_CurWeapon( entvars_t* const pev, const int state, const int id, const int clip ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgCurWeapon, NULL, pev ); + WRITE_BYTE( state ); + WRITE_BYTE( id ); + WRITE_BYTE( clip ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_Damage( void* const buffer, const int size, int& dmg_save, int& dmg_take, long& bits, float* origin ) + { + BEGIN_READ( buffer, size ); + dmg_save = READ_BYTE(); + dmg_take = READ_BYTE(); + bits = READ_LONG(); + origin[0] = READ_COORD(); + origin[1] = READ_COORD(); + origin[2] = READ_COORD(); + END_READ(); + } +#else + void NetMsg_Damage( entvars_t* const pev, const int dmg_save, const int dmg_take, const long bits, const float* origin ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgDamage, NULL, pev ); + WRITE_BYTE( dmg_save ); + WRITE_BYTE( dmg_take ); + WRITE_LONG( bits ); + WRITE_COORD( origin[0] ); + WRITE_COORD( origin[1] ); + WRITE_COORD( origin[2] ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_DeathMsg( void* const buffer, const int size, int& killer_index, int& victim_index, string& weapon_name ) + { + BEGIN_READ( buffer, size ); + killer_index = READ_BYTE(); + victim_index = READ_BYTE(); + weapon_name = READ_STRING(); + END_READ(); + } +#else + void NetMsg_DeathMsg( const int killer_index, const int victim_index, string& weapon_name ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgDeathMsg ); + WRITE_BYTE( killer_index ); + WRITE_BYTE( victim_index ); + WRITE_STRING( weapon_name.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_Fade( void* const buffer, const int size, ScreenFade& fade ) + { + BEGIN_READ( buffer, size ); + fade.duration = READ_SHORT(); + fade.holdTime = READ_SHORT(); + fade.fadeFlags = READ_SHORT(); + fade.r = READ_BYTE(); + fade.g = READ_BYTE(); + fade.b = READ_BYTE(); + fade.a = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_Fade( entvars_t* const pev, const ScreenFade& fade ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgFade, NULL, pev ); // use the magic #1 for "one client" + WRITE_SHORT( fade.duration ); + WRITE_SHORT( fade.holdTime ); + WRITE_SHORT( fade.fadeFlags ); + WRITE_BYTE( fade.r ); + WRITE_BYTE( fade.g ); + WRITE_BYTE( fade.b ); + WRITE_BYTE( fade.a ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_Flashlight( void* const buffer, const int size, int& is_on, int& flash_battery ) + { + BEGIN_READ( buffer, size ); + is_on = READ_BYTE(); + flash_battery = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_Flashlight( entvars_t* const pev, const int is_on, const int flash_battery ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgFlashlight, NULL, pev ); + WRITE_BYTE( is_on ); + WRITE_BYTE( flash_battery ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_GeigerRange( void* const buffer, const int size, int& range ) + { + BEGIN_READ( buffer, size ); + range = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_GeigerRange( entvars_t* const pev, const int range ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgGeigerRange, NULL, pev ); + WRITE_BYTE( range ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_Health( void* const buffer, const int size, int& health ) + { + BEGIN_READ( buffer, size ); + health = READ_SHORT(); + END_READ(); + } +#else + void NetMsg_Health( entvars_t* const pev, const int health ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgHealth, NULL, pev ); + WRITE_SHORT( health ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_HideWeapon( void* const buffer, const int size, int& hide ) + { + BEGIN_READ( buffer, size ); + hide = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_HideWeapon( entvars_t* const pev, const int hide ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgHideWeapon, NULL, pev ); + WRITE_BYTE( hide ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_HudText( void* const buffer, const int size, string& text ) + { + BEGIN_READ( buffer, size ); + text = READ_STRING(); + END_READ(); + } +#else + void NetMsg_HudText( entvars_t* const pev, const string& text ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgHudText, NULL, pev ); + WRITE_STRING( text.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_HudText2( void* const buffer, const int size, string& text, int& flags ) + { + BEGIN_READ( buffer, size ); + text = READ_STRING(); + flags = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_HudText2( entvars_t* const pev, const string& text, const int flags ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgHudText2, NULL, pev ); + WRITE_STRING( text.c_str() ); + WRITE_BYTE( flags ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_InitHUD( void* const buffer, const int size ) + { + BEGIN_READ( buffer, size ); + END_READ(); + } +#else + void NetMsg_InitHUD( entvars_t* const pev ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgInitHUD, NULL, pev ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ItemPickup( void* const buffer, const int size, string& item_name ) + { + BEGIN_READ( buffer, size ); + item_name = READ_STRING(); + END_READ(); + } +#else + void NetMsg_ItemPickup( entvars_t* const pev, const string& item_name ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgItemPickup, NULL, pev ); + WRITE_STRING( item_name.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_MOTD( void* const buffer, const int size, bool& is_finished, string& MOTD ) + { + BEGIN_READ( buffer, size ); + is_finished = (READ_BYTE() == 1); + MOTD = READ_STRING(); + END_READ(); + } +#else + void NetMsg_MOTD( entvars_t* const pev, const bool is_finished, const string& MOTD ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgMOTD, NULL, pev ); + WRITE_BYTE( is_finished ? 1 : 0); + WRITE_STRING( MOTD.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ResetHUD( void* const buffer, const int size ) + { + BEGIN_READ( buffer, size ); + END_READ(); + } +#else + void NetMsg_ResetHUD( entvars_t* const pev ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgResetHUD, NULL, pev ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SayText( void* const buffer, const int size, int& entity_index, string& text, string& location ) + { + BEGIN_READ( buffer, size ); + entity_index = READ_BYTE(); + text = READ_STRING(); + location = READ_STRING(); + END_READ(); + } +#else + //MESSAGE TO EVERYBODY + void NetMsg_SayText( const int entity_index, const string& text, const string& location ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgSayText, NULL ); + WRITE_BYTE( entity_index ); + WRITE_STRING( text.c_str() ); + WRITE_STRING( location.c_str() ); + MESSAGE_END(); + } + + //MESSAGE TO ONE PERSON + void NetMsg_SayText( entvars_t* const pev, const int entity_index, const string& text, const string& location ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSayText, NULL, pev ); + WRITE_BYTE( entity_index ); + WRITE_STRING( text.c_str() ); + WRITE_STRING( location.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// : 0001073 +#ifndef AVH_SERVER + void NetMsg_ScoreInfo( void* const buffer, const int size, ScoreInfo& info ) + { + BEGIN_READ( buffer, size ); + info.player_index = READ_BYTE(); + info.score = READ_SHORT(); + info.frags = READ_SHORT(); + info.deaths = READ_SHORT(); + info.extra = READ_SHORT(); + info.player_class = READ_BYTE(); + info.auth = READ_SHORT(); + info.team = READ_SHORT(); + info.health = READ_SHORT(); + char* theString = READ_STRING(); +#ifdef USE_OLDAUTH + if(info.auth & PLAYERAUTH_CUSTOM) + { + //clear the string (I dont think this array is reset anywhere else (since everything is set when the score info message is sent anyways) + //so just memset it here to prevent any possible problems. + memset(&g_PlayerExtraInfo[info.player_index].customicon, 0, sizeof(g_PlayerExtraInfo[info.player_index].customicon)); + + // Read custom icon + + + if(theString && strlen(theString) >= 4 && strlen(theString) <= CUSTOM_ICON_LENGTH+2)//make sure the string is within the right size. + strncpy(g_PlayerExtraInfo[info.player_index].customicon, theString, sizeof(g_PlayerExtraInfo[info.player_index].customicon)-1); + } +#endif + END_READ(); + } +#else + void NetMsg_ScoreInfo( const ScoreInfo& info ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgScoreInfo ); + WRITE_BYTE( info.player_index ); + WRITE_SHORT( info.score ); + WRITE_SHORT( info.frags ); + WRITE_SHORT( info.deaths ); + WRITE_SHORT( info.extra ); + WRITE_BYTE( info.player_class ); + WRITE_SHORT( info.auth ); + WRITE_SHORT( info.team ); + WRITE_SHORT( info.health ); + WRITE_STRING("0"); + MESSAGE_END(); + } + void NetMsgSpec_ScoreInfo( const ScoreInfo& info ) + { + MESSAGE_BEGIN( MSG_SPEC, g_msgScoreInfo ); + WRITE_BYTE( info.player_index ); + WRITE_SHORT( info.score ); + WRITE_SHORT( info.frags ); + WRITE_SHORT( info.deaths ); + WRITE_SHORT( info.extra ); + WRITE_BYTE( info.player_class ); + WRITE_SHORT( info.auth ); + WRITE_SHORT( info.team ); + WRITE_SHORT( info.health ); + WRITE_STRING("0"); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ServerName( void* const buffer, const int size, string& name ) + { + BEGIN_READ( buffer, size ); + name = READ_STRING(); + END_READ(); + } +#else + void NetMsg_ServerName( entvars_t* const pev, const string& name ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgServerName, NULL, pev ); + WRITE_STRING( name.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetFOV( void* const buffer, const int size, int& fov ) + { + BEGIN_READ( buffer, size ); + fov = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_SetFOV( entvars_t* const pev, const int fov ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetFOV, NULL, pev ); + WRITE_BYTE( fov ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_Shake( void* const buffer, const int size, ScreenShake& shake ) + { + BEGIN_READ( buffer, size ); + shake.amplitude = READ_SHORT(); + shake.duration = READ_SHORT(); + shake.frequency = READ_SHORT(); + END_READ(); + } +#else + void NetMsg_Shake( entvars_t* const pev, const ScreenShake& shake ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgShake, NULL, pev ); + WRITE_SHORT( shake.amplitude ); + WRITE_SHORT( shake.duration ); + WRITE_SHORT( shake.frequency ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ShowGameTitle( void* const buffer, const int size ) + { + BEGIN_READ( buffer, size ); + END_READ(); + } +#else + void NetMsg_ShowGameTitle( entvars_t* const pev ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgShowGameTitle, NULL, pev ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ShowMenu( void* const buffer, const int size, int& valid_slots, int& display_time, int& flags, string& content ) + { + BEGIN_READ( buffer, size ); + valid_slots = READ_SHORT(); + display_time = READ_CHAR(); + flags = READ_BYTE(); + content = READ_STRING(); + END_READ(); + } +#else + void NetMsg_ShowMenu( entvars_t* const pev, const int valid_slots, const int display_time, const int flags, const string& content ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgShowMenu, NULL, pev ); + WRITE_SHORT( valid_slots ); + WRITE_CHAR( display_time ); + WRITE_BYTE( flags ); + WRITE_STRING( content.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_StatusText( void* const buffer, const int size, int& location, string& content ) + { + BEGIN_READ( buffer, size ); + location = READ_BYTE(); + content = READ_STRING(); + END_READ(); + } +#else + void NetMsg_StatusText( entvars_t* const pev, const int location, const string& content ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgStatusText, NULL, pev ); + WRITE_BYTE( location ); + WRITE_STRING( content.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_StatusValue( void* const buffer, const int size, int& location, int& state ) + { + BEGIN_READ( buffer, size ); + location = READ_BYTE(); + state = READ_SHORT(); + END_READ(); + } +#else + void NetMsg_StatusValue( entvars_t* const pev, const int location, const int state ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgStatusValue, NULL, pev ); + WRITE_BYTE( location ); + WRITE_SHORT( state ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_TeamInfo( void* const buffer, const int size, int& player_index, string& team_id ) + { + BEGIN_READ( buffer, size ); + player_index = READ_BYTE(); + team_id = READ_STRING(); + END_READ(); + } +#else + //MESSAGE TO EVERYBODY + void NetMsg_TeamInfo( const int player_index, const string& team_id ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgTeamInfo ); + WRITE_BYTE( player_index ); + WRITE_STRING( team_id.c_str() ); + MESSAGE_END(); + } + + //MESSAGE TO SPECTATORS + void NetMsgSpec_TeamInfo( const int player_index, const string& team_id ) + { + MESSAGE_BEGIN( MSG_SPEC, g_msgTeamInfo ); + WRITE_BYTE( player_index ); + WRITE_STRING( team_id.c_str() ); + MESSAGE_END(); + } + + //MESSAGE TO ONE PERSON + void NetMsg_TeamInfo( entvars_t* const pev, const int player_index, const string& team_id ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgTeamInfo, NULL, pev ); + WRITE_BYTE( player_index ); + WRITE_STRING( team_id.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_TeamNames( void* const buffer, const int size, StringList& team_names ) + { + team_names.clear(); + BEGIN_READ( buffer, size ); + int num_teams = READ_BYTE(); + for( int counter = 0; counter < num_teams; counter++ ) + { + string name(READ_STRING()); + team_names.push_back(name); + } + END_READ(); + } +#else + void NetMsg_TeamNames( entvars_t* const pev, const StringList& team_names ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgTeamNames, NULL, pev ); + WRITE_BYTE( team_names.size() ); + for( int counter = 0; counter < team_names.size(); counter++ ) + { + WRITE_STRING( team_names[counter].c_str() ); + } + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_TeamScore( void* const buffer, const int size, string& team_name, int& score, int& reset ) + { + BEGIN_READ( buffer, size ); + team_name = READ_STRING(); + score = READ_SHORT(); + reset = READ_SHORT(); + END_READ(); + } +#else + void NetMsg_TeamScore( entvars_t* const pev, const string& team_name, const int score, const int reset ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgTeamScore, NULL, pev ); + WRITE_STRING( team_name.c_str() ); + WRITE_SHORT( score ); + WRITE_SHORT( reset ); + MESSAGE_END(); + } + + void NetMsg_TeamScore( const string& team_name, const int score, const int reset ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgTeamScore ); + WRITE_STRING( team_name.c_str() ); + WRITE_SHORT( score ); + WRITE_SHORT( reset ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_TextMsg( void* const buffer, const int size, int& destination, StringList& message ) + { + message.clear(); + BEGIN_READ( buffer, size ); + destination = READ_BYTE(); + message.push_back( string( READ_STRING() ) ); + message.push_back( string( READ_STRING() ) ); + message.push_back( string( READ_STRING() ) ); + message.push_back( string( READ_STRING() ) ); + message.push_back( string( READ_STRING() ) ); + END_READ(); + } +#else + //MESSAGE TO EVERYBODY + void NetMsg_TextMsg( const int destination, const StringList& message ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgTextMsg ); + WRITE_BYTE( destination ); + WRITE_STRING( message[0].c_str() ); + if( message.size() > 1 ) + WRITE_STRING( message[1].c_str() ); + if( message.size() > 2 ) + WRITE_STRING( message[2].c_str() ); + if( message.size() > 3 ) + WRITE_STRING( message[3].c_str() ); + if( message.size() > 4 ) + WRITE_STRING( message[4].c_str() ); + MESSAGE_END(); + } + + //MESSAGE TO SPECTATORS + void NetMsgSpec_TextMsg( const int destination, const StringList& message ) + { + MESSAGE_BEGIN( MSG_SPEC, g_msgTextMsg ); + WRITE_BYTE( destination ); + WRITE_STRING( message[0].c_str() ); + if( message.size() > 1 ) + WRITE_STRING( message[1].c_str() ); + if( message.size() > 2 ) + WRITE_STRING( message[2].c_str() ); + if( message.size() > 3 ) + WRITE_STRING( message[3].c_str() ); + if( message.size() > 4 ) + WRITE_STRING( message[4].c_str() ); + MESSAGE_END(); + } + + //MESSAGE TO ONE PERSON + void NetMsg_TextMsg( entvars_t* const pev, const int destination, const StringList& message ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgTextMsg, NULL, pev ); + WRITE_BYTE( destination ); + WRITE_STRING( message[0].c_str() ); + if( message.size() > 1 ) + WRITE_STRING( message[1].c_str() ); + if( message.size() > 2 ) + WRITE_STRING( message[2].c_str() ); + if( message.size() > 3 ) + WRITE_STRING( message[3].c_str() ); + if( message.size() > 4 ) + WRITE_STRING( message[4].c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_Train( void* const buffer, const int size, int& state ) + { + BEGIN_READ( buffer, size ); + state = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_Train( entvars_t* const pev, const int state ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgTrain, NULL, pev ); + WRITE_BYTE( state ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_WeaponList( void* const buffer, const int size, WeaponList& weapon ) + { + BEGIN_READ( buffer, size ); + weapon.weapon_name = READ_STRING(); + weapon.ammo1_type = READ_CHAR(); + weapon.ammo1_max_amnt = READ_BYTE(); + weapon.ammo2_type = READ_CHAR(); + weapon.ammo2_max_amnt = READ_BYTE(); + weapon.bucket = READ_CHAR(); + weapon.bucket_pos = READ_CHAR(); + weapon.bit_index = READ_CHAR(); + weapon.flags = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_WeaponList( entvars_t* const pev, const WeaponList& weapon ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgWeaponList, NULL, pev ); + WRITE_STRING( weapon.weapon_name.c_str() ); + WRITE_CHAR( weapon.ammo1_type ); + WRITE_BYTE( weapon.ammo1_max_amnt ); + WRITE_CHAR( weapon.ammo2_type ); + WRITE_BYTE( weapon.ammo2_max_amnt ); + WRITE_CHAR( weapon.bucket ); + WRITE_CHAR( weapon.bucket_pos ); + WRITE_CHAR( weapon.bit_index ); + WRITE_BYTE( weapon.flags ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_WeapPickup( void* const buffer, const int size, int& weapon_id ) + { + BEGIN_READ( buffer, size ); + weapon_id = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_WeapPickup( entvars_t* const pev , const int weapon_id ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgWeapPickup, NULL, pev ); + WRITE_BYTE( weapon_id ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +enum AlienInfo_ChangeFlags +{ + NO_CHANGE = 0, + COORDS_CHANGED = 1, + STATUS_CHANGED = 2, + HEALTH_CHANGED = 4 +}; + +#ifndef AVH_SERVER + void NetMsg_AlienInfo( void* const buffer, const int size, bool& was_hive_info, AvHAlienUpgradeListType& upgrades, HiveInfoListType& hives ) + { + BEGIN_READ( buffer, size ); + int status, type, header = READ_BYTE(); + was_hive_info = (header & 0x80) == 0; + if( was_hive_info ) + { + int num_hives = header; + AlienInfo_ChangeFlags changes; + for( int counter = 0; counter < num_hives; counter++ ) + { + if( hives.size() <= counter ) + { + AvHHiveInfo hive; + hives.push_back( hive ); + } + + changes = (AlienInfo_ChangeFlags)READ_BYTE(); + if( changes & COORDS_CHANGED ) + { + hives[counter].mPosX = READ_COORD(); + hives[counter].mPosY = READ_COORD(); + hives[counter].mPosZ = READ_COORD(); + } + if( changes & STATUS_CHANGED ) + { + status = READ_BYTE(); + type = (status >> 3) & 0x03; + hives[counter].mUnderAttack = (status & 0x80) != 0; + hives[counter].mStatus = status & 0x07; + switch(type) + { + case 0: hives[counter].mTechnology = MESSAGE_NULL; break; + case 1: hives[counter].mTechnology = ALIEN_BUILD_DEFENSE_CHAMBER; break; + case 2: hives[counter].mTechnology = ALIEN_BUILD_SENSORY_CHAMBER; break; + case 3: hives[counter].mTechnology = ALIEN_BUILD_MOVEMENT_CHAMBER; break; + } + } + if( changes & HEALTH_CHANGED ) + { + hives[counter].mHealthPercentage = READ_BYTE(); + } + } + } + else + { + int num_upgrades = READ_BYTE(); + upgrades.clear(); + for( int counter = 0; counter < num_upgrades; counter++ ) + { + AvHAlienUpgradeCategory theUpgradeCategory = AvHAlienUpgradeCategory(READ_BYTE()); + upgrades.push_back(theUpgradeCategory); + } + } + END_READ(); + } +#else + void NetMsg_AlienInfo_Upgrades( entvars_t* const pev, const AvHAlienUpgradeListType& upgrades ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgAlienInfo, NULL, pev ); + WRITE_BYTE( 0x80 ); //magic number for hive size field, upgrade info + WRITE_BYTE( upgrades.size() ); + AvHAlienUpgradeListType::const_iterator current, end = upgrades.end(); + for( current = upgrades.begin(); current != end; ++current ) + { + WRITE_BYTE( *current ); + } + MESSAGE_END(); + } + + void NetMsg_AlienInfo_Hives( entvars_t* const pev, const HiveInfoListType& hives, const HiveInfoListType& client_hives ) + { + if ( pev == NULL ) + MESSAGE_BEGIN( MSG_SPEC, g_msgAlienInfo); + else + MESSAGE_BEGIN( MSG_ONE, g_msgAlienInfo, NULL, pev ); + WRITE_BYTE( hives.size() ); + HiveInfoListType::const_iterator current, end = hives.end(); + int status, tech, index = 0; + int change_flags = NO_CHANGE; + for( current = hives.begin(); current != end; ++current, ++index ) + { + //put together change bitfield + if( client_hives.size() <= index || client_hives[index].mPosX != current->mPosX || + client_hives[index].mPosY != current->mPosY || client_hives[index].mPosZ != current->mPosZ ) + { change_flags |= COORDS_CHANGED; } + + if( client_hives.size() <= index || client_hives[index].mStatus != current->mStatus || + client_hives[index].mUnderAttack != current->mUnderAttack || client_hives[index].mTechnology != current->mTechnology ) + { change_flags |= STATUS_CHANGED; } + + if( client_hives.size() <= index || client_hives[index].mHealthPercentage != current->mHealthPercentage ) + { change_flags |= HEALTH_CHANGED; } + WRITE_BYTE(change_flags); + + //send change data + if( change_flags & COORDS_CHANGED ) + { + WRITE_COORD(current->mPosX); + WRITE_COORD(current->mPosY); + WRITE_COORD(current->mPosZ); + } + if( change_flags & STATUS_CHANGED ) + { + status = current->mStatus & 0x07; // 3 bits + switch( current->mTechnology ) // 2 bits + { + case MESSAGE_NULL: tech = 0; break; + case ALIEN_BUILD_DEFENSE_CHAMBER: tech = 1; break; + case ALIEN_BUILD_SENSORY_CHAMBER: tech = 2; break; + case ALIEN_BUILD_MOVEMENT_CHAMBER: tech = 3; break; + default: tech = 0; break; + } + status |= tech << 3; + status |= current->mUnderAttack ? 0x80 : 0x00; // 1 bit + WRITE_BYTE(status); + } + if( change_flags & HEALTH_CHANGED ) + { + WRITE_BYTE(current->mHealthPercentage); + } + } + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//TODO: sub rapid conversion as described in Game Programming Gems... +union float_converter +{ + float f; + long l; +}; + +#ifndef AVH_SERVER + void NetMsg_BalanceVar( void* const buffer, const int size, string& name, BalanceMessageAction& action, int& ivalue, float& fvalue, string& svalue ) + { + BEGIN_READ( buffer, size ); + action = static_cast(READ_BYTE()); + switch( action ) + { + case BALANCE_ACTION_INSERT_INT: + { + name = READ_STRING(); + ivalue = READ_LONG(); + break; + } + case BALANCE_ACTION_INSERT_FLOAT: + { + float_converter c; + name = READ_STRING(); + c.l = READ_LONG(); + fvalue = c.f; + break; + } + case BALANCE_ACTION_INSERT_STRING: + { + name = READ_STRING(); + svalue = READ_STRING(); + break; + } + case BALANCE_ACTION_REMOVE: + { + name = READ_STRING(); + break; + } + case BALANCE_ACTION_CLEAR: + case BALANCE_ACTION_NOTIFY_PENDING: + case BALANCE_ACTION_NOTIFY_FINISHED: + { + break; + } + default: + break; + //todo: error condition here? + } + END_READ(); + } +#else + void NetMsg_BalanceVarChangesPending( entvars_t* const pev, const bool pending ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); + WRITE_BYTE( pending ? BALANCE_ACTION_NOTIFY_PENDING : BALANCE_ACTION_NOTIFY_FINISHED ); + MESSAGE_END(); + } + + void NetMsg_BalanceVarInsertInt( entvars_t* const pev, const string& name, const int data ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); + WRITE_BYTE( BALANCE_ACTION_INSERT_INT ); + WRITE_STRING( name.c_str() ); + WRITE_LONG( data ); + MESSAGE_END(); + } + + void NetMsg_BalanceVarInsertFloat( entvars_t* const pev, const string& name, const float data ) + { + float_converter c; + c.f = data; + MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); + WRITE_BYTE( BALANCE_ACTION_INSERT_FLOAT ); + WRITE_STRING( name.c_str() ); + WRITE_LONG( c.l ); + MESSAGE_END(); + } + + void NetMsg_BalanceVarInsertString( entvars_t* const pev, const string& name, const string& data ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); + WRITE_BYTE( BALANCE_ACTION_INSERT_STRING ); + WRITE_STRING( name.c_str() ); + WRITE_STRING( data.c_str() ); + MESSAGE_END(); + } + + void NetMsg_BalanceVarRemove( entvars_t* const pev, const string& name ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); + WRITE_BYTE( BALANCE_ACTION_REMOVE ); + WRITE_STRING( name.c_str() ); + MESSAGE_END(); + } + + void NetMsg_BalanceVarClear( entvars_t* const pev ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBalanceVar, NULL, pev ); + WRITE_BYTE( BALANCE_ACTION_CLEAR ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_BlipList( void* const buffer, const int size, bool& friendly_blips, AvHVisibleBlipList& list ) + { + int blip_count; + int8 status, info; + float X, Y, Z; + list.Clear(); + BEGIN_READ( buffer, size ); + int list_info = READ_BYTE(); + friendly_blips = (list_info & 0x80) != 0; + blip_count = (list_info & 0x7F); + for( int counter = 0; counter < blip_count; counter++ ) + { + X = READ_COORD(); + Y = READ_COORD(); + Z = READ_COORD(); + status = READ_BYTE(); + info = friendly_blips ? READ_BYTE() : 0; + list.AddBlip( X, Y, Z, status, info ); + } + END_READ(); + } +#else + void NetMsg_BlipList( entvars_t* const pev, const bool friendly_blips, const AvHVisibleBlipList& list ) + { + int maxBlips = friendly_blips ? 20 : 25; + maxBlips = min ( list.mNumBlips, maxBlips ); + + MESSAGE_BEGIN( MSG_ONE_UNRELIABLE, g_msgBlipList, NULL, pev ); + //pack header - 7 bits for blip count (doesn't go over 40 in practice), 1 bit for Friend or Foe + unsigned char list_info = maxBlips | (friendly_blips ? 0x80 : 0); + WRITE_BYTE( list_info ); + //pack each blip - this could be optimized as follows once bit packer is implemented: + // convert X, Y to integer values ranging from 0 to 2047 (11 bits each) based on map extents + // convert Z to integer value ranging from 0 to 511 (9 bits) + // 4 bits for status (range 0-15, 1-9 currently used) + // 5 bits for info (range 1-32, refers to player number) + // total is 40 bits = 5 bytes for friendly, 35 bits for foe. + // savings would be 37.5% for friendly bytes. + // blip precision would be equal to double large minimap precision, with worst case of 4 unit X,Y separation for MT. + // because maps are much smaller vertically than horizontally as a rule, the worst case of 16 unit Z separation + // will very rarely occur. + for( int counter = 0; counter < maxBlips; counter++ ) + { + WRITE_COORD( list.mBlipPositions[counter][0] ); + WRITE_COORD( list.mBlipPositions[counter][1] ); + WRITE_COORD( list.mBlipPositions[counter][2] ); + WRITE_BYTE( list.mBlipStatus[counter] ); + if( friendly_blips ) { WRITE_BYTE( list.mBlipInfo[counter] ); } + } + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_BuildMiniMap( void* const buffer, const int size, string& name, int& num_samples, int& processed_samples, int& width, int& height, uint8** map, bool& finished ) + { + BEGIN_READ( buffer, size ); + switch( READ_BYTE() ) + { + case 0: + name = READ_STRING(); + num_samples = READ_LONG(); + processed_samples = 0; + width = READ_LONG(); + height = READ_LONG(); + *map = new uint8[num_samples]; + finished = false; + break; + case 1: + { + int packet_samples = READ_BYTE(); + for( int counter = 0; counter < packet_samples; counter++ ) + { + (*map)[processed_samples++] = READ_BYTE(); + } + finished = false; + break; + } + case 2: + finished = true; + break; + } + END_READ(); + } +#else + void NetMsg_BuildMiniMap_Initialize( entvars_t* const pev, const string& name, const int num_samples, const int width, const int height ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBuildMiniMap, NULL, pev ); + WRITE_BYTE( 0 ); + WRITE_STRING( name.c_str() ); + WRITE_LONG( num_samples ); + WRITE_LONG( width ); + WRITE_LONG( height ); + MESSAGE_END(); + } + + void NetMsg_BuildMiniMap_Update( entvars_t* const pev, const int num_samples, const uint8* const samples ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBuildMiniMap, NULL, pev ); + WRITE_BYTE( 1 ); + WRITE_BYTE( num_samples ); + for( int counter = 0; counter < num_samples; counter++ ) + { + WRITE_BYTE( samples[counter] ); + } + MESSAGE_END(); + } + + void NetMsg_BuildMiniMap_Complete( entvars_t* const pev ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgBuildMiniMap, NULL, pev ); + WRITE_BYTE( 2 ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ClientScripts( void* const buffer, const int size, StringList& script_names ) + { + script_names.clear(); + BEGIN_READ( buffer, size ); + int num_scripts = READ_BYTE(); + while( script_names.size() < num_scripts ) + { + script_names.push_back( string( READ_STRING() ) ); + } + END_READ(); + } +#else + void NetMsg_ClientScripts( entvars_t* const pev, const StringList& script_names ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgClientScripts, NULL, pev ); + WRITE_BYTE( script_names.size() ); + StringList::const_iterator current, end = script_names.end(); + for( current = script_names.begin(); current != end; ++current ) + { + WRITE_STRING( current->c_str() ); + } + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_DebugCSP( void* const buffer, const int size, weapon_data_t& weapon_data, float& next_attack ) + { + BEGIN_READ( buffer, size ); + weapon_data.m_iId = READ_LONG(); + weapon_data.m_iClip = READ_LONG(); + weapon_data.m_flNextPrimaryAttack = READ_COORD(); + weapon_data.m_flTimeWeaponIdle = READ_COORD(); + next_attack = READ_COORD(); + END_READ(); + } +#else + void NetMsg_DebugCSP( entvars_t* const pev, const weapon_data_t& weapon_data, const float next_attack ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgDebugCSP, NULL, pev ); + WRITE_LONG( weapon_data.m_iId ); + WRITE_LONG( weapon_data.m_iClip ); + WRITE_COORD( weapon_data.m_flNextPrimaryAttack ); + WRITE_COORD( weapon_data.m_flTimeWeaponIdle ); + WRITE_COORD( next_attack ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_EditPS( void* const buffer, const int size, int& particle_index ) + { + BEGIN_READ( buffer, size ); + particle_index = READ_SHORT(); + END_READ(); + } +#else + void NetMsg_EditPS( entvars_t* const pev, const int particle_index ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgEditPS, NULL, pev ); + WRITE_SHORT( particle_index ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_Fog( void* const buffer, const int size, bool& enabled, int& R, int& G, int& B, float& start, float& end ) + { + BEGIN_READ( buffer, size ); + enabled = (READ_BYTE() != 0); + if( enabled ) + { + R = READ_BYTE(); + G = READ_BYTE(); + B = READ_BYTE(); + start = READ_COORD(); + end = READ_COORD(); + } + END_READ(); + } +#else + void NetMsg_Fog( entvars_t* const pev, const bool enabled, const int R, const int G, const int B, const float start, const float end ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgFog, NULL, pev ); + WRITE_BYTE( enabled ? 1 : 0 ); + if( enabled ) + { + WRITE_BYTE( R ); + WRITE_BYTE( G ); + WRITE_BYTE( B ); + WRITE_COORD( start ); + WRITE_COORD( end ); + } + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_GameStatus( void* const buffer, const int size, int& status_code, AvHMapMode& map_mode, int& game_time, int& timelimit, int& misc_data ) + { + BEGIN_READ( buffer, size ); + status_code = READ_BYTE(); + map_mode = (AvHMapMode)READ_BYTE(); + switch( status_code ) + { + case kGameStatusReset: + case kGameStatusResetNewMap: + case kGameStatusEnded: + break; + case kGameStatusGameTime: + game_time = READ_SHORT(); + timelimit = READ_SHORT(); + misc_data = READ_BYTE(); + break; + case kGameStatusUnspentLevels: + misc_data = READ_BYTE(); + break; + } + END_READ(); + } +#else + void NetMsg_GameStatus_State( const int status_code, const int map_mode ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgGameStatus ); + WRITE_BYTE( status_code ); + WRITE_BYTE( map_mode ); + MESSAGE_END(); + } + + void NetMsg_GameStatus_State( entvars_t* const pev, const int status_code, const int map_mode ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgGameStatus, NULL, pev ); + WRITE_BYTE( status_code ); + WRITE_BYTE( map_mode ); + MESSAGE_END(); + } + + void NetMsg_GameStatus_Time( const int status_code, const int map_mode, const int game_time, const int timelimit, const int attacking_team_number, const bool is_reliable ) + { + int message_type = is_reliable ? MSG_ALL : MSG_BROADCAST; + MESSAGE_BEGIN( message_type, g_msgGameStatus ); + WRITE_BYTE( status_code ); + WRITE_BYTE( map_mode ); + WRITE_SHORT( game_time ); + WRITE_SHORT( timelimit ); + WRITE_BYTE( attacking_team_number ); + MESSAGE_END(); + } + + void NetMsg_GameStatus_UnspentLevels( entvars_t* const pev, const int status_code, const int map_mode, const int unspent_levels ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgGameStatus, NULL, pev ); + WRITE_BYTE( status_code ); + WRITE_BYTE( map_mode ); + WRITE_BYTE( unspent_levels ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ListPS( void* const buffer, const int size, string& system_name ) + { + BEGIN_READ( buffer, size ); + system_name = READ_STRING(); + END_READ(); + } +#else + void NetMsg_ListPS( entvars_t* const pev, const string& system_name ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgListPS, NULL, pev ); + WRITE_STRING( system_name.c_str() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_HUDSetUpgrades( void* const buffer, const int size, int& upgradeMask ) + { + BEGIN_READ( buffer, size ); + upgradeMask=READ_BYTE(); + END_READ(); + } +#else + void NetMsg_HUDSetUpgrades( int upgradeMask ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgHUDSetUpgrades); + WRITE_BYTE( upgradeMask ); + MESSAGE_END(); + } + void NetMsg_HUDSetUpgrades( entvars_t* const pev, int upgradeMask ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgHUDSetUpgrades, NULL, pev ); + WRITE_BYTE( upgradeMask ); + MESSAGE_END(); + } +#endif + +#ifndef AVH_SERVER + void NetMsg_PlayHUDNotification( void* const buffer, const int size, int& flags, int& sound, float& location_x, float& location_y ) + { + BEGIN_READ( buffer, size ); + flags = READ_BYTE(); + sound = READ_BYTE(); + location_x = READ_COORD(); + location_y = READ_COORD(); + END_READ(); + } +#else + void NetMsg_PlayHUDNotification( entvars_t* const pev, const int flags, const int sound, const float location_x, const float location_y ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgPlayHUDNotification, NULL, pev ); + WRITE_BYTE( flags ); + WRITE_BYTE( sound ); + WRITE_COORD( location_x ); + WRITE_COORD( location_y ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ProgressBar( void* const buffer, const int size, int& entity_number, int& progress, int& seconds) + { + BEGIN_READ( buffer, size ); + entity_number = READ_SHORT(); + progress = READ_BYTE(); + if ( progress == 5 ) + seconds=READ_BYTE(); + + END_READ(); + } +#else + void NetMsg_ProgressBar( entvars_t* const pev, const int entity_number, const int progress, int seconds ) + { + MESSAGE_BEGIN( MSG_ONE_UNRELIABLE, g_msgProgressBar, NULL, pev ); + WRITE_SHORT( entity_number ); + WRITE_BYTE( progress ); + if ( progress == 5 ) + WRITE_BYTE( seconds ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_ServerVar( void* const buffer, const int size, string& name, int& value ) + { + BEGIN_READ( buffer, size ); + name = READ_STRING(); + value = READ_SHORT(); + END_READ(); + } +#else + void NetMsg_ServerVar( entvars_t* const pev, const string& name, const int& value ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgServerVar, NULL, pev ); + WRITE_STRING( name.c_str() ); + WRITE_SHORT( value ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetGammaRamp( void* const buffer, const int size, float& gamma ) + { + BEGIN_READ( buffer, size ); + gamma = READ_BYTE() / 128.0f; + END_READ(); + } +#else + void NetMsg_SetGammaRamp( entvars_t* const pev, const float gamma ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetGammaRamp, NULL, pev ); + WRITE_BYTE( floor( min( max(gamma, 0.0f), 1.992f) * 128.0f) ); + MESSAGE_END(); + } + + void NetMsgSpec_SetGammaRamp( const float gamma ) + { + MESSAGE_BEGIN( MSG_SPEC, g_msgSetGammaRamp ); + WRITE_BYTE( floor( min( max(gamma, 0.0f), 1.992f) * 128.0f) ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetOrder( void* const buffer, const int size, AvHOrder& order ) + { + EntityListType players; + BEGIN_READ( buffer, size ); + order.SetReceiver( READ_BYTE() ); + order.SetOrderType( (AvHOrderType)READ_BYTE() ); + //order.SetOrderTargetType((AvHOrderTargetType)READ_BYTE()); //this is a redundant byte because SetOrderType automatically sets the target type as well. + switch( order.GetOrderTargetType() ) + { + case ORDERTARGETTYPE_LOCATION: + { + vec3_t location; + location.x = READ_COORD(); + location.y = READ_COORD(); + location.z = READ_COORD(); + order.SetLocation( location ); + break; + } + case ORDERTARGETTYPE_TARGET: + order.SetTargetIndex( READ_SHORT() ); + break; + } + order.SetUser3TargetType( (AvHUser3)READ_BYTE() ); + order.SetOrderCompleted( READ_BYTE() ); + // : 1050 + // Need to sync the order status as it is only manipulated by the serverside state machine + order.SetOrderStatus( READ_BYTE() ); + END_READ(); + } +#else + void NetMsg_SetOrder( entvars_t* const pev, const AvHOrder& order ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetOrder, NULL, pev ); + WRITE_BYTE( order.GetReceiver() ); + WRITE_BYTE( order.GetOrderType() ); + //WRITE_BYTE( order.GetOrderTargetType() ); //this is a redundant byte because SetOrderType automatically sets the target type as well. + switch( order.GetOrderTargetType() ) + { + case ORDERTARGETTYPE_LOCATION: + { + vec3_t location; + order.GetLocation( location ); + WRITE_COORD( location.x ); + WRITE_COORD( location.y ); + WRITE_COORD( location.z ); + break; + } + case ORDERTARGETTYPE_TARGET: + WRITE_SHORT( order.GetTargetIndex() ); + break; + } + WRITE_BYTE( order.GetTargetUser3Type() ); + WRITE_BYTE( order.GetOrderCompleted() ); + // : 1050 + // Need to sync the order status as it is only manipulated by the serverside state machine + WRITE_BYTE( order.GetOrderStatus() ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_DelParts( void* const buffer, const int size) + { + BEGIN_READ( buffer, size ); + READ_BYTE(); + END_READ(); + } + void NetMsg_SetParticleTemplate( void* const buffer, const int size, int &index, AvHParticleTemplate& particle_template ) + { + ParticleParams gen_params, vel_params; + PSVector gravity; + BEGIN_READ( buffer, size ); + index=READ_SHORT(); + particle_template.SetName( string( READ_STRING() ) ); + particle_template.SetMaxParticles( READ_LONG() ); + particle_template.SetParticleSize( READ_COORD() ); + particle_template.SetSprite( string( READ_STRING() ) ); + particle_template.SetParticleSystemLifetime( READ_COORD() ); + particle_template.SetParticleLifetime( READ_COORD() ); + particle_template.SetAnimationSpeed( READ_COORD() ); + particle_template.SetNumSpriteFrames( READ_BYTE() ); + particle_template.SetParticleScaling( READ_COORD() ); + particle_template.SetRenderMode( READ_BYTE() ); + particle_template.SetGenerationRate( READ_LONG() ); + particle_template.SetGenerationShape( READ_BYTE() ); + for( int counter = 0; counter < 8; counter++ ) { gen_params[counter] = READ_LONG(); } + particle_template.SetGenerationParams( gen_params ); + particle_template.SetGenerationEntityIndex( READ_LONG() ); + particle_template.SetGenerationEntityParameter( READ_COORD() ); + particle_template.SetStartingVelocityShape( READ_BYTE() ); + for( int counter = 0; counter < 8; counter++ ) { vel_params[counter] = READ_LONG(); } + particle_template.SetStartingVelocityParams( vel_params ); + for( int counter = 0; counter < 3; counter++ ) { gravity[counter] = READ_COORD(); } + particle_template.SetGravity( gravity ); + particle_template.SetMaxAlpha( READ_COORD() ); + particle_template.SetParticleSystemIndexToGenerate( READ_LONG() ); + particle_template.SetFlags( READ_LONG() ); + END_READ(); + } +#else + void NetMsg_SetParticleTemplate( entvars_t* const pev, const int index, const AvHParticleTemplate& particle_template ) + { + ParticleParams gen_params, vel_params; + PSVector gravity; + if ( pev ) + MESSAGE_BEGIN( MSG_ONE, g_msgSetParticleTemplates, NULL, pev ); + else + MESSAGE_BEGIN( MSG_SPEC, g_msgSetParticleTemplates); + WRITE_SHORT(index); + WRITE_STRING( particle_template.GetName().c_str() ); + WRITE_LONG( particle_template.GetMaxParticles() ); + WRITE_COORD( particle_template.GetParticleSize() ); + WRITE_STRING( particle_template.GetSprite().c_str() ); + WRITE_COORD( particle_template.GetParticleSystemLifetime() ); + WRITE_COORD( particle_template.GetParticleLifetime() ); + WRITE_COORD( particle_template.GetAnimationSpeed() ); + WRITE_BYTE( particle_template.GetNumSpriteFrames() ); + WRITE_COORD( particle_template.GetParticleScaling() ); + WRITE_BYTE( particle_template.GetRenderMode() ); + WRITE_LONG( particle_template.GetGenerationRate() ); + WRITE_BYTE( particle_template.GetGenerationShape() ); + particle_template.GetGenerationParams( gen_params ); + for( int counter = 0; counter < 8; counter++ ) { WRITE_LONG( gen_params[counter] ); } + WRITE_LONG( particle_template.GetGenerationEntityIndex() ); + WRITE_COORD( particle_template.GetGenerationEntityParameter() ); + WRITE_BYTE( particle_template.GetStartingVelocityShape() ); + particle_template.GetStartingVelocityParams( vel_params ); + for( int counter = 0; counter < 8; counter++ ) { WRITE_LONG( vel_params[counter] ); } + particle_template.GetGravity( gravity ); + for( int counter = 0; counter < 3; counter++ ) { WRITE_COORD( gravity[counter] ); } + WRITE_COORD( particle_template.GetMaxAlpha() ); + WRITE_LONG( particle_template.GetParticleSystemIndexToGenerate() ); + WRITE_LONG( particle_template.GetFlags() ); + MESSAGE_END(); + } + void NetMsg_DelParts( entvars_t* const pev ) + { + if ( pev ) + MESSAGE_BEGIN( MSG_ONE, g_msgDelParts, NULL, pev ); + else + MESSAGE_BEGIN( MSG_SPEC, g_msgDelParts); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetSelect( void* const buffer, const int size, Selection& selection ) + { + selection.selected_entities.clear(); + BEGIN_READ( buffer, size ); + selection.group_number = READ_BYTE(); + int num_entities = READ_BYTE(); + for(int counter = 0; counter < num_entities; counter++ ) + { selection.selected_entities.push_back( READ_SHORT() ); } + switch( selection.group_number ) + { + case 0: + selection.tracking_entity = (READ_BYTE() == 0) ? 0 : READ_SHORT(); + break; + case kSelectAllHotGroup: + break; + default: + selection.group_type = (AvHUser3)READ_BYTE(); + selection.group_alert = (AvHAlertType)READ_BYTE(); + break; + } + END_READ(); + } +#else + void NetMsg_SetSelect( entvars_t* const pev, Selection& selection ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetSelect, NULL, pev ); + WRITE_BYTE( selection.group_number ); + WRITE_BYTE( selection.selected_entities.size() ); + EntityListType::const_iterator current, end = selection.selected_entities.end(); + for( current = selection.selected_entities.begin(); current != end; ++current ) + { WRITE_SHORT( *current ); } + switch( selection.group_number ) + { + case 0: + if( selection.tracking_entity != 0 ) + { + WRITE_BYTE( 1 ); + WRITE_SHORT( selection.tracking_entity ); + } + else + { + WRITE_BYTE( 0 ); + } + break; + case kSelectAllHotGroup: + break; + default: + WRITE_BYTE( selection.group_type ); + WRITE_BYTE( selection.group_alert ); + break; + } + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetRequest( void* const buffer, const int size, int& request_type, int& request_count ) + { + BEGIN_READ( buffer, size ); + request_type = READ_BYTE(); + request_count = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_SetRequest( entvars_t* pev, const int request_type, const int request_count ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetRequest, NULL, pev ); + WRITE_BYTE( request_type ); + WRITE_BYTE( request_count ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetSoundNames( void* const buffer, const int size, bool& reset, string& sound_name ) + { + BEGIN_READ( buffer, size ); + reset = (READ_BYTE() != 0 ) ? true : false; + if( !reset ) + { sound_name = READ_STRING(); } + END_READ(); + } +#else + void NetMsg_SetSoundNames( entvars_t* pev, const bool reset, const string& sound_name ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetSoundNames, NULL, pev ); + WRITE_BYTE( reset ? 1 : 0 ); + if( !reset ) + { WRITE_STRING( sound_name.c_str() ); } + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetTechNode( void* const buffer, const int size, AvHTechNode*& node ) + { + BEGIN_READ( buffer, size ); + node = new AvHTechNode( (AvHMessageID)READ_BYTE() ); + node->setTechID( (AvHTechID)READ_BYTE() ); + node->setPrereqTechID1( (AvHTechID)READ_BYTE() ); + node->setPrereqTechID2( (AvHTechID)READ_BYTE() ); + node->setCost( READ_SHORT() ); + node->setBuildTime( READ_SHORT() ); + int flags = READ_BYTE(); + node->setAllowMultiples( (flags & 0x01) != 0 ); + node->setResearchState( (flags & 0x04) != 0 ); + node->setResearchable( (flags & 0x02) != 0 ); + END_READ(); + } +#else + void NetMsg_SetTechNode( entvars_t* pev, const AvHTechNode* node ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetTechNodes, NULL, pev ); + WRITE_BYTE( node->getMessageID() ); + WRITE_BYTE( node->getTechID() ); + WRITE_BYTE( node->getPrereqTechID1() ); + WRITE_BYTE( node->getPrereqTechID2() ); + WRITE_SHORT( node->getCost() ); + WRITE_SHORT( node->getBuildTime() ); + int flags = 0; + if( node->getAllowMultiples() ) { flags |= 0x01; } + if( node->getIsResearchable() ) { flags |= 0x02; } + if( node->getIsResearched() ) { flags |= 0x04; } + WRITE_BYTE( flags ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetTechSlots( void* const buffer, const int size, AvHTechSlots& tech_slots ) + { + BEGIN_READ( buffer, size ); + tech_slots.mUser3 = (AvHUser3)READ_BYTE(); + for( int counter = 0; counter < kNumTechSlots; counter++ ) + { tech_slots.mTechSlots[counter] = (AvHMessageID)READ_BYTE(); } + END_READ(); + } +#else + void NetMsg_SetTechSlots( entvars_t* pev, const AvHTechSlots& tech_slots ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetTechSlots, NULL, pev ); + WRITE_BYTE( tech_slots.mUser3 ); + for( int counter = 0; counter < kNumTechSlots; counter++ ) + { WRITE_BYTE( tech_slots.mTechSlots[counter] ); } + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetTopDown( void* const buffer, const int size, bool& is_menu_tech, bool& is_top_down, float* position, int& tech_slots ) + { + BEGIN_READ( buffer, size ); + is_menu_tech = (READ_BYTE() != 0); + if( is_menu_tech ) + { tech_slots = READ_LONG(); } + else + { + is_top_down = (READ_BYTE() != 0); + position[0] = READ_COORD(); + position[1] = READ_COORD(); + position[2] = READ_COORD(); + } + END_READ(); + } +#else + void NetMsg_SetTopDown_Position( entvars_t* const pev, const bool is_top_down, const float* const position ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetTopDown, NULL, pev ); + WRITE_BYTE( 0 ); + WRITE_BYTE( is_top_down ? 1 : 0 ); + WRITE_COORD( position[0] ); + WRITE_COORD( position[1] ); + WRITE_COORD( position[2] ); + MESSAGE_END(); + } + + void NetMsg_SetTopDown_TechSlots( entvars_t* const pev, const int tech_slots ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetTopDown, NULL, pev ); + WRITE_BYTE( 1 ); + WRITE_LONG( tech_slots ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_SetupMap( void* const buffer, const int size, bool& is_location, string& name, float* min_extents, float* max_extents, bool& draw_background ) + { + BEGIN_READ( buffer, size ); + is_location = (READ_BYTE() != 0); + name = READ_STRING(); + if( is_location ) + { + max_extents[0] = READ_COORD(); + max_extents[1] = READ_COORD(); + min_extents[0] = READ_COORD(); + min_extents[1] = READ_COORD(); + } + else + { + min_extents[2] = READ_COORD(); + max_extents[2] = READ_COORD(); + min_extents[0] = READ_COORD(); + min_extents[1] = READ_COORD(); + max_extents[0] = READ_COORD(); + max_extents[1] = READ_COORD(); + draw_background = (READ_BYTE() != 0); + } + } +#else + void NetMsg_SetupMap_Extents( entvars_t* const pev, const string& name, const float* const min_extents, const float* const max_extents, const bool draw_background ) + { + if ( pev) + MESSAGE_BEGIN( MSG_ONE, g_msgSetupMap, NULL, pev ); + else + MESSAGE_BEGIN( MSG_SPEC, g_msgSetupMap); + WRITE_BYTE( 0 ); + WRITE_STRING( name.c_str() ); + WRITE_COORD( min_extents[2] ); + WRITE_COORD( max_extents[2] ); + WRITE_COORD( min_extents[0] ); + WRITE_COORD( min_extents[1] ); + WRITE_COORD( max_extents[0] ); + WRITE_COORD( max_extents[1] ); + WRITE_BYTE( draw_background ? 1 : 0 ); + MESSAGE_END(); + } + + void NetMsg_SetupMap_Location( entvars_t* const pev, const string& name, const float* const min_extents, const float* const max_extents ) + { + MESSAGE_BEGIN( MSG_ONE, g_msgSetupMap, NULL, pev ); + WRITE_BYTE( 1 ); + WRITE_STRING( name.c_str() ); + WRITE_COORD( max_extents[0] ); + WRITE_COORD( max_extents[1] ); + WRITE_COORD( min_extents[0] ); + WRITE_COORD( min_extents[1] ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifndef AVH_SERVER + void NetMsg_UpdateCountdown( void* const buffer, const int size, int& countdown ) + { + BEGIN_READ( buffer, size ); + countdown = READ_BYTE(); + END_READ(); + } +#else + void NetMsg_UpdateCountdown( const int countdown ) + { + MESSAGE_BEGIN( MSG_ALL, g_msgUpdateCountdown ); + WRITE_BYTE( countdown ); + MESSAGE_END(); + } +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +const int kNumStatusBits = 6; +const int kStatusMask = 0x3F; +const int kNumTeamBits = 2; +const int kTeamMask = 0x03; +const int kNumPositionCoordinateBits = 12; +const int kPositionCoordinateMask = 0xFFF; +const int kPositionCoordinateOffset = 4096; +const float kPositionCoordinateScale = 0.5f; +const int kNumPositionBits = kNumPositionCoordinateBits*2; + +const int kNumSquadBits = 3; +const int kSquadMask = 0x07; +const int kNumAngleBits = 4; +const int kAngleMask = 0x0F; +const int kNumPlayerIndexBits = 6; +const int kPlayerIndexMask = 0x3F; +const int kNumIndexBits = 14; +const int kIndexMask = 0x3FFF; +const int kNumFlagBits = 3; +const int kFlagMask = 0x07; +const int kEntHierFlagPlayer = 0x01; +const int kEntHierFlagDeletion = 0x02; +const int kEntHierFlagUnderAttack = 0x04; + + +#ifndef AVH_SERVER + //TODO : replace OldItems with vector + void ReadEntHier( MapEntityMap& NewItems, EntityListType& OldItems, int short_data, int long_data ); + float UnpackageCoord( const int packaged_coord ); + void NetMsg_DelEntityHierarchy(void* const buffer, const int size ) { + BEGIN_READ( buffer, size ); + END_READ(); + } + void NetMsg_UpdateEntityHierarchy( void* const buffer, const int size, MapEntityMap& NewItems, EntityListType& OldItems ) + { + NewItems.clear(); + OldItems.clear(); + int amnt_read = 0; + int short_data, long_data = 0; + BEGIN_READ( buffer, size ); + while( amnt_read < size ) + { + short_data = READ_SHORT(); + amnt_read += 2; + if( (short_data & kEntHierFlagDeletion) == 0 ) + { + long_data = READ_LONG(); + amnt_read += 4; + } + ReadEntHier( NewItems, OldItems, short_data, long_data ); + } + END_READ(); + } + + void ReadEntHier( MapEntityMap& NewItems, EntityListType& OldItems, int short_data, int long_data ) + { + int flags = short_data & kFlagMask; + short_data >>= kNumFlagBits; + + if( (flags & kEntHierFlagDeletion) == kEntHierFlagDeletion ) // Deletion (player or otherwise) + { + OldItems.push_back( short_data & kIndexMask ); + return; + } + + MapEntity ent; + int index = 0; + + ent.mUnderAttack = ((flags & kEntHierFlagUnderAttack) == kEntHierFlagUnderAttack ); + ent.mUser3 = (AvHUser3)(long_data & kStatusMask); + long_data >>= kNumStatusBits; + ent.mTeam = (AvHTeamNumber)(long_data & kTeamMask); + long_data >>= kNumTeamBits; + ent.mY = UnpackageCoord(long_data & kPositionCoordinateMask); + long_data >>= kNumPositionCoordinateBits; + ent.mX = UnpackageCoord(long_data & kPositionCoordinateMask); + + if( (flags & kEntHierFlagPlayer) == kEntHierFlagPlayer ) // Player added/changed + { + index = short_data & kPlayerIndexMask; + short_data >>= kNumPlayerIndexBits; + ent.mAngle = (short_data & kAngleMask) * 22.5f; + short_data >>= kNumAngleBits; + ent.mSquadNumber = short_data & kSquadMask; + } + else // Other item added/changed + { + index = short_data & kIndexMask; + short_data >>= kNumIndexBits; + ent.mSquadNumber = 0; + ent.mAngle = 0; + } + + NewItems.insert( MapEntityMap::value_type( index, ent ) ); + } + + float UnpackageCoord( const int packaged_coord ) + { + float returnVal = packaged_coord; + returnVal /= kPositionCoordinateScale; + returnVal -= kPositionCoordinateOffset; + return returnVal; + } + +#else + void WriteEntHier( const int index, const MapEntity& ent, bool delete_flag, int& short_data, int& long_data ); + int PackageCoord( const float coord ); + void NetMsg_DelEntityHierarchy( entvars_t* const pev ) + { + if ( pev == NULL ) { + MESSAGE_BEGIN( MSG_SPEC, g_msgDelEntityHierarchy); + } + else { + MESSAGE_BEGIN( MSG_ONE, g_msgDelEntityHierarchy, NULL, pev ); + } + MESSAGE_END(); + } + + void NetMsg_UpdateEntityHierarchy( entvars_t* const pev, const MapEntityMap& NewItems, const EntityListType& OldItems, bool specMsg ) + { + const int kMaxUpdatesPerPacket = 30; + if( NewItems.empty() && OldItems.empty() ) { return; } //nothing to send! + + MapEntityMap::const_iterator new_current, new_end = NewItems.end(); + MapEntity temp; + EntityListType::const_iterator old_current, old_end = OldItems.end(); + int short_data, long_data, count = 1; + if ( specMsg ) { + MESSAGE_BEGIN( MSG_SPEC, g_msgUpdateEntityHierarchy); + } + else { + MESSAGE_BEGIN( MSG_ONE, g_msgUpdateEntityHierarchy, NULL, pev ); + } + for( new_current = NewItems.begin(); new_current != new_end; ++new_current, ++count ) + { + if( count % kMaxUpdatesPerPacket == 0 ) + { + MESSAGE_END(); + if ( specMsg ) { + MESSAGE_BEGIN( MSG_SPEC, g_msgUpdateEntityHierarchy); + } + else + MESSAGE_BEGIN( MSG_ONE, g_msgUpdateEntityHierarchy, NULL, pev ); + } + WriteEntHier( new_current->first, new_current->second, false, short_data, long_data ); + WRITE_SHORT(short_data); + WRITE_LONG(long_data); + } + for( old_current = OldItems.begin(); old_current != old_end; ++old_current, ++count ) + { + if( count % kMaxUpdatesPerPacket == 0 ) + { + MESSAGE_END(); + if ( specMsg ) { + MESSAGE_BEGIN( MSG_SPEC, g_msgUpdateEntityHierarchy); + } + else + MESSAGE_BEGIN( MSG_ONE, g_msgUpdateEntityHierarchy, NULL, pev ); + } + WriteEntHier( *old_current, temp, true, short_data, long_data ); + WRITE_SHORT(short_data); + } + MESSAGE_END(); + } + + void WriteEntHier( const int index, const MapEntity& ent, bool delete_flag, int& short_data, int& long_data ) + { + if( delete_flag ) + { + ASSERT( (index & ~kIndexMask) == 0 ); + short_data = index; + short_data <<= kNumFlagBits; + ASSERT( (short_data & kFlagMask) == 0 ); + short_data |= kEntHierFlagDeletion; + return; + } + + long_data = PackageCoord(ent.mX); + long_data <<= kNumPositionCoordinateBits; + ASSERT((long_data & kPositionCoordinateMask) == 0); + long_data |= PackageCoord(ent.mY); + long_data <<= kNumTeamBits; + ASSERT((long_data & kTeamMask) == 0); + ASSERT((ent.mTeam & ~kTeamMask) == 0); + long_data |= ent.mTeam & kTeamMask; + long_data <<= kNumStatusBits; + ASSERT((long_data & kStatusMask) == 0); + ASSERT((ent.mUser3 & ~kStatusMask) == 0); + long_data |= ent.mUser3 & kStatusMask; + + + switch( ent.mUser3 ) + { + case AVH_USER3_MARINE_PLAYER: case AVH_USER3_COMMANDER_PLAYER: + case AVH_USER3_ALIEN_PLAYER1: case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: case AVH_USER3_ALIEN_EMBRYO: + case AVH_USER3_HEAVY: + { + ASSERT( (ent.mSquadNumber & ~kSquadMask) == 0 ); + short_data = ent.mSquadNumber; + short_data <<= kNumAngleBits; + int angle = WrapFloat(ent.mAngle,0,360); + angle /= 22.5f; + ASSERT( (short_data & kAngleMask) == 0); + ASSERT( (angle & ~kAngleMask) == 0); + short_data |= angle & kAngleMask; + short_data <<= kNumPlayerIndexBits; + ASSERT( ( short_data & kPlayerIndexMask ) == 0 ); + ASSERT( ( index & ~kPlayerIndexMask ) == 0 ); + short_data |= index & kIndexMask; + short_data <<= kNumFlagBits; + ASSERT( ( short_data & kFlagMask ) == 0 ); + short_data |= kEntHierFlagPlayer; + if ( ent.mUnderAttack ) short_data |= kEntHierFlagUnderAttack; + break; + } + default: + ASSERT( ( index & ~kIndexMask ) == 0 ); + short_data = index & kIndexMask; + short_data <<= kNumFlagBits; + ASSERT( (short_data & kFlagMask) == 0 ); + if ( ent.mUnderAttack ) { + short_data |= kEntHierFlagUnderAttack; + } + } + } + + int PackageCoord( const float coord ) + { + float adjustedCoord = coord; + adjustedCoord += kPositionCoordinateOffset; + adjustedCoord *= kPositionCoordinateScale; + int returnVal = adjustedCoord; + ASSERT( (returnVal & ~kPositionCoordinateMask) == 0); + returnVal &= kPositionCoordinateMask; + return returnVal; + } + +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// : 0000971 +#ifndef AVH_SERVER + void NetMsg_IssueOrder( void* const buffer, const int size, int& ordertype, int& ordersource, int& ordertarget ) + { + BEGIN_READ( buffer, size ); + ordertype = READ_BYTE(); + ordersource = READ_LONG(); + ordertarget = READ_LONG(); + END_READ(); + } +#else + void NetMsg_IssueOrder( entvars_t* const pev, const int ordertype, const int ordersource, const int ordertarget) + { + MESSAGE_BEGIN( MSG_ONE, g_msgIssueOrder, NULL, pev ); + WRITE_BYTE( ordertype ); + WRITE_LONG( ordersource ); + WRITE_LONG( ordertarget ); + MESSAGE_END(); + } +#endif +// : diff --git a/main/source/mod/AvHNetworkMessages.h b/main/source/mod/AvHNetworkMessages.h index 916b79b..19070bf 100644 --- a/main/source/mod/AvHNetworkMessages.h +++ b/main/source/mod/AvHNetworkMessages.h @@ -34,11 +34,6 @@ #include "AvHEntityHierarchy.h" #include "../engine/shake.h" #include "../common/weaponinfo.h" -extern "C" { -#include -#include -#include -} //FUNCTION PROTOTYPES #ifdef AVH_SERVER @@ -70,7 +65,7 @@ extern "C" { void NetMsg_StatusValue( entvars_t* const pev, const int location, const int state ); void NetMsg_TeamInfo( entvars_t* const pev, const int player_index, const string& team_id ); void NetMsg_TeamNames( entvars_t* const pev, const vector& team_names ); - void NetMsg_TeamScore( entvars_t* const pev, const string& team_name, const int score, const int deaths ); + void NetMsg_TeamScore( entvars_t* const pev, const string& team_name, const int score, const int reset ); void NetMsg_TextMsg( entvars_t* const pev, const int destination, const vector& message ); void NetMsg_Train( entvars_t* const pev, const int state ); void NetMsg_WeaponList( entvars_t* const pev, const WeaponList& weapon ); @@ -95,12 +90,15 @@ extern "C" { void NetMsg_GameStatus_State( entvars_t* const pev, const int status_code, const int map_mode ); void NetMsg_GameStatus_UnspentLevels( entvars_t* const pev, const int status_code, const int map_mode, const int unspent_levels ); void NetMsg_ListPS( entvars_t* const pev, const string& system_name ); + void NetMsg_HUDSetUpgrades( int upgradeMask ); + void NetMsg_HUDSetUpgrades( entvars_t* const pev, int upgradeMask ); void NetMsg_PlayHUDNotification( entvars_t* const pev, const int flags, const int sound, const float location_x, const float location_y ); - void NetMsg_ProgressBar( entvars_t* const pev, const int entity_number, const int progress ); - void NetMsg_ServerVar( entvars_t* const pev, const string& name, const string& value ); + void NetMsg_ProgressBar( entvars_t* const pev, const int entity_number, const int progress, int percent=0 ); + void NetMsg_ServerVar( entvars_t* const pev, const string& name, const int& value ); void NetMsg_SetGammaRamp( entvars_t* const pev, const float gamma ); void NetMsg_SetOrder( entvars_t* const pev, const AvHOrder& order ); - void NetMsg_SetParticleTemplate( entvars_t* const pev, const AvHParticleTemplate& particle_template ); + void NetMsg_DelParts( entvars_t* const pev); + void NetMsg_SetParticleTemplate( entvars_t* const pev, const int index, const AvHParticleTemplate& particle_template ); void NetMsg_SetRequest( entvars_t* pev, const int request_type, const int request_count ); void NetMsg_SetSelect( entvars_t* const pev, Selection& selection ); void NetMsg_SetSoundNames( entvars_t* pev, const bool reset, const string& sound_name ); @@ -110,9 +108,9 @@ extern "C" { void NetMsg_SetTopDown_TechSlots( entvars_t* const pev, const int tech_slots ); void NetMsg_SetupMap_Extents( entvars_t* const pev, const string& name, const float* const min_extents, const float* const max_extents, const bool draw_background ); void NetMsg_SetupMap_Location( entvars_t* const pev, const string& name, const float* const min_extents, const float* const max_extents ); - void NetMsg_UpdateEntityHierarchy( entvars_t* const pev, const MapEntityMap& NewItems, const EntityListType& OldItems ); + void NetMsg_UpdateEntityHierarchy( entvars_t* const pev, const MapEntityMap& NewItems, const EntityListType& OldItems, bool specMsg); + void NetMsg_DelEntityHierarchy( entvars_t* const pev); void NetMsg_IssueOrder(entvars_t* const pev, const int ordertype, const int ordersource, const int ordertarget); - void NetMsg_LUAMessage(entvars_t* const pev, lua_State *L); //BROADCAST MESSAGE TRANSMISSION void NetMsg_DeathMsg( const int killer_index, const int victim_index, string& weapon_name ); @@ -120,9 +118,10 @@ extern "C" { void NetMsg_GameStatus_Time( const int status_code, const int map_mode, const int game_time, const int timelimit, const int attacking_team_number, const bool is_reliable ); void NetMsg_SayText( const int entity_index, const string& text, const string& location ); void NetMsg_TeamInfo( const int player_index, const string& team_id ); - void NetMsg_TeamScore( const string& team_name, const int score, const int deaths ); + void NetMsg_TeamScore( const string& team_name, const int score, const int reset ); void NetMsg_TextMsg( const int destination, const vector& message ); void NetMsg_ScoreInfo( const ScoreInfo& info ); + void NetMsgSpec_ScoreInfo( const ScoreInfo& info ); void NetMsg_UpdateCountdown( const int countdown ); //SPECTATOR MESSAGE TRANSMISSION @@ -166,7 +165,7 @@ extern "C" { void NetMsg_StatusValue( void* const buffer, const int size, int& location, int& state ); void NetMsg_TeamInfo( void* const buffer, const int size, int& player_index, string& team_id ); void NetMsg_TeamNames( void* const buffer, const int size, vector& team_names ); - void NetMsg_TeamScore( void* const buffer, const int size, string& team_name, int& score, int& deaths ); + void NetMsg_TeamScore( void* const buffer, const int size, string& team_name, int& score, int& reset ); void NetMsg_TextMsg( void* const buffer, const int size, int& destination, vector& message ); //30 void NetMsg_Train( void* const buffer, const int size, int& state ); @@ -184,13 +183,15 @@ extern "C" { void NetMsg_Fog( void* const buffer, const int size, bool& enabled, int& R, int& G, int& B, float& start, float& end ); void NetMsg_GameStatus( void* const buffer, const int size, int& status_code, AvHMapMode& map_mode, int& game_time, int& timelimit, int& misc_data ); void NetMsg_ListPS( void* const buffer, const int size, string& system_name ); + void NetMsg_HUDSetUpgrades( void* const buffer, const int size, int& upgradeMask ); void NetMsg_PlayHUDNotification( void* const buffer, const int size, int& flags, int& sound, float& location_x, float& location_y ); - void NetMsg_ProgressBar( void* const buffer, const int size, int& entity_number, int& progress ); + void NetMsg_ProgressBar( void* const buffer, const int size, int& entity_number, int& progress, int &percent); //45 - void NetMsg_ServerVar( void* const buffer, const int size, string& name, string& value ); + void NetMsg_ServerVar( void* const buffer, const int size, string& name, int& value ); void NetMsg_SetGammaRamp( void* const buffer, const int size, float& gamma ); void NetMsg_SetOrder( void* const buffer, const int size, AvHOrder& order ); - void NetMsg_SetParticleTemplate( void* const buffer, const int size, AvHParticleTemplate& particle_template ); + void NetMsg_SetParticleTemplate( void* const buffer, const int size, int &index, AvHParticleTemplate& particle_template ); + void NetMsg_DelParts( void* const buffer, const int size); void NetMsg_SetRequest( void* const buffer, const int size, int& request_type, int& request_count ); //50 void NetMsg_SetSelect( void* const buffer, const int size, Selection& selection ); @@ -202,9 +203,8 @@ extern "C" { void NetMsg_SetupMap( void* const buffer, const int size, bool& is_location, string& name, float* min_extents, float* max_extents, bool& draw_background ); void NetMsg_UpdateCountdown( void* const buffer, const int size, int& countdown ); void NetMsg_UpdateEntityHierarchy( void* const buffer, const int size, MapEntityMap& NewItems, EntityListType& OldItems ); + void NetMsg_DelEntityHierarchy( void* const buffer, const int size); void NetMsg_IssueOrder( void* const buffer, const int size, int& ordertype, int& ordersource, int& ordertarget ); - void NetMsg_LUAMessage( void* const buffer, const int size, lua_State *L, int &arguments); - // 60 #endif //AVH_SERVER diff --git a/main/source/mod/AvHNexusClient.cpp b/main/source/mod/AvHNexusClient.cpp index dc64b9e..60660b8 100644 --- a/main/source/mod/AvHNexusClient.cpp +++ b/main/source/mod/AvHNexusClient.cpp @@ -1,7 +1,4 @@ -#ifdef AVH_PLAYTEST_BUILD - #define AVH_NO_NEXUS -#endif - +#define AVH_NO_NEXUS #ifdef AVH_NO_NEXUS #include using std::string; diff --git a/main/source/mod/AvHNexusServer.cpp b/main/source/mod/AvHNexusServer.cpp index dde7460..6a3a772 100644 --- a/main/source/mod/AvHNexusServer.cpp +++ b/main/source/mod/AvHNexusServer.cpp @@ -1,15 +1,12 @@ -#ifdef AVH_PLAYTEST_BUILD - #define AVH_NO_NEXUS -#endif - +#define AVH_NO_NEXUS #ifdef AVH_NO_NEXUS #include using std::string; #include "AvHNexusServer.h" - + #include "AvHServerUtil.h" bool AvHNexus::send(entvars_t* const pev, const unsigned char* data, const unsigned int length) { return false; } bool AvHNexus::recv(entvars_t* const pev, const char* data, const unsigned int length) { return false; } - string AvHNexus::getNetworkID(const edict_t* edict) { return ""; } + string AvHNexus::getNetworkID(const edict_t* edict) { return AvHSUGetPlayerAuthIDString((edict_t *)edict); } void AvHNexus::handleUnauthorizedJoinTeamAttempt(const edict_t* edict, const unsigned char team_index) {} void AvHNexus::performSpeedTest(void) {} void AvHNexus::processResponses(void) {} diff --git a/main/source/mod/AvHOrder.cpp b/main/source/mod/AvHOrder.cpp index e6abadd..c4fba76 100644 --- a/main/source/mod/AvHOrder.cpp +++ b/main/source/mod/AvHOrder.cpp @@ -1,771 +1,771 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHOrder.cpp $ -// $Date: 2002/10/16 01:02:16 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHOrder.cpp,v $ -// Revision 1.17 2002/10/16 01:02:16 Flayra -// - Added commander hacking particle effect, but it doesn't seem to play or be visible -// -// Revision 1.16 2002/10/03 18:59:46 Flayra -// - Reworked orders -// -// Revision 1.15 2002/09/25 20:49:04 Flayra -// - Removed use order -// -// Revision 1.14 2002/08/02 21:53:18 Flayra -// - Added type to the order, so help can be displayed on the client -// -// Revision 1.13 2002/07/24 18:45:42 Flayra -// - Linux and scripting changes -// -// Revision 1.12 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHOrder.h" - -#ifdef AVH_CLIENT -#include "cl_dll/cl_util.h" -#include "cl_dll/util_vector.h" -#include "common/const.h" -#include "engine/progdefs.h" -#include "cl_dll/ev_hldm.h" -#include "common/vector_util.h" -#include "common/r_efx.h" -#endif - -#ifdef AVH_SERVER -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "common/vector_util.h" -//#include "mod/AvHSelection.h" -#include "mod/AvHSelectionHelper.h" -#include "dlls/cbase.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHWeldable.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHParticleConstants.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHPlayerUpgrade.h" -#endif - -#include "pm_shared/pm_defs.h" -#include "pm_shared/pm_shared.h" -#include "pm_shared/pm_movevars.h" -#include "pm_shared/pm_debug.h" -#include // NULL -#include // sqrt -#include // strcpy -#include // atoi -#include // isspace -#include "mod/AvHSpecials.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHMessage.h" -#include "util/MathUtil.h" -extern playermove_t *pmove; -#include "mod/AvHSelectionHelper.h" -#include "mod/AvHSharedUtil.h" - -bool AvHOrder::operator==(const AvHOrder& inOrder) const -{ - bool theAreEqual = this->mPlayer == inOrder.mPlayer && this->mOrderType == inOrder.mOrderType; -#ifdef AVH_SERVER - theAreEqual = theAreEqual && this->mTimeOrderCompleted == inOrder.mTimeOrderCompleted && this->mOrderID == inOrder.mOrderID; -#endif - theAreEqual = theAreEqual && this->mLocation == inOrder.mLocation && this->mTargetIndex == inOrder.mTargetIndex && this->mOrderStatus == inOrder.mOrderStatus; - theAreEqual = theAreEqual && this->mOrderTargetType == inOrder.mOrderTargetType && this->mOrderTargetUser3 == inOrder.mOrderTargetUser3; - - return theAreEqual; -} - -bool AvHOrder::operator!=(const AvHOrder& inOrder) const -{ - return !this->operator==(inOrder); -} - -void AvHOrder::operator=(const AvHOrder& inOrder) -{ - this->mPlayer = inOrder.mPlayer; - this->mOrderType = inOrder.mOrderType; - this->mOrderTargetType = inOrder.mOrderTargetType; - this->mOrderTargetUser3 = inOrder.mOrderTargetUser3; - this->mLocation = inOrder.mLocation; - this->mTargetIndex = inOrder.mTargetIndex; - this->mOrderStatus = inOrder.mOrderStatus; -#ifdef AVH_SERVER - this->mTimeOrderCompleted = inOrder.mTimeOrderCompleted; - this->mOrderID = inOrder.mOrderID; -#endif - return; -} - -bool AvHOrder::SetReceiver(const EntityInfo& inPlayer) -{ - this->mPlayer = inPlayer; - return true; -} - -// Shared -AvHOrder::AvHOrder() -{ - this->mPlayer = -1; - this->mOrderType = ORDERTYPEL_DEFAULT; - this->mOrderTargetType = ORDERTARGETTYPE_GLOBAL; - this->mOrderTargetUser3 = AVH_USER3_NONE; - this->mTargetIndex = -1; - this->mOrderStatus = kOrderStatusActive; - - #ifdef AVH_SERVER - this->mTimeOrderCompleted = -1; - this->mOrderID = -1; - #endif -} - -void AvHOrder::ClearReceiver() -{ - this->mPlayer = -1; -} - -int AvHOrder::GetTargetIndex() const -{ - return this->mTargetIndex; -} - - -AvHOrderType AvHOrder::GetOrderType() const -{ - return this->mOrderType; -} - -AvHOrderTargetType AvHOrder::GetOrderTargetType() const -{ - return this->mOrderTargetType; -} - -void AvHOrder::SetOrderTargetType(AvHOrderTargetType inTargetType) -{ - this->mOrderTargetType=inTargetType; -} - - -AvHUser3 AvHOrder::GetTargetUser3Type() const -{ - return this->mOrderTargetUser3; -} - -bool AvHOrder::GetHasReceiver(int inIndex) const -{ - return ( inIndex == this->mPlayer ); -} - -EntityInfo AvHOrder::GetReceiver() const -{ - return this->mPlayer; -} - -void AvHOrder::GetLocation(vec3_t& outLocation) const -{ - if(this->mTargetIndex != -1) - { - #ifdef AVH_SERVER - CBaseEntity* theTargetEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mTargetIndex)); - if(theTargetEntity) - { - VectorCopy(theTargetEntity->pev->origin, outLocation); - } - #endif - - #ifdef AVH_CLIENT - cl_entity_s* theTargetEntity = gEngfuncs.GetEntityByIndex(this->mTargetIndex); - if(theTargetEntity) - { - VectorCopy(theTargetEntity->curstate.origin, outLocation); - } - #endif - } - else - { - VectorCopy(this->mLocation, outLocation); - } -} - -void AvHOrder::GetOrderColor(float& outR, float& outG, float& outB, float& outA) const -{ - switch(this->mOrderType) - { - case ORDERTYPE_UNDEFINED: - default: - outR = outG = outB = outA = 1.0f; - break; - case ORDERTYPEL_MOVE: - outR = 0.0f; outG = 1.0f; outB = 0.0f; outA = .5f; - break; - case ORDERTYPET_ATTACK: - outR = 1.0f; outG = 0.0f; outB = 0.0f; outA = .5f; - break; - case ORDERTYPET_GUARD: - outR = 1.0f; outG = 1.0f; outB = 0.0f; outA = .5f; - break; - case ORDERTYPET_WELD: - outR = 0.0f; outG = 0.0f; outB = 1.0f; outA = .5f; - break; - } -} - - -bool AvHOrder::RemovePlayerFromReceivers(int inIndex) -{ - bool theSuccess = false; - - if ( inIndex == this->mPlayer ) - { - this->mPlayer = -1; - theSuccess=true; - } - - return theSuccess; -} - -void AvHOrder::SetTargetIndex(int inTargetIndex) -{ - this->mTargetIndex = inTargetIndex; -} - -void AvHOrder::SetOrderType(AvHOrderType inType) -{ - this->mOrderType = inType; - - // TODO: Set target type from order type - switch(this->mOrderType) - { - case ORDERTYPEL_MOVE: - case ORDERTYPET_WELD: - default: - this->mOrderTargetType = ORDERTARGETTYPE_LOCATION; - break; - case ORDERTYPET_ATTACK: - case ORDERTYPET_GUARD: - this->mOrderTargetType = ORDERTARGETTYPE_TARGET; - break; - } -} - -void AvHOrder::SetUser3TargetType(AvHUser3 inUser3) -{ - this->mOrderTargetUser3 = inUser3; -} - - -void AvHOrder::SetLocation(const vec3_t& inPosition) -{ - VectorCopy(inPosition, this->mLocation); -} - -bool AvHOrder::GetOrderActive() const -{ - return (this->mOrderStatus == kOrderStatusActive); -} - -bool AvHOrder::GetOrderCancelled() const -{ - return (this->mOrderStatus == kOrderStatusCancelled); -} - -// puzl: 1050 -// Need to sync the order status as it is only manipulated by the serverside state machine -int AvHOrder::GetOrderStatus() const -{ - return this->mOrderStatus; -} -#ifndef AVH_SERVER -void AvHOrder::SetOrderStatus(int inOrderStatus) -{ - this->mOrderStatus=inOrderStatus; -} -#endif - -bool AvHOrder::GetOrderCompleted() const -{ - return (this->mOrderStatus == kOrderStatusComplete); -} - -void AvHOrder::SetOrderCompleted(bool inCompleted) -{ -#ifdef AVH_SERVER - if(inCompleted) - { this->mTimeOrderCompleted = gpGlobals->time; } - else - { this->mTimeOrderCompleted = -1; } -#else - this->mOrderCompleted = inCompleted; -#endif -} - -#ifdef AVH_SERVER -bool AvHOrder::Update() -{ - bool theOrderJustCompleted = false; - - ASSERT(this->GetReceiver() != -1 ); - if(this->GetOrderActive()) - { - bool theOrderIsComplete = false; - AvHPlayer* thePlayer = NULL; - vec3_t theOrderLocation; - this->GetLocation(theOrderLocation); - - EntityInfo theReceiver = this->GetReceiver(); - float theDistance; - const float kMoveToDistance = 90; - const float kPickupDistance = 20; - - CBaseEntity* theTargetEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mTargetIndex)); - AvHBaseBuildable* theTargetBuildable = dynamic_cast(theTargetEntity); - AvHPlayer* theTargetPlayer = dynamic_cast(theTargetEntity); - AvHWeldable* theWeldable = dynamic_cast(theTargetEntity); - switch(this->mOrderType) - { - case ORDERTYPE_UNDEFINED: - default: - break; - - case ORDERTYPEL_MOVE: - // set true if all receivers are within a certain distance of move to order - theTargetPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theReceiver))); - if(theTargetPlayer) - { - theOrderIsComplete = true; - theDistance = VectorDistance(theTargetPlayer->pev->origin, theOrderLocation); - if(!theTargetPlayer->GetIsRelevant() || (theDistance > kMoveToDistance)) - { - theOrderIsComplete = false; - } - } - - if(theOrderIsComplete) - { - this->mOrderStatus = kOrderStatusComplete; - } - break; - - case ORDERTYPET_GET: - // set true if all receivers are within a certain distance of item - theTargetPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theReceiver))); - if(theTargetPlayer) - { - // If one of the players in the group is near enough to pick it up - theDistance = VectorDistance(theTargetPlayer->pev->origin, theOrderLocation); - if(theTargetPlayer->GetIsRelevant() && (theDistance < kPickupDistance)) - { - theOrderIsComplete = true; - } - } - - - // If the item is gone, the order is done - if(!theTargetEntity) - { - this->mOrderStatus = kOrderStatusCancelled; - } - break; - - case ORDERTYPET_ATTACK: - // set true if target is dead or not relevant - if(!theTargetEntity || (theTargetPlayer && !theTargetPlayer->GetIsRelevant())) - { - this->mOrderStatus = kOrderStatusCancelled; - theOrderIsComplete = true; - } - else if(theTargetEntity && !theTargetEntity->IsAlive()) - { - this->mOrderStatus = kOrderStatusComplete; - theOrderIsComplete = true; - } - break; - - case ORDERTYPET_BUILD: - if(!theTargetEntity || !theTargetEntity->IsAlive()) - { - this->mOrderStatus = kOrderStatusCancelled; - theOrderIsComplete = true; - } - else if(theTargetBuildable && theTargetBuildable->GetIsBuilt()) - { - this->mOrderStatus = kOrderStatusComplete; - theOrderIsComplete = true; - } - else - { - if(theTargetEntity) - { - bool theIsBuilding; - bool theIsResearching; - float thePercentage; - - AvHSHUGetBuildResearchState(theTargetEntity->pev->iuser3, theTargetEntity->pev->iuser4, theTargetEntity->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); - if(!theIsBuilding && (thePercentage == 1.0f)) - { - this->mOrderStatus = kOrderStatusComplete; - theOrderIsComplete = true; - } - } - } - break; - - case ORDERTYPET_GUARD: - theOrderIsComplete = false; - - if(!theTargetEntity ||!theTargetEntity->IsAlive()) - { - this->mOrderStatus = kOrderStatusCancelled; - theOrderIsComplete = true; - } - break; - - case ORDERTYPET_WELD: - //ALERT(at_console, "Checking weldables "); - // set true when target is fully welded - if(!theTargetEntity ||!theTargetEntity->IsAlive()) - { - this->mOrderStatus = kOrderStatusCancelled; - theOrderIsComplete = true; - } - if(theWeldable && theWeldable->GetIsWelded()) - { - this->mOrderStatus = kOrderStatusComplete; - theOrderIsComplete = true; - } - else if ( !theWeldable ) - { - if ( theTargetEntity->pev->iuser3 == AVH_USER3_MARINE_PLAYER ) - { - // Players are welded if they have full armour - if ( theTargetEntity->pev->armorvalue == AvHPlayerUpgrade::GetMaxArmorLevel(theTargetEntity->pev->iuser4, (AvHUser3)theTargetEntity->pev->iuser3)) - { - this->mOrderStatus = kOrderStatusComplete; - theOrderIsComplete = true; - } - } - else - { - // Structures are welded if they have full health - if ( theTargetEntity->pev->health == theTargetEntity->pev->max_health ) - { - this->mOrderStatus = kOrderStatusComplete; - theOrderIsComplete = true; - } - } - } - break; - } - - if(theOrderIsComplete) - { - this->SetOrderCompleted(); - theOrderJustCompleted = true; - } - } - - return theOrderJustCompleted; -} - -int AvHOrder::GetOrderID() const -{ - return this->mOrderID; -} - - -void AvHOrder::SetOrderID() -{ - static int sOrderID=0; - this->mOrderID = ++sOrderID; -} - -float AvHOrder::GetTimeOrderCompleted() const -{ - return this->mTimeOrderCompleted; -} - -void AvHOrder::SetTimeOrderCompleted(float inTime) -{ - this->mTimeOrderCompleted = inTime; -} -#endif - -void AvHChangeOrder(OrderListType& inList, const AvHOrder& inOrder) -{ - EntityInfo theReceiver = inOrder.GetReceiver(); - - // Run through list - ASSERT( theReceiver != -1 ); - for(OrderListType::iterator theOrderListIter = inList.begin(); theOrderListIter != inList.end(); /* no increment */) - { - if ( theOrderListIter->GetHasReceiver(theReceiver) ) - { - theOrderListIter = inList.erase(theOrderListIter); - } - else - { - theOrderListIter++; - } - } - - // If the order has any receivers, add it (but we could be deleting it) - if(!inOrder.GetOrderCompleted()) - { - // Add the order on to the end of the list - inList.push_back(inOrder); - } -} - -// Used for context sensitive mouse and for processing right-click -// Must be shared, uses prediction code -// Fill in target index or target point, depending on type of order decided upon -AvHOrderType AvHGetDefaultOrderType(AvHTeamNumber inTeam, const vec3_t& inOrigin, const vec3_t& inNormRay, int& outTargetIndex, vec3_t& outTargetPoint, AvHUser3& outUser3, AvHOrderTargetType& outTargetType) -{ - vec3_t theTraceStart; - vec3_t theTraceEnd; - AvHOrderType theOrderType = ORDERTYPE_UNDEFINED; - -// // Look for a player order -// if(!AvHOrderTracePlayers(inTeam, inOrigin, inNormRay, theOrderType, outTargetIndex)) -// { -// // If couldn't find one, create a non-player order if possible -// //AvHOrderTraceNonPlayers(inTeam, inOrigin, inNormRay, theOrderType, outTargetIndex, outTargetPoint); -// } - - - // Offset a little so we don't hit the commander - VectorMA(inOrigin, kSelectionStartRange, inNormRay, theTraceStart); - VectorMA(inOrigin, kSelectionEndRange, inNormRay, theTraceEnd); - int theFoundIndex = -1; - vec3_t theFoundLocation; - AvHTeamNumber theTeamOfThingHit; - bool thePlayerHit = false; - int theUserThree = 0; - int theUserFour = 0; - - if(AvHSHUTraceTangible(theTraceStart, theTraceEnd, theFoundIndex, theFoundLocation, theTeamOfThingHit, thePlayerHit, theUserThree, theUserFour)) - { - float theHealthPercentage=100.0f; - float theArmorPercentage=100.0f; - bool theSighted=false; -#ifdef AVH_SERVER - CBaseEntity *theEntity=AvHSUGetEntityFromIndex(theFoundIndex); - if ( theEntity ) - { - theHealthPercentage=theEntity->pev->health/theEntity->pev->max_health; - } - else - { - ALERT(at_console, "Not a buildable\n"); - } - - theArmorPercentage = theEntity->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(theEntity->pev->iuser4, (AvHUser3)theEntity->pev->iuser3); - theSighted=GetHasUpgrade(theEntity->pev->iuser4, MASK_VIS_SIGHTED); -#endif - - // Did we hit an enemy? If so, issue an attack order on him, then we're done, it's highest priority - if(thePlayerHit ) - { - // Did we hit a player on our team? If so check for welding, if not guard - if((theTeamOfThingHit == inTeam ) && (theTeamOfThingHit != TEAM_IND) ) - { - theOrderType = ORDERTYPET_GUARD; - if ( theArmorPercentage < 0.90f ) { - theOrderType = ORDERTYPET_WELD; - outTargetType = ORDERTARGETTYPE_TARGET; - } - outTargetIndex = theFoundIndex; - outUser3 = (AvHUser3)theUserThree; - } - } - // Something to pick up? - if(!thePlayerHit ) - { -// if ( ( theTeamOfThingHit ) != inTeam && (theTeamOfThingHit != TEAM_IND) && theSighted ) -// { -// // Use it's center for the height -// VectorCopy(theFoundLocation, outTargetPoint); -// theOrderType = ORDERTYPET_ATTACK; -// outTargetIndex = theFoundIndex; -// outUser3 = (AvHUser3)theUserThree; -// outTargetType = ORDERTARGETTYPE_LOCATION; -// } -// else - if ( theUserThree == AVH_USER3_MARINEITEM) - { - // Use it's center for the height - VectorCopy(theFoundLocation, outTargetPoint); - outTargetIndex = theFoundIndex; - outUser3 = (AvHUser3)theUserThree; - - // We're done - theOrderType = ORDERTYPET_GET; - } - // Buildable? - else if(GetHasUpgrade(theUserFour, MASK_BUILDABLE) && (theTeamOfThingHit == inTeam) && (theTeamOfThingHit != TEAM_IND)) - { - // Use it's center for the height - VectorCopy(theFoundLocation, outTargetPoint); - outTargetIndex = theFoundIndex; - outUser3 = (AvHUser3)theUserThree; - - // We're done - theOrderType = ORDERTYPET_BUILD; - } - // Weldable? - else if(theUserThree == AVH_USER3_WELD ) - { - // Use it's center for the height - VectorCopy(theFoundLocation, outTargetPoint); - outTargetIndex = theFoundIndex; - - // We're done - theOrderType = ORDERTYPET_WELD; - } - // Hit the ground? Move to, we're done - else if(theUserThree == AVH_USER3_WAYPOINT || (( theTeamOfThingHit != inTeam) && !theSighted )) - { - // Use it's center for the height - VectorCopy(theFoundLocation, outTargetPoint); - - // We're done - theOrderType = ORDERTYPEL_MOVE; - } - // Did we hit an entity on our team? Repair/guard it, we're done - else if((theTeamOfThingHit == inTeam) && (theTeamOfThingHit != TEAM_IND)) - { - theOrderType = ORDERTYPET_GUARD; - if ( theHealthPercentage < 0.90f ) { - theOrderType = ORDERTYPET_WELD; - VectorCopy(theFoundLocation, outTargetPoint); - outTargetType = ORDERTARGETTYPE_LOCATION; - } - outTargetIndex = theFoundIndex; - outUser3 = (AvHUser3)theUserThree; - } - } -// else if(!thePlayerHit && (theUserThree == AVH_USER3_USEABLE)) -// { -// // Use it's center for the height -// VectorCopy(theFoundLocation, outTargetPoint); -// outTargetIndex = theFoundIndex; -// -// // We're done -// theOrderType = ORDERTYPEL_USE; -// } - } - - return theOrderType; -} - -#ifdef AVH_SERVER -bool AvHCreateSpecificOrder(AvHTeamNumber inTeam, const vec3_t& inOrigin, AvHOrderType inOrder, const vec3_t& inNormRay, AvHOrder& outOrder) -{ - bool theSuccess = false; - int theTargetIndex = -1; - vec3_t theTargetLocation; - AvHOrderType theOrderType = inOrder; - AvHUser3 theUser3 = AVH_USER3_NONE; - - vec3_t theStartPoint; - VectorMA(inOrigin, kSelectionStartRange, inNormRay, theStartPoint); - - vec3_t theEndPoint; - VectorMA(inOrigin, kSelectionEndRange, inNormRay, theEndPoint); - - vec3_t theValidOrigin; - AvHSHUServerGetFirstNonSolidPoint(theStartPoint, theEndPoint, theValidOrigin); - - AvHOrderTargetType theTargetType=ORDERTARGETTYPE_UNDEFINED; - // Create default order if passed in - if(inOrder == ORDERTYPEL_DEFAULT) - { - theOrderType = AvHGetDefaultOrderType(inTeam, theValidOrigin, inNormRay, theTargetIndex, theTargetLocation, theUser3, theTargetType); - } - - if(theOrderType != ORDERTYPE_UNDEFINED) - { - // Init order with values it found - outOrder.SetOrderType(theOrderType); - outOrder.SetLocation(theTargetLocation); - outOrder.SetTargetIndex(theTargetIndex); - outOrder.SetUser3TargetType(theUser3); - if ( theTargetType != ORDERTARGETTYPE_UNDEFINED ) - outOrder.SetOrderTargetType(theTargetType); - theSuccess = true; - } - - outOrder.SetOrderID(); - - return theSuccess; -} - -bool AvHToggleUseable(CBaseEntity* inUser, const vec3_t& inOrigin, const vec3_t& inNormRay) -{ - bool theSuccess = false; - vec3_t theTraceStart; - vec3_t theTraceEnd; - - // Offset a little so we don't hit the commander - VectorMA(inOrigin, 100, inNormRay, theTraceStart); - - VectorMA(inOrigin, kSelectionEndRange, inNormRay, theTraceEnd); - int theFoundIndex = -1; - vec3_t theFoundLocation; - AvHTeamNumber theTeamOfThingHit; - bool thePlayerHit = false; - int theUserThree = 0; - int theUserFour = 0; - - if(AvHSHUTraceTangible(theTraceStart, theTraceEnd, theFoundIndex, theFoundLocation, theTeamOfThingHit, thePlayerHit, theUserThree, theUserFour)) - { - if(!thePlayerHit && (theUserThree == AVH_USER3_USEABLE)) - { - // Find entity we clicked on, use it - //CBaseEntity* theEntity = CBaseEntity::Instance(ENT(theFoundIndex)); - CBaseEntity* theEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theFoundIndex)); - if(theEntity) - { - // For each entity with this target name (including this one), use it - if(theEntity->pev->targetname) - { - CBaseEntity* theTarget = NULL; - while((theTarget = UTIL_FindEntityByTargetname(theTarget, STRING(theEntity->pev->targetname))) != NULL) - { - int theObjectCaps = theTarget->ObjectCaps(); - if((theObjectCaps & FCAP_IMPULSE_USE) || (FCAP_ONOFF_USE)) - { - theTarget->Use(inUser, inUser, USE_TOGGLE, 0); - } - } - } - else if(FClassnameIs(theEntity->edict(), "func_button") && theEntity->pev->target) ////voogru: Its probably a button!, classname check to prevent any possible exploits - { - CBaseEntity* theTarget = NULL; - while((theTarget = UTIL_FindEntityByTargetname(theTarget, STRING(theEntity->pev->target))) != NULL) - { - int theObjectCaps = theTarget->ObjectCaps(); - if((theObjectCaps & FCAP_IMPULSE_USE) || (FCAP_ONOFF_USE)) - { - theTarget->Use(inUser, inUser, USE_TOGGLE, 0); - } - } - } - theSuccess = true; - } - } - } - return theSuccess; -} +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHOrder.cpp $ +// $Date: 2002/10/16 01:02:16 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHOrder.cpp,v $ +// Revision 1.17 2002/10/16 01:02:16 Flayra +// - Added commander hacking particle effect, but it doesn't seem to play or be visible +// +// Revision 1.16 2002/10/03 18:59:46 Flayra +// - Reworked orders +// +// Revision 1.15 2002/09/25 20:49:04 Flayra +// - Removed use order +// +// Revision 1.14 2002/08/02 21:53:18 Flayra +// - Added type to the order, so help can be displayed on the client +// +// Revision 1.13 2002/07/24 18:45:42 Flayra +// - Linux and scripting changes +// +// Revision 1.12 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHOrder.h" + +#ifdef AVH_CLIENT +#include "cl_dll/cl_util.h" +#include "cl_dll/util_vector.h" +#include "common/const.h" +#include "engine/progdefs.h" +#include "cl_dll/ev_hldm.h" +#include "common/vector_util.h" +#include "common/r_efx.h" +#endif + +#ifdef AVH_SERVER +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "common/vector_util.h" +//#include "mod/AvHSelection.h" +#include "mod/AvHSelectionHelper.h" +#include "dlls/cbase.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHWeldable.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHParticleConstants.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHPlayerUpgrade.h" +#endif + +#include "pm_shared/pm_defs.h" +#include "pm_shared/pm_shared.h" +#include "pm_shared/pm_movevars.h" +#include "pm_shared/pm_debug.h" +#include // NULL +#include // sqrt +#include // strcpy +#include // atoi +#include // isspace +#include "mod/AvHSpecials.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHMessage.h" +#include "util/MathUtil.h" +extern playermove_t *pmove; +#include "mod/AvHSelectionHelper.h" +#include "mod/AvHSharedUtil.h" + +bool AvHOrder::operator==(const AvHOrder& inOrder) const +{ + bool theAreEqual = this->mPlayer == inOrder.mPlayer && this->mOrderType == inOrder.mOrderType; +#ifdef AVH_SERVER + theAreEqual = theAreEqual && this->mTimeOrderCompleted == inOrder.mTimeOrderCompleted && this->mOrderID == inOrder.mOrderID; +#endif + theAreEqual = theAreEqual && this->mLocation == inOrder.mLocation && this->mTargetIndex == inOrder.mTargetIndex && this->mOrderStatus == inOrder.mOrderStatus; + theAreEqual = theAreEqual && this->mOrderTargetType == inOrder.mOrderTargetType && this->mOrderTargetUser3 == inOrder.mOrderTargetUser3; + + return theAreEqual; +} + +bool AvHOrder::operator!=(const AvHOrder& inOrder) const +{ + return !this->operator==(inOrder); +} + +void AvHOrder::operator=(const AvHOrder& inOrder) +{ + this->mPlayer = inOrder.mPlayer; + this->mOrderType = inOrder.mOrderType; + this->mOrderTargetType = inOrder.mOrderTargetType; + this->mOrderTargetUser3 = inOrder.mOrderTargetUser3; + this->mLocation = inOrder.mLocation; + this->mTargetIndex = inOrder.mTargetIndex; + this->mOrderStatus = inOrder.mOrderStatus; +#ifdef AVH_SERVER + this->mTimeOrderCompleted = inOrder.mTimeOrderCompleted; + this->mOrderID = inOrder.mOrderID; +#endif + return; +} + +bool AvHOrder::SetReceiver(const EntityInfo& inPlayer) +{ + this->mPlayer = inPlayer; + return true; +} + +// Shared +AvHOrder::AvHOrder() +{ + this->mPlayer = -1; + this->mOrderType = ORDERTYPEL_DEFAULT; + this->mOrderTargetType = ORDERTARGETTYPE_GLOBAL; + this->mOrderTargetUser3 = AVH_USER3_NONE; + this->mTargetIndex = -1; + this->mOrderStatus = kOrderStatusActive; + + #ifdef AVH_SERVER + this->mTimeOrderCompleted = -1; + this->mOrderID = -1; + #endif +} + +void AvHOrder::ClearReceiver() +{ + this->mPlayer = -1; +} + +int AvHOrder::GetTargetIndex() const +{ + return this->mTargetIndex; +} + + +AvHOrderType AvHOrder::GetOrderType() const +{ + return this->mOrderType; +} + +AvHOrderTargetType AvHOrder::GetOrderTargetType() const +{ + return this->mOrderTargetType; +} + +void AvHOrder::SetOrderTargetType(AvHOrderTargetType inTargetType) +{ + this->mOrderTargetType=inTargetType; +} + + +AvHUser3 AvHOrder::GetTargetUser3Type() const +{ + return this->mOrderTargetUser3; +} + +bool AvHOrder::GetHasReceiver(int inIndex) const +{ + return ( inIndex == this->mPlayer ); +} + +EntityInfo AvHOrder::GetReceiver() const +{ + return this->mPlayer; +} + +void AvHOrder::GetLocation(vec3_t& outLocation) const +{ + if(this->mTargetIndex != -1) + { + #ifdef AVH_SERVER + CBaseEntity* theTargetEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mTargetIndex)); + if(theTargetEntity) + { + VectorCopy(theTargetEntity->pev->origin, outLocation); + } + #endif + + #ifdef AVH_CLIENT + cl_entity_s* theTargetEntity = gEngfuncs.GetEntityByIndex(this->mTargetIndex); + if(theTargetEntity) + { + VectorCopy(theTargetEntity->curstate.origin, outLocation); + } + #endif + } + else + { + VectorCopy(this->mLocation, outLocation); + } +} + +void AvHOrder::GetOrderColor(float& outR, float& outG, float& outB, float& outA) const +{ + switch(this->mOrderType) + { + case ORDERTYPE_UNDEFINED: + default: + outR = outG = outB = outA = 1.0f; + break; + case ORDERTYPEL_MOVE: + outR = 0.0f; outG = 1.0f; outB = 0.0f; outA = .5f; + break; + case ORDERTYPET_ATTACK: + outR = 1.0f; outG = 0.0f; outB = 0.0f; outA = .5f; + break; + case ORDERTYPET_GUARD: + outR = 1.0f; outG = 1.0f; outB = 0.0f; outA = .5f; + break; + case ORDERTYPET_WELD: + outR = 0.0f; outG = 0.0f; outB = 1.0f; outA = .5f; + break; + } +} + + +bool AvHOrder::RemovePlayerFromReceivers(int inIndex) +{ + bool theSuccess = false; + + if ( inIndex == this->mPlayer ) + { + this->mPlayer = -1; + theSuccess=true; + } + + return theSuccess; +} + +void AvHOrder::SetTargetIndex(int inTargetIndex) +{ + this->mTargetIndex = inTargetIndex; +} + +void AvHOrder::SetOrderType(AvHOrderType inType) +{ + this->mOrderType = inType; + + // TODO: Set target type from order type + switch(this->mOrderType) + { + case ORDERTYPEL_MOVE: + case ORDERTYPET_WELD: + default: + this->mOrderTargetType = ORDERTARGETTYPE_LOCATION; + break; + case ORDERTYPET_ATTACK: + case ORDERTYPET_GUARD: + this->mOrderTargetType = ORDERTARGETTYPE_TARGET; + break; + } +} + +void AvHOrder::SetUser3TargetType(AvHUser3 inUser3) +{ + this->mOrderTargetUser3 = inUser3; +} + + +void AvHOrder::SetLocation(const vec3_t& inPosition) +{ + VectorCopy(inPosition, this->mLocation); +} + +bool AvHOrder::GetOrderActive() const +{ + return (this->mOrderStatus == kOrderStatusActive); +} + +bool AvHOrder::GetOrderCancelled() const +{ + return (this->mOrderStatus == kOrderStatusCancelled); +} + +// : 1050 +// Need to sync the order status as it is only manipulated by the serverside state machine +int AvHOrder::GetOrderStatus() const +{ + return this->mOrderStatus; +} +#ifndef AVH_SERVER +void AvHOrder::SetOrderStatus(int inOrderStatus) +{ + this->mOrderStatus=inOrderStatus; +} +#endif + +bool AvHOrder::GetOrderCompleted() const +{ + return (this->mOrderStatus == kOrderStatusComplete); +} + +void AvHOrder::SetOrderCompleted(bool inCompleted) +{ +#ifdef AVH_SERVER + if(inCompleted) + { this->mTimeOrderCompleted = gpGlobals->time; } + else + { this->mTimeOrderCompleted = -1; } +#else + this->mOrderCompleted = inCompleted; +#endif +} + +#ifdef AVH_SERVER +bool AvHOrder::Update() +{ + bool theOrderJustCompleted = false; + + ASSERT(this->GetReceiver() != -1 ); + if(this->GetOrderActive()) + { + bool theOrderIsComplete = false; + AvHPlayer* thePlayer = NULL; + vec3_t theOrderLocation; + this->GetLocation(theOrderLocation); + + EntityInfo theReceiver = this->GetReceiver(); + float theDistance; + const float kMoveToDistance = 90; + const float kPickupDistance = 20; + + CBaseEntity* theTargetEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mTargetIndex)); + AvHBaseBuildable* theTargetBuildable = dynamic_cast(theTargetEntity); + AvHPlayer* theTargetPlayer = dynamic_cast(theTargetEntity); + AvHWeldable* theWeldable = dynamic_cast(theTargetEntity); + switch(this->mOrderType) + { + case ORDERTYPE_UNDEFINED: + default: + break; + + case ORDERTYPEL_MOVE: + // set true if all receivers are within a certain distance of move to order + theTargetPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theReceiver))); + if(theTargetPlayer) + { + theOrderIsComplete = true; + theDistance = VectorDistance(theTargetPlayer->pev->origin, theOrderLocation); + if(!theTargetPlayer->GetIsRelevant() || (theDistance > kMoveToDistance)) + { + theOrderIsComplete = false; + } + } + + if(theOrderIsComplete) + { + this->mOrderStatus = kOrderStatusComplete; + } + break; + + case ORDERTYPET_GET: + // set true if all receivers are within a certain distance of item + theTargetPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theReceiver))); + if(theTargetPlayer) + { + // If one of the players in the group is near enough to pick it up + theDistance = VectorDistance(theTargetPlayer->pev->origin, theOrderLocation); + if(theTargetPlayer->GetIsRelevant() && (theDistance < kPickupDistance)) + { + theOrderIsComplete = true; + } + } + + + // If the item is gone, the order is done + if(!theTargetEntity) + { + this->mOrderStatus = kOrderStatusCancelled; + } + break; + + case ORDERTYPET_ATTACK: + // set true if target is dead or not relevant + if(!theTargetEntity || (theTargetPlayer && !theTargetPlayer->GetIsRelevant())) + { + this->mOrderStatus = kOrderStatusCancelled; + theOrderIsComplete = true; + } + else if(theTargetEntity && !theTargetEntity->IsAlive()) + { + this->mOrderStatus = kOrderStatusComplete; + theOrderIsComplete = true; + } + break; + + case ORDERTYPET_BUILD: + if(!theTargetEntity || !theTargetEntity->IsAlive()) + { + this->mOrderStatus = kOrderStatusCancelled; + theOrderIsComplete = true; + } + else if(theTargetBuildable && theTargetBuildable->GetIsBuilt()) + { + this->mOrderStatus = kOrderStatusComplete; + theOrderIsComplete = true; + } + else + { + if(theTargetEntity) + { + bool theIsBuilding; + bool theIsResearching; + float thePercentage; + + AvHSHUGetBuildResearchState(theTargetEntity->pev->iuser3, theTargetEntity->pev->iuser4, theTargetEntity->pev->fuser1, theIsBuilding, theIsResearching, thePercentage); + if(!theIsBuilding && (thePercentage == 1.0f)) + { + this->mOrderStatus = kOrderStatusComplete; + theOrderIsComplete = true; + } + } + } + break; + + case ORDERTYPET_GUARD: + theOrderIsComplete = false; + + if(!theTargetEntity ||!theTargetEntity->IsAlive()) + { + this->mOrderStatus = kOrderStatusCancelled; + theOrderIsComplete = true; + } + break; + + case ORDERTYPET_WELD: + //ALERT(at_console, "Checking weldables "); + // set true when target is fully welded + if(!theTargetEntity ||!theTargetEntity->IsAlive()) + { + this->mOrderStatus = kOrderStatusCancelled; + theOrderIsComplete = true; + } + if(theWeldable && theWeldable->GetIsWelded()) + { + this->mOrderStatus = kOrderStatusComplete; + theOrderIsComplete = true; + } + else if ( !theWeldable ) + { + if ( theTargetEntity->pev->iuser3 == AVH_USER3_MARINE_PLAYER ) + { + // Players are welded if they have full armour + if ( theTargetEntity->pev->armorvalue == AvHPlayerUpgrade::GetMaxArmorLevel(theTargetEntity->pev->iuser4, (AvHUser3)theTargetEntity->pev->iuser3)) + { + this->mOrderStatus = kOrderStatusComplete; + theOrderIsComplete = true; + } + } + else + { + // Structures are welded if they have full health + if ( theTargetEntity->pev->health == theTargetEntity->pev->max_health ) + { + this->mOrderStatus = kOrderStatusComplete; + theOrderIsComplete = true; + } + } + } + break; + } + + if(theOrderIsComplete) + { + this->SetOrderCompleted(); + theOrderJustCompleted = true; + } + } + + return theOrderJustCompleted; +} + +int AvHOrder::GetOrderID() const +{ + return this->mOrderID; +} + + +void AvHOrder::SetOrderID() +{ + static int sOrderID=0; + this->mOrderID = ++sOrderID; +} + +float AvHOrder::GetTimeOrderCompleted() const +{ + return this->mTimeOrderCompleted; +} + +void AvHOrder::SetTimeOrderCompleted(float inTime) +{ + this->mTimeOrderCompleted = inTime; +} +#endif + +void AvHChangeOrder(OrderListType& inList, const AvHOrder& inOrder) +{ + EntityInfo theReceiver = inOrder.GetReceiver(); + + // Run through list + ASSERT( theReceiver != -1 ); + for(OrderListType::iterator theOrderListIter = inList.begin(); theOrderListIter != inList.end(); /* no increment */) + { + if ( theOrderListIter->GetHasReceiver(theReceiver) ) + { + theOrderListIter = inList.erase(theOrderListIter); + } + else + { + theOrderListIter++; + } + } + + // If the order has any receivers, add it (but we could be deleting it) + if(!inOrder.GetOrderCompleted()) + { + // Add the order on to the end of the list + inList.push_back(inOrder); + } +} + +// Used for context sensitive mouse and for processing right-click +// Must be shared, uses prediction code +// Fill in target index or target point, depending on type of order decided upon +AvHOrderType AvHGetDefaultOrderType(AvHTeamNumber inTeam, const vec3_t& inOrigin, const vec3_t& inNormRay, int& outTargetIndex, vec3_t& outTargetPoint, AvHUser3& outUser3, AvHOrderTargetType& outTargetType) +{ + vec3_t theTraceStart; + vec3_t theTraceEnd; + AvHOrderType theOrderType = ORDERTYPE_UNDEFINED; + +// // Look for a player order +// if(!AvHOrderTracePlayers(inTeam, inOrigin, inNormRay, theOrderType, outTargetIndex)) +// { +// // If couldn't find one, create a non-player order if possible +// //AvHOrderTraceNonPlayers(inTeam, inOrigin, inNormRay, theOrderType, outTargetIndex, outTargetPoint); +// } + + + // Offset a little so we don't hit the commander + VectorMA(inOrigin, kSelectionStartRange, inNormRay, theTraceStart); + VectorMA(inOrigin, kSelectionEndRange, inNormRay, theTraceEnd); + int theFoundIndex = -1; + vec3_t theFoundLocation; + AvHTeamNumber theTeamOfThingHit; + bool thePlayerHit = false; + int theUserThree = 0; + int theUserFour = 0; + + if(AvHSHUTraceTangible(theTraceStart, theTraceEnd, theFoundIndex, theFoundLocation, theTeamOfThingHit, thePlayerHit, theUserThree, theUserFour)) + { + float theHealthPercentage=100.0f; + float theArmorPercentage=100.0f; + bool theSighted=false; +#ifdef AVH_SERVER + CBaseEntity *theEntity=AvHSUGetEntityFromIndex(theFoundIndex); + if ( theEntity ) + { + theHealthPercentage=theEntity->pev->health/theEntity->pev->max_health; + } + else + { + ALERT(at_console, "Not a buildable\n"); + } + + theArmorPercentage = theEntity->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(theEntity->pev->iuser4, (AvHUser3)theEntity->pev->iuser3); + theSighted=GetHasUpgrade(theEntity->pev->iuser4, MASK_VIS_SIGHTED); +#endif + + // Did we hit an enemy? If so, issue an attack order on him, then we're done, it's highest priority + if(thePlayerHit ) + { + // Did we hit a player on our team? If so check for welding, if not guard + if((theTeamOfThingHit == inTeam ) && (theTeamOfThingHit != TEAM_IND) ) + { + theOrderType = ORDERTYPET_GUARD; + if ( theArmorPercentage < 0.90f ) { + theOrderType = ORDERTYPET_WELD; + outTargetType = ORDERTARGETTYPE_TARGET; + } + outTargetIndex = theFoundIndex; + outUser3 = (AvHUser3)theUserThree; + } + } + // Something to pick up? + if(!thePlayerHit ) + { +// if ( ( theTeamOfThingHit ) != inTeam && (theTeamOfThingHit != TEAM_IND) && theSighted ) +// { +// // Use it's center for the height +// VectorCopy(theFoundLocation, outTargetPoint); +// theOrderType = ORDERTYPET_ATTACK; +// outTargetIndex = theFoundIndex; +// outUser3 = (AvHUser3)theUserThree; +// outTargetType = ORDERTARGETTYPE_LOCATION; +// } +// else + if ( theUserThree == AVH_USER3_MARINEITEM) + { + // Use it's center for the height + VectorCopy(theFoundLocation, outTargetPoint); + outTargetIndex = theFoundIndex; + outUser3 = (AvHUser3)theUserThree; + + // We're done + theOrderType = ORDERTYPET_GET; + } + // Buildable? + else if(GetHasUpgrade(theUserFour, MASK_BUILDABLE) && (theTeamOfThingHit == inTeam) && (theTeamOfThingHit != TEAM_IND)) + { + // Use it's center for the height + VectorCopy(theFoundLocation, outTargetPoint); + outTargetIndex = theFoundIndex; + outUser3 = (AvHUser3)theUserThree; + + // We're done + theOrderType = ORDERTYPET_BUILD; + } + // Weldable? + else if(theUserThree == AVH_USER3_WELD ) + { + // Use it's center for the height + VectorCopy(theFoundLocation, outTargetPoint); + outTargetIndex = theFoundIndex; + + // We're done + theOrderType = ORDERTYPET_WELD; + } + // Hit the ground? Move to, we're done + else if(theUserThree == AVH_USER3_WAYPOINT || (( theTeamOfThingHit != inTeam) && !theSighted )) + { + // Use it's center for the height + VectorCopy(theFoundLocation, outTargetPoint); + + // We're done + theOrderType = ORDERTYPEL_MOVE; + } + // Did we hit an entity on our team? Repair/guard it, we're done + else if((theTeamOfThingHit == inTeam) && (theTeamOfThingHit != TEAM_IND)) + { + theOrderType = ORDERTYPET_GUARD; + if ( theHealthPercentage < 0.90f ) { + theOrderType = ORDERTYPET_WELD; + VectorCopy(theFoundLocation, outTargetPoint); + outTargetType = ORDERTARGETTYPE_LOCATION; + } + outTargetIndex = theFoundIndex; + outUser3 = (AvHUser3)theUserThree; + } + } +// else if(!thePlayerHit && (theUserThree == AVH_USER3_USEABLE)) +// { +// // Use it's center for the height +// VectorCopy(theFoundLocation, outTargetPoint); +// outTargetIndex = theFoundIndex; +// +// // We're done +// theOrderType = ORDERTYPEL_USE; +// } + } + + return theOrderType; +} + +#ifdef AVH_SERVER +bool AvHCreateSpecificOrder(AvHTeamNumber inTeam, const vec3_t& inOrigin, AvHOrderType inOrder, const vec3_t& inNormRay, AvHOrder& outOrder) +{ + bool theSuccess = false; + int theTargetIndex = -1; + vec3_t theTargetLocation; + AvHOrderType theOrderType = inOrder; + AvHUser3 theUser3 = AVH_USER3_NONE; + + vec3_t theStartPoint; + VectorMA(inOrigin, kSelectionStartRange, inNormRay, theStartPoint); + + vec3_t theEndPoint; + VectorMA(inOrigin, kSelectionEndRange, inNormRay, theEndPoint); + + vec3_t theValidOrigin; + AvHSHUServerGetFirstNonSolidPoint(theStartPoint, theEndPoint, theValidOrigin); + + AvHOrderTargetType theTargetType=ORDERTARGETTYPE_UNDEFINED; + // Create default order if passed in + if(inOrder == ORDERTYPEL_DEFAULT) + { + theOrderType = AvHGetDefaultOrderType(inTeam, theValidOrigin, inNormRay, theTargetIndex, theTargetLocation, theUser3, theTargetType); + } + + if(theOrderType != ORDERTYPE_UNDEFINED) + { + // Init order with values it found + outOrder.SetOrderType(theOrderType); + outOrder.SetLocation(theTargetLocation); + outOrder.SetTargetIndex(theTargetIndex); + outOrder.SetUser3TargetType(theUser3); + if ( theTargetType != ORDERTARGETTYPE_UNDEFINED ) + outOrder.SetOrderTargetType(theTargetType); + theSuccess = true; + } + + outOrder.SetOrderID(); + + return theSuccess; +} + +bool AvHToggleUseable(CBaseEntity* inUser, const vec3_t& inOrigin, const vec3_t& inNormRay) +{ + bool theSuccess = false; + vec3_t theTraceStart; + vec3_t theTraceEnd; + + // Offset a little so we don't hit the commander + VectorMA(inOrigin, 100, inNormRay, theTraceStart); + + VectorMA(inOrigin, kSelectionEndRange, inNormRay, theTraceEnd); + int theFoundIndex = -1; + vec3_t theFoundLocation; + AvHTeamNumber theTeamOfThingHit; + bool thePlayerHit = false; + int theUserThree = 0; + int theUserFour = 0; + + if(AvHSHUTraceTangible(theTraceStart, theTraceEnd, theFoundIndex, theFoundLocation, theTeamOfThingHit, thePlayerHit, theUserThree, theUserFour)) + { + if(!thePlayerHit && (theUserThree == AVH_USER3_USEABLE)) + { + // Find entity we clicked on, use it + //CBaseEntity* theEntity = CBaseEntity::Instance(ENT(theFoundIndex)); + CBaseEntity* theEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theFoundIndex)); + if(theEntity) + { + // For each entity with this target name (including this one), use it + if(theEntity->pev->targetname) + { + CBaseEntity* theTarget = NULL; + while((theTarget = UTIL_FindEntityByTargetname(theTarget, STRING(theEntity->pev->targetname))) != NULL) + { + int theObjectCaps = theTarget->ObjectCaps(); + if((theObjectCaps & FCAP_IMPULSE_USE) || (FCAP_ONOFF_USE)) + { + theTarget->Use(inUser, inUser, USE_TOGGLE, 0); + } + } + } + else if(FClassnameIs(theEntity->edict(), "func_button") && theEntity->pev->target) ////: Its probably a button!, classname check to prevent any possible exploits + { + CBaseEntity* theTarget = NULL; + while((theTarget = UTIL_FindEntityByTargetname(theTarget, STRING(theEntity->pev->target))) != NULL) + { + int theObjectCaps = theTarget->ObjectCaps(); + if((theObjectCaps & FCAP_IMPULSE_USE) || (FCAP_ONOFF_USE)) + { + theTarget->Use(inUser, inUser, USE_TOGGLE, 0); + } + } + } + theSuccess = true; + } + } + } + return theSuccess; +} #endif \ No newline at end of file diff --git a/main/source/mod/AvHOrder.h b/main/source/mod/AvHOrder.h index 91327fb..afb2ff8 100644 --- a/main/source/mod/AvHOrder.h +++ b/main/source/mod/AvHOrder.h @@ -1,120 +1,120 @@ -#ifndef AVH_ORDER_H -#define AVH_ORDER_H - -#include "util/nowarnings.h" -#include "types.h" -#include "mod/AvHConstants.h" -#include "mod/AvHSpecials.h" - -#ifdef AVH_CLIENT -#include "common/triangleapi.h" -#include "cl_dll/wrect.h" -#include "cl_dll/cl_dll.h" -#endif - -#ifdef AVH_SERVER -#include "dlls/extdll.h" -class CBaseEntity; -#endif - -const int kOrderStatusActive = 0; -const int kOrderStatusComplete = 1; -const int kOrderStatusCancelled = 2; - -class AvHOrder -{ -public: - -// Server -#ifdef AVH_SERVER - bool Update(); - int GetOrderID() const; - void SetOrderID(); - float GetTimeOrderCompleted() const; - void SetTimeOrderCompleted(float inTime); -#endif - -#ifndef AVH_SERVER - void SetOrderStatus(int inOrderStatus); -#endif - -// Shared - AvHOrder(); - void ClearReceiver(); - bool GetHasReceiver(int inPlayerIndex) const; - EntityInfo GetReceiver() const; - int GetTargetIndex() const; - AvHOrderType GetOrderType() const; - int GetOrderStatus() const; - AvHOrderTargetType GetOrderTargetType() const; - AvHUser3 GetTargetUser3Type() const; - void GetLocation(vec3_t& outPosition) const; - void GetOrderColor(float& outR, float& outG, float& outB, float& outA) const; - - bool RemovePlayerFromReceivers(int inIndex); - - bool GetOrderActive() const; - bool GetOrderCancelled() const; - bool GetOrderCompleted() const; - void SetOrderCompleted(const bool inCompleted = true); - - bool SetReceiver(const EntityInfo& entity); - void SetTargetIndex(int inTargetIndex); - void SetOrderType(AvHOrderType inType); - void SetOrderTargetType(AvHOrderTargetType inTargetType); - void SetUser3TargetType(AvHUser3 inUser3); - void SetLocation(const vec3_t& inPosition); - - bool operator==(const AvHOrder& inOrder) const; - bool operator!=(const AvHOrder& inOrder) const; - void operator=(const AvHOrder& inOrder); - -private: - EntityInfo mPlayer; - AvHOrderType mOrderType; - AvHOrderTargetType mOrderTargetType; - AvHUser3 mOrderTargetUser3; - vec3_t mLocation; - int mTargetIndex; - int mOrderStatus; - - #ifdef AVH_SERVER - float mTimeOrderCompleted; - int mOrderID; - #else - bool mOrderCompleted; - #endif -}; - -typedef vector OrderListType; -// tankefugl: 0000971 -typedef enum { - TEAMMATE_MARINE_ORDER_WELD = 0, - TEAMMATE_MARINE_ORDER_FOLLOW, - TEAMMATE_MARINE_ORDER_COVER, - TEAMMATE_MARINE_ORDER_UNKNOWN, - TEAMMATE_ALIEN_ORDER_HEAL, - TEAMMATE_ALIEN_ORDER_FOLLOW, - TEAMMATE_ALIEN_ORDER_COVER, - TEAMMATE_MARINE_LEFT_ARROW = 16, - TEAMMATE_MARINE_RIGHT_ARROW, - TEAMMATE_ALIEN_LEFT_ARROW, - TEAMMATE_ALIEN_RIGHT_ARROW, - TEAMMATE_ALIEN_ORDER_UNKNOWN -} TeammateOrderEnum; -typedef pair TeammateOrderType; -typedef map TeammateOrderListType; -// :tankefugl - -void AvHChangeOrder(OrderListType& inList, const AvHOrder& inOrder); -//void AvHRemovePlayerFromOrders(OrderListType& inList, int inPlayerIndex); - -// Must be shared -AvHOrderType AvHGetDefaultOrderType(AvHTeamNumber inTeam, const vec3_t& inOrigin, const vec3_t& inNormRay, int& outTargetIndex, vec3_t& outTargetPoint, AvHUser3& outUser3, AvHOrderTargetType& outTargetType); - -#ifdef AVH_SERVER -bool AvHCreateSpecificOrder(AvHTeamNumber inTeam, const vec3_t& inOrigin, AvHOrderType inOrder, const vec3_t& inNormRay, AvHOrder& outOrder); -bool AvHToggleUseable(CBaseEntity* inUser, const vec3_t& inNormRay, const vec3_t& inPosition); -#endif - -#endif +#ifndef AVH_ORDER_H +#define AVH_ORDER_H + +#include "util/nowarnings.h" +#include "types.h" +#include "mod/AvHConstants.h" +#include "mod/AvHSpecials.h" + +#ifdef AVH_CLIENT +#include "common/triangleapi.h" +#include "cl_dll/wrect.h" +#include "cl_dll/cl_dll.h" +#endif + +#ifdef AVH_SERVER +#include "dlls/extdll.h" +class CBaseEntity; +#endif + +const int kOrderStatusActive = 0; +const int kOrderStatusComplete = 1; +const int kOrderStatusCancelled = 2; + +class AvHOrder +{ +public: + +// Server +#ifdef AVH_SERVER + bool Update(); + int GetOrderID() const; + void SetOrderID(); + float GetTimeOrderCompleted() const; + void SetTimeOrderCompleted(float inTime); +#endif + +#ifndef AVH_SERVER + void SetOrderStatus(int inOrderStatus); +#endif + +// Shared + AvHOrder(); + void ClearReceiver(); + bool GetHasReceiver(int inPlayerIndex) const; + EntityInfo GetReceiver() const; + int GetTargetIndex() const; + AvHOrderType GetOrderType() const; + int GetOrderStatus() const; + AvHOrderTargetType GetOrderTargetType() const; + AvHUser3 GetTargetUser3Type() const; + void GetLocation(vec3_t& outPosition) const; + void GetOrderColor(float& outR, float& outG, float& outB, float& outA) const; + + bool RemovePlayerFromReceivers(int inIndex); + + bool GetOrderActive() const; + bool GetOrderCancelled() const; + bool GetOrderCompleted() const; + void SetOrderCompleted(const bool inCompleted = true); + + bool SetReceiver(const EntityInfo& entity); + void SetTargetIndex(int inTargetIndex); + void SetOrderType(AvHOrderType inType); + void SetOrderTargetType(AvHOrderTargetType inTargetType); + void SetUser3TargetType(AvHUser3 inUser3); + void SetLocation(const vec3_t& inPosition); + + bool operator==(const AvHOrder& inOrder) const; + bool operator!=(const AvHOrder& inOrder) const; + void operator=(const AvHOrder& inOrder); + +private: + EntityInfo mPlayer; + AvHOrderType mOrderType; + AvHOrderTargetType mOrderTargetType; + AvHUser3 mOrderTargetUser3; + vec3_t mLocation; + int mTargetIndex; + int mOrderStatus; + + #ifdef AVH_SERVER + float mTimeOrderCompleted; + int mOrderID; + #else + bool mOrderCompleted; + #endif +}; + +typedef vector OrderListType; +// : 0000971 +typedef enum { + TEAMMATE_MARINE_ORDER_WELD = 0, + TEAMMATE_MARINE_ORDER_FOLLOW, + TEAMMATE_MARINE_ORDER_COVER, + TEAMMATE_MARINE_ORDER_UNKNOWN, + TEAMMATE_ALIEN_ORDER_HEAL, + TEAMMATE_ALIEN_ORDER_FOLLOW, + TEAMMATE_ALIEN_ORDER_COVER, + TEAMMATE_MARINE_LEFT_ARROW = 16, + TEAMMATE_MARINE_RIGHT_ARROW, + TEAMMATE_ALIEN_LEFT_ARROW, + TEAMMATE_ALIEN_RIGHT_ARROW, + TEAMMATE_ALIEN_ORDER_UNKNOWN +} TeammateOrderEnum; +typedef pair TeammateOrderType; +typedef map TeammateOrderListType; +// : + +void AvHChangeOrder(OrderListType& inList, const AvHOrder& inOrder); +//void AvHRemovePlayerFromOrders(OrderListType& inList, int inPlayerIndex); + +// Must be shared +AvHOrderType AvHGetDefaultOrderType(AvHTeamNumber inTeam, const vec3_t& inOrigin, const vec3_t& inNormRay, int& outTargetIndex, vec3_t& outTargetPoint, AvHUser3& outUser3, AvHOrderTargetType& outTargetType); + +#ifdef AVH_SERVER +bool AvHCreateSpecificOrder(AvHTeamNumber inTeam, const vec3_t& inOrigin, AvHOrderType inOrder, const vec3_t& inNormRay, AvHOrder& outOrder); +bool AvHToggleUseable(CBaseEntity* inUser, const vec3_t& inNormRay, const vec3_t& inPosition); +#endif + +#endif diff --git a/main/source/mod/AvHOverviewControl.cpp b/main/source/mod/AvHOverviewControl.cpp index a7cee4f..81e302b 100644 --- a/main/source/mod/AvHOverviewControl.cpp +++ b/main/source/mod/AvHOverviewControl.cpp @@ -162,11 +162,12 @@ void AvHOverviewControl::GetDrawInfo(AvHOverviewMap::DrawInfo& outDrawInfo) outDrawInfo.mY = 0; outDrawInfo.mWidth = theWidth; outDrawInfo.mHeight = theHeight; - + outDrawInfo.mZoomScale = 1.0f; AvHMapExtents theMapExtents; theOverviewMap.GetMapExtents(theMapExtents); outDrawInfo.mFullScreen = true; + outDrawInfo.mCommander = false; float worldWidth = theMapExtents.GetMaxMapX() - theMapExtents.GetMinMapX(); float worldHeight = theMapExtents.GetMaxMapY() - theMapExtents.GetMinMapY(); diff --git a/main/source/mod/AvHOverviewMap.cpp b/main/source/mod/AvHOverviewMap.cpp index 710b11d..f1a3aaa 100644 --- a/main/source/mod/AvHOverviewMap.cpp +++ b/main/source/mod/AvHOverviewMap.cpp @@ -1,908 +1,1064 @@ -#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(); -} - -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 = ""; - - mLastUpdateTime = 0; -} - -void AvHOverviewMap::Clear() -{ - this->Init(); -} - -void AvHOverviewMap::GetSpriteForEntity(const DrawableEntity& entity, int& outSprite, int& outFrame, int& outRenderMode) -{ - - outRenderMode = kRenderTransTexture; - - if ((this->mUser3 == AVH_USER3_COMMANDER_PLAYER) || (entity.mUser3 == AVH_USER3_UNKNOWN)) - { - outSprite = Safe_SPR_Load(kCommBlipSprite); - outFrame = 0; - } - else - { - gHUD.GetSpriteForUser3(entity.mUser3, outSprite, outFrame, outRenderMode); - } - -} - -void AvHOverviewMap::GetColorForEntity(const DrawableEntity& entity, float& outR, float& outG, float& outB) -{ - - if (entity.mUser3 == AVH_USER3_WAYPOINT) - { - outR = 0.1; - outG = 0.8; - outB = 1.0; - } - else if (entity.mTeam == TEAM_IND) - { - - if (entity.mUser3 == AVH_USER3_WELD) - { - outR = 1.0; - outG = 0.7; - outB = 0.0; - } - else - { - outR = 0.5; - outG = 0.5; - outB = 0.5; - } - } - else - { - if (entity.mTeam == mTeam) - { - - if (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) - { - outR = 0.5; - outG = 0.8; - outB = 1.0; - } - else - { - - outR = 1.0; - outG = 1.0; - outB = 1.0; - - // Color squads. - - 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 || - (entity.mSquadNumber != 0 && - entity.mSquadNumber == localPlayerSquad)) - { - outR = 0.5; - outG = 1.0; - outB = 0.5; - } - } - - } - - } - else - { - outR = 1.0; - outG = 0.1; - 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; - - // 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); - } - - } - } - - 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 = ""; -} \ No newline at end of file +#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; + // : 1066 reset overview map + this->mLastMinimap = -1; + this->mMapName=""; + 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, bool commanderOverview) +{ + outRenderMode = kRenderTransTexture; + + if (entity.mUser3 == AVH_USER3_UNKNOWN) + { + outSprite = Safe_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 = 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.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; + } + + int 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) + { + + 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) +{ + + // : 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 ) + { + int tmpSpr=0; + for ( int i=drawLabels; i >=0 && tmpSpr == 0 ; i-- ) { + string theMiniMapName = AvHMiniMap::GetSpriteNameFromMap(ScreenWidth(), mMapName, i); + tmpSpr = Safe_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); + + 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); + } + + // 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 = 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 = ""; +} diff --git a/main/source/mod/AvHOverviewMap.h b/main/source/mod/AvHOverviewMap.h index 37c528f..d305110 100644 --- a/main/source/mod/AvHOverviewMap.h +++ b/main/source/mod/AvHOverviewMap.h @@ -1,130 +1,135 @@ -#ifndef AVHOVERVIEWMAP_H -#define AVHOVERVIEWMAP_H - -#include "mod/AvHEntityHierarchy.h" -#include "mod/AvHMapExtents.h" -#include "mod/AvHOrder.h" - -class DrawableEntity -{ -public: - DrawableEntity() : mUser3(AVH_USER3_NONE), mIsAlive(true), mX(0), mY(0), mAngleRadians(0), mIsLocalPlayer(false), mEntityNumber(0), mTeam(TEAM_IND), mSquadNumber(0) - {} - - AvHUser3 mUser3; - bool mIsAlive; - int mX; - int mY; - AvHTeamNumber mTeam; - float mAngleRadians; - bool mIsLocalPlayer; - int mEntityNumber; - int mSquadNumber; -}; - -class AvHOverviewMap -{ -public: - - struct DrawInfo - { - - int mX; - int mY; - int mWidth; - int mHeight; - - float mViewWorldMinX; - float mViewWorldMinY; - float mViewWorldMaxX; - float mViewWorldMaxY; - - bool mFullScreen; - - }; - AvHOverviewMap(); - - void Clear(); - - bool GetHasData() const; - void GetWorldPosFromMouse(const DrawInfo& inDrawInfo, int inMouseX, int inMouseY, float& outWorldX, float& outWorldY); - - void SetMapExtents(const string& inMapName, const AvHMapExtents& inMapExtents); - void GetMapExtents(AvHMapExtents& outMapExtents); - - void SetUser3(AvHUser3 inUser3); - void SetWorldPosition(float inWorldX, float inWorldY); - void GetWorldPosition(float& outWorldX, float& outWorldY); - - void Update(float inCurrentTime); - void UpdateOrders(const OrderListType& inOrderList, const EntityListType& inDrawPlayers); - void VidInit(void); - - void AddAlert(float x, float y); - - void Draw(const DrawInfo& inDrawInfo); - - int GetEntityAtWorldPosition(float inWorldX, float inWorldY, float inRadius) const; - -protected: - void KillOldAlerts(float inCurrentTime); - - void DrawMiniMap(const DrawInfo& inDrawInfo); - void DrawMiniMapEntity(const DrawInfo& inDrawInfo, const DrawableEntity& inEntity); - void DrawAlerts(const DrawInfo& inDrawInfo); - - AvHUser3 mUser3; - AvHTeamNumber mTeam; - // puzl: 1066 the name of the last minimap we loaded - string mLastMinimapName; -private: - - void WorldToMiniMapCoords(const DrawInfo& inDrawInfo, float& x, float& y); - - void Init(); - void GetSpriteForEntity(const DrawableEntity& entity, int& outSprite, int& outFrame, int& outRenderMode); - void GetColorForEntity(const DrawableEntity& entity, float& outR, float& outG, float& outB); - - void UpdateDrawData(float inCurrentTime); - - float mWorldPlayerX; - float mWorldPlayerY; - - AvHMapExtents mMapExtents; - - string mMapName; - int mMiniMapSprite; - - HSPRITE mReticleSprite; - - typedef vector DrawableEntityListType; - DrawableEntityListType mDrawableEntityList; - - struct MapAlert - { - - float mStartTime; - float mExpireTime; - - float mX; // World space. - float mY; - - }; - - typedef std::vector MapAlertListType; - MapAlertListType mAlertList; - - struct MapOrder - { - float mX; // World space. - float mY; - }; - - typedef std::vector MapOrderListType; - MapOrderListType mMapOrderList; - - float mLastUpdateTime; - -}; - +#ifndef AVHOVERVIEWMAP_H +#define AVHOVERVIEWMAP_H + +#include "mod/AvHEntityHierarchy.h" +#include "mod/AvHMapExtents.h" +#include "mod/AvHOrder.h" + +class DrawableEntity +{ +public: + DrawableEntity() : mUser3(AVH_USER3_NONE), mIsAlive(true), mX(0), mY(0), mAngleRadians(0), mIsLocalPlayer(false), mEntityNumber(0), mTeam(TEAM_IND), mSquadNumber(0), mIsUnderAttack(0) + {} + + AvHUser3 mUser3; + bool mIsAlive; + bool mIsUnderAttack; + int mX; + int mY; + AvHTeamNumber mTeam; + float mAngleRadians; + bool mIsLocalPlayer; + int mEntityNumber; + int mSquadNumber; +}; + +class AvHOverviewMap +{ +public: + + struct DrawInfo + { + + int mX; + int mY; + int mWidth; + int mHeight; + + float mViewWorldMinX; + float mViewWorldMinY; + float mViewWorldMaxX; + float mViewWorldMaxY; + + float mZoomScale; + bool mFullScreen; + bool mCommander; + }; + AvHOverviewMap(); + + void Clear(); + + bool GetHasData() const; + void GetWorldPosFromMouse(const DrawInfo& inDrawInfo, int inMouseX, int inMouseY, float& outWorldX, float& outWorldY); + + void SetMapExtents(const string& inMapName, const AvHMapExtents& inMapExtents); + void GetMapExtents(AvHMapExtents& outMapExtents); + + void SetUser3(AvHUser3 inUser3); + void SetWorldPosition(float inWorldX, float inWorldY); + void GetWorldPosition(float& outWorldX, float& outWorldY); + + void Update(float inCurrentTime); + void UpdateOrders(const OrderListType& inOrderList, const EntityListType& inDrawPlayers); + void VidInit(void); + + void AddAlert(float x, float y); + + void Draw(const DrawInfo& inDrawInfo); + + int GetEntityAtWorldPosition(float inWorldX, float inWorldY, float inRadius) const; + +protected: + void KillOldAlerts(float inCurrentTime); + + void DrawMiniMap(const DrawInfo& inDrawInfo); + void DrawMiniMapEntity(const DrawInfo& inDrawInfo, const DrawableEntity& inEntity); + void DrawAlerts(const DrawInfo& inDrawInfo); + + AvHUser3 mUser3; + AvHTeamNumber mTeam; + // : 1066 the name of the last minimap we loaded + int mLastMinimap; +private: + + void WorldToMiniMapCoords(const DrawInfo& inDrawInfo, float& x, float& y); + + void Init(); + void GetSpriteForEntity(const DrawableEntity& entity, int& outSprite, int& outFrame, int& outRenderMode, bool commanderOverview); + void GetColorForEntity(const DrawableEntity& entity, float& outR, float& outG, float& outB); + + void UpdateDrawData(float inCurrentTime); + + float mWorldPlayerX; + float mWorldPlayerY; + + float mBlinkTime; + bool mBlinkOn; + + AvHMapExtents mMapExtents; + + string mMapName; + int mMiniMapSprite; + + HSPRITE mReticleSprite; + + typedef vector DrawableEntityListType; + DrawableEntityListType mDrawableEntityList; + + struct MapAlert + { + + float mStartTime; + float mExpireTime; + + float mX; // World space. + float mY; + + }; + + typedef std::vector MapAlertListType; + MapAlertListType mAlertList; + + struct MapOrder + { + float mX; // World space. + float mY; + }; + + typedef std::vector MapOrderListType; + MapOrderListType mMapOrderList; + + float mLastUpdateTime; + +}; + #endif \ No newline at end of file diff --git a/main/source/mod/AvHParasiteGun.cpp b/main/source/mod/AvHParasiteGun.cpp index daccf01..8820342 100644 --- a/main/source/mod/AvHParasiteGun.cpp +++ b/main/source/mod/AvHParasiteGun.cpp @@ -1,236 +1,236 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHParasiteGun.cpp $ -// $Date: 2002/11/22 21:28:16 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHParasiteGun.cpp,v $ -// Revision 1.15 2002/11/22 21:28:16 Flayra -// - mp_consistency changes -// -// Revision 1.14 2002/11/12 02:25:51 Flayra -// - Parasite no longer does damage to teammates improperly -// -// Revision 1.13 2002/11/06 01:38:37 Flayra -// - Added ability for buildings to be enabled and disabled, for turrets to be shut down -// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) -// -// Revision 1.12 2002/09/09 20:00:32 Flayra -// - Parasite works on buildings now -// - Parasite no longer changes score -// -// Revision 1.11 2002/08/31 18:01:02 Flayra -// - Work at VALVe -// -// Revision 1.10 2002/08/16 02:40:36 Flayra -// - Damage types -// - Parasite can now affect organic enemy targets (babblers, alien buildings) -// -// Revision 1.9 2002/07/24 19:09:17 Flayra -// - Linux issues -// -// Revision 1.8 2002/07/24 18:55:52 Flayra -// - Linux case sensitivity stuff -// -// Revision 1.7 2002/07/24 18:45:42 Flayra -// - Linux and scripting changes -// -// Revision 1.6 2002/07/01 21:19:29 Flayra -// - Removed hard-coded parasite damage -// -// Revision 1.5 2002/06/25 17:50:59 Flayra -// - Reworking for correct player animations and new enable/disable state, new view model artwork, alien weapon refactoring -// -// Revision 1.4 2002/06/10 19:49:06 Flayra -// - Updated with new alien view model artwork (with running anims) -// -// Revision 1.3 2002/06/03 16:27:06 Flayra -// - Animation constants and changes with new artwork -// -// Revision 1.2 2002/05/28 17:58:08 Flayra -// - Parasite works properly in tournament mode now -// -// Revision 1.1 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHAlienWeapons.h" -#include "mod/AvHPlayer.h" - -#ifdef AVH_CLIENT -#include "cl_dll/eventscripts.h" -#include "cl_dll/in_defs.h" -#include "cl_dll/wrect.h" -#include "cl_dll/cl_dll.h" -#endif - -#include "common/hldm.h" -#include "common/event_api.h" -#include "common/event_args.h" -#include "common/vector_util.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHPlayerUpgrade.h" - -#ifdef AVH_SERVER -#include "mod/AvHGamerules.h" -#endif - -LINK_ENTITY_TO_CLASS(kwParasiteGun, AvHParasiteGun); -extern int gParasiteStartEventID; - -void AvHParasiteGun::Init() -{ - this->mRange = kParasiteRange; - this->mDamage = BALANCE_VAR(kParasiteDamage); -} - -int AvHParasiteGun::GetBarrelLength() const -{ - return kParasiteBarrelLength; -} - -float AvHParasiteGun::GetRateOfFire() const -{ - return BALANCE_VAR(kParasiteROF); -} - -int AvHParasiteGun::GetDamageType() const -{ - return NS_DMG_ORGANIC; - //return NS_DMG_NORMAL; -} - -int AvHParasiteGun::GetDeployAnimation() const -{ - return 6; -} - -bool AvHParasiteGun::GetFiresUnderwater() const -{ - return true; -} - -bool AvHParasiteGun::GetIsDroppable() const -{ - return false; -} - -int AvHParasiteGun::GetShootAnimation() const -{ - return -1; -} - -void AvHParasiteGun::FireProjectiles(void) -{ - #ifdef AVH_SERVER - - UTIL_MakeVectors(this->m_pPlayer->pev->v_angle); - - Vector vecAiming = gpGlobals->v_forward; - Vector vecSrc = this->m_pPlayer->GetGunPosition( ) + vecAiming; - Vector vecEnd = vecSrc + vecAiming*kParasiteRange; - - // Ignore damage upgrades - //int theTracerFreq; - //float theDamageMultiplier; - //AvHPlayerUpgrade::GetWeaponUpgrade(this->m_pPlayer->pev->iuser4, &theDamageMultiplier, &theTracerFreq); - - // Perform trace to hit victim - TraceResult tr; - UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, dont_ignore_glass, this->m_pPlayer->edict(), &tr); - CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); - if(theEntityHit) - { - float theScalar = 1.0f; - - bool theCanDoDamageTo = GetGameRules()->CanEntityDoDamageTo(this->m_pPlayer, theEntityHit, &theScalar); - - theScalar *= AvHPlayerUpgrade::GetAlienRangedDamageUpgrade(this->m_pPlayer->pev->iuser4); - - if(theCanDoDamageTo || (theEntityHit->pev->team == TEAM_IND)) - { - AvHPlayer* thePlayer = dynamic_cast(theEntityHit); - if(!thePlayer || thePlayer->GetCanBeAffectedByEnemies()) - { - bool thePlayEffect = false; - - AvHBaseBuildable* theBuildable = dynamic_cast(theEntityHit); - if((thePlayer || theBuildable) && !GetHasUpgrade(theEntityHit->pev->iuser4, MASK_PARASITED)) - { - // Increase score for guy who parasited - //this->m_pPlayer->AddPoints(1, TRUE); - - // joev: bug 0000841 - Don't allow alien players/structures to actually be parasited. - // They can take the damage, just not have the "upgrade". - if (theEntityHit->pev->team != this->m_pPlayer->pev->team) { - SetUpgradeMask(&theEntityHit->pev->iuser4, MASK_PARASITED); - } - // :joev - thePlayEffect = true; - } - - if(theCanDoDamageTo) - { - float theDamage = this->mDamage*theScalar; - if(theEntityHit->TakeDamage(this->pev, this->m_pPlayer->pev, theDamage, this->GetDamageType()) > 0) - { - thePlayEffect = true; - } - } - - if(thePlayEffect) - { - // Play parasite-hit sound at player - EMIT_SOUND(theEntityHit->edict(), CHAN_AUTO, kParasiteHitSound, 1.0f, ATTN_NORM); - } - } - } - } - - #endif -} - -char* AvHParasiteGun::GetViewModel() const -{ - return kLevel1ViewModel; -} - -void AvHParasiteGun::Precache() -{ - AvHAlienWeapon::Precache(); - - PRECACHE_UNMODIFIED_SOUND(kParasiteFireSound); - PRECACHE_UNMODIFIED_SOUND(kParasiteHitSound); - PRECACHE_UNMODIFIED_MODEL(kParasiteProjectileModel); - - this->mEvent = PRECACHE_EVENT(1, kParasiteShootEventName); -} - -void AvHParasiteGun::Spawn() -{ - AvHAlienWeapon::Spawn(); - - Precache(); - - this->m_iId = AVH_WEAPON_PARASITE; - - // Set our class name - this->pev->classname = MAKE_STRING(kwsParasiteGun); - - SET_MODEL(ENT(this->pev), kNullModel); - - FallInit();// get ready to fall down. -} - -bool AvHParasiteGun::UsesAmmo(void) const -{ - return false; -} - +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHParasiteGun.cpp $ +// $Date: 2002/11/22 21:28:16 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHParasiteGun.cpp,v $ +// Revision 1.15 2002/11/22 21:28:16 Flayra +// - mp_consistency changes +// +// Revision 1.14 2002/11/12 02:25:51 Flayra +// - Parasite no longer does damage to teammates improperly +// +// Revision 1.13 2002/11/06 01:38:37 Flayra +// - Added ability for buildings to be enabled and disabled, for turrets to be shut down +// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) +// +// Revision 1.12 2002/09/09 20:00:32 Flayra +// - Parasite works on buildings now +// - Parasite no longer changes score +// +// Revision 1.11 2002/08/31 18:01:02 Flayra +// - Work at VALVe +// +// Revision 1.10 2002/08/16 02:40:36 Flayra +// - Damage types +// - Parasite can now affect organic enemy targets (babblers, alien buildings) +// +// Revision 1.9 2002/07/24 19:09:17 Flayra +// - Linux issues +// +// Revision 1.8 2002/07/24 18:55:52 Flayra +// - Linux case sensitivity stuff +// +// Revision 1.7 2002/07/24 18:45:42 Flayra +// - Linux and scripting changes +// +// Revision 1.6 2002/07/01 21:19:29 Flayra +// - Removed hard-coded parasite damage +// +// Revision 1.5 2002/06/25 17:50:59 Flayra +// - Reworking for correct player animations and new enable/disable state, new view model artwork, alien weapon refactoring +// +// Revision 1.4 2002/06/10 19:49:06 Flayra +// - Updated with new alien view model artwork (with running anims) +// +// Revision 1.3 2002/06/03 16:27:06 Flayra +// - Animation constants and changes with new artwork +// +// Revision 1.2 2002/05/28 17:58:08 Flayra +// - Parasite works properly in tournament mode now +// +// Revision 1.1 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHAlienWeapons.h" +#include "mod/AvHPlayer.h" + +#ifdef AVH_CLIENT +#include "cl_dll/eventscripts.h" +#include "cl_dll/in_defs.h" +#include "cl_dll/wrect.h" +#include "cl_dll/cl_dll.h" +#endif + +#include "common/hldm.h" +#include "common/event_api.h" +#include "common/event_args.h" +#include "common/vector_util.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHPlayerUpgrade.h" + +#ifdef AVH_SERVER +#include "mod/AvHGamerules.h" +#endif + +LINK_ENTITY_TO_CLASS(kwParasiteGun, AvHParasiteGun); +extern int gParasiteStartEventID; + +void AvHParasiteGun::Init() +{ + this->mRange = kParasiteRange; + this->mDamage = BALANCE_VAR(kParasiteDamage); +} + +int AvHParasiteGun::GetBarrelLength() const +{ + return kParasiteBarrelLength; +} + +float AvHParasiteGun::GetRateOfFire() const +{ + return BALANCE_VAR(kParasiteROF); +} + +int AvHParasiteGun::GetDamageType() const +{ + return NS_DMG_ORGANIC; + //return NS_DMG_NORMAL; +} + +int AvHParasiteGun::GetDeployAnimation() const +{ + return 6; +} + +bool AvHParasiteGun::GetFiresUnderwater() const +{ + return true; +} + +bool AvHParasiteGun::GetIsDroppable() const +{ + return false; +} + +int AvHParasiteGun::GetShootAnimation() const +{ + return -1; +} + +void AvHParasiteGun::FireProjectiles(void) +{ + #ifdef AVH_SERVER + + UTIL_MakeVectors(this->m_pPlayer->pev->v_angle); + + Vector vecAiming = gpGlobals->v_forward; + Vector vecSrc = this->m_pPlayer->GetGunPosition( ) + vecAiming; + Vector vecEnd = vecSrc + vecAiming*kParasiteRange; + + // Ignore damage upgrades + //int theTracerFreq; + //float theDamageMultiplier; + //AvHPlayerUpgrade::GetWeaponUpgrade(this->m_pPlayer->pev->iuser4, &theDamageMultiplier, &theTracerFreq); + + // Perform trace to hit victim + TraceResult tr; + UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, dont_ignore_glass, this->m_pPlayer->edict(), &tr); + CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); + if(theEntityHit) + { + float theScalar = 1.0f; + + bool theCanDoDamageTo = GetGameRules()->CanEntityDoDamageTo(this->m_pPlayer, theEntityHit, &theScalar); + + theScalar *= AvHPlayerUpgrade::GetAlienRangedDamageUpgrade(this->m_pPlayer->pev->iuser4); + + if(theCanDoDamageTo || (theEntityHit->pev->team == TEAM_IND)) + { + AvHPlayer* thePlayer = dynamic_cast(theEntityHit); + if(!thePlayer || thePlayer->GetCanBeAffectedByEnemies()) + { + bool thePlayEffect = false; + + AvHBaseBuildable* theBuildable = dynamic_cast(theEntityHit); + if((thePlayer || theBuildable) && !GetHasUpgrade(theEntityHit->pev->iuser4, MASK_PARASITED)) + { + // Increase score for guy who parasited + //this->m_pPlayer->AddPoints(1, TRUE); + + // : bug 0000841 - Don't allow alien players/structures to actually be parasited. + // They can take the damage, just not have the "upgrade". + if (theEntityHit->pev->team != this->m_pPlayer->pev->team) { + SetUpgradeMask(&theEntityHit->pev->iuser4, MASK_PARASITED); + } + // : + thePlayEffect = true; + } + + if(theCanDoDamageTo) + { + float theDamage = this->mDamage*theScalar; + if(theEntityHit->TakeDamage(this->pev, this->m_pPlayer->pev, theDamage, this->GetDamageType()) > 0) + { + thePlayEffect = true; + } + } + + if(thePlayEffect) + { + // Play parasite-hit sound at player + EMIT_SOUND(theEntityHit->edict(), CHAN_AUTO, kParasiteHitSound, 1.0f, ATTN_NORM); + } + } + } + } + + #endif +} + +char* AvHParasiteGun::GetViewModel() const +{ + return kLevel1ViewModel; +} + +void AvHParasiteGun::Precache() +{ + AvHAlienWeapon::Precache(); + + PRECACHE_UNMODIFIED_SOUND(kParasiteFireSound); + PRECACHE_UNMODIFIED_SOUND(kParasiteHitSound); + PRECACHE_UNMODIFIED_MODEL(kParasiteProjectileModel); + + this->mEvent = PRECACHE_EVENT(1, kParasiteShootEventName); +} + +void AvHParasiteGun::Spawn() +{ + AvHAlienWeapon::Spawn(); + + Precache(); + + this->m_iId = AVH_WEAPON_PARASITE; + + // Set our class name + this->pev->classname = MAKE_STRING(kwsParasiteGun); + + SET_MODEL(ENT(this->pev), kNullModel); + + FallInit();// get ready to fall down. +} + +bool AvHParasiteGun::UsesAmmo(void) const +{ + return false; +} + diff --git a/main/source/mod/AvHParticleTemplate.cpp b/main/source/mod/AvHParticleTemplate.cpp index e95e1b2..72290b9 100644 --- a/main/source/mod/AvHParticleTemplate.cpp +++ b/main/source/mod/AvHParticleTemplate.cpp @@ -426,6 +426,79 @@ AvHParticleTemplate::SetStartingVelocityParams(const ParticleParams& inParms) } +void AvHParticleTemplate::operator=(const AvHParticleTemplate& other) +{ + this->mName = other.mName; + this->mMaxParticles = other.mMaxParticles; + memcpy(this->mBaseColor, other.mBaseColor, sizeof(PSVector)); + this->mSprite = other.mSprite; + this->mParticleSize = other.mParticleSize; + this->mParticleSystemLifetime = other.mParticleSystemLifetime; + this->mParticleLifetime = other.mParticleLifetime; + memcpy(this->mGravity, other.mGravity, sizeof(PSVector)); + this->mGenerationRate = other.mGenerationRate; + this->mGenerationShape = other.mGenerationShape; + memcpy(this->mGenerationParams, other.mGenerationParams, sizeof(ParticleParams)); + this->mGenerationEntityIndex = other.mGenerationEntityIndex; + this->mGenerationEntityParameter = other.mGenerationEntityParameter; + this->mGenerationEntityName = other.mGenerationEntityName; + this->mStartingVelocityShape = other.mStartingVelocityShape; + memcpy(this->mStartingVelocityParams, other.mStartingVelocityParams, sizeof(ParticleParams)); + this->mDeathShape = other.mDeathShape; + memcpy(this->mDeathParams, other.mDeathParams, sizeof(ParticleParams)); + this->mCollisionShape = other.mCollisionShape; + memcpy(this->mCollisionParams, other.mCollisionParams, sizeof(ParticleParams)); + this->mMinSpeed = other.mMinSpeed; + this->mMaxSpeed = other.mMaxSpeed; + this->mNumSpriteFrames = other.mNumSpriteFrames; + this->mAnimationSpeed = other.mAnimationSpeed; + this->mParticleScaling = other.mParticleScaling; + this->mRenderMode = other.mRenderMode; + this->mMaxAlpha = other.mMaxAlpha; + this->mFadeIn = other.mFadeIn; + this->mFlags = other.mFlags; + this->mParticleSystemToGenerate = other.mParticleSystemToGenerate; + this->mParticleSystemIndexToGenerate = other.mParticleSystemIndexToGenerate; +} + +bool AvHParticleTemplate::operator==(const AvHParticleTemplate& other) const +{ + bool result=false; + if ( this->mName == other.mName && + this->mMaxParticles == other.mMaxParticles && + this->mBaseColor == other.mBaseColor && + this->mSprite == other.mSprite && + this->mParticleSize == other.mParticleSize && + this->mParticleSystemLifetime == other.mParticleSystemLifetime && + this->mParticleLifetime == other.mParticleLifetime && + this->mGravity == other.mGravity && + this->mGenerationRate == other.mGenerationRate && + this->mGenerationShape == other.mGenerationShape && + this->mGenerationParams == other.mGenerationParams && + this->mGenerationEntityIndex == other.mGenerationEntityIndex && + this->mGenerationEntityParameter == other.mGenerationEntityParameter && + this->mGenerationEntityName == other.mGenerationEntityName && + this->mStartingVelocityShape == other.mStartingVelocityShape && + this->mStartingVelocityParams == other.mStartingVelocityParams && + this->mDeathShape == other.mDeathShape && + this->mDeathParams == other.mDeathParams && + this->mCollisionShape == other.mCollisionShape && + this->mCollisionParams == other.mCollisionParams && + this->mMinSpeed == other.mMinSpeed && + this->mMaxSpeed == other.mMaxSpeed && + this->mNumSpriteFrames == other.mNumSpriteFrames && + this->mAnimationSpeed == other.mAnimationSpeed && + this->mParticleScaling == other.mParticleScaling && + this->mRenderMode == other.mRenderMode && + this->mMaxAlpha == other.mMaxAlpha && + this->mFadeIn == other.mFadeIn && + this->mFlags == other.mFlags && + this->mParticleSystemToGenerate == other.mParticleSystemToGenerate && + this->mParticleSystemIndexToGenerate == other.mParticleSystemIndexToGenerate ) + result=true; + return result; +} + AvHParticleTemplateList::AvHParticleTemplateList() { } @@ -434,7 +507,8 @@ void AvHParticleTemplateList::Clear() { //for(ParticleTemplateListType::iterator theIter = this->mTemplateList.begin(); theIter != this->mTemplateList.end(); theIter++) - this->mTemplateList.clear(); + if ( this->mTemplateList.size() > 0 ) + this->mTemplateList.erase(this->mTemplateList.begin(), this->mTemplateList.end()); } int @@ -449,8 +523,8 @@ AvHParticleTemplateList::CreateTemplateFromIndex(int inBaseIndex) theNewTemplate = this->mTemplateList[inBaseIndex]; } - this->mTemplateList.push_back(theNewTemplate); - int theNewIndex = (int)this->mTemplateList.size() - 1; + int theNewIndex = (int)this->mTemplateList.size(); + this->mTemplateList[theNewIndex]=theNewTemplate; return theNewIndex; } @@ -473,9 +547,12 @@ AvHParticleTemplateList::GetTemplateAtIndex(uint32 inIndex) const { const AvHParticleTemplate* theTemplate = NULL; - if(inIndex < this->mTemplateList.size()) + ParticleTemplateListType::const_iterator theIter=this->mTemplateList.find(inIndex); + + + if(theIter != this->mTemplateList.end() ) { - theTemplate = &(this->mTemplateList[inIndex]); + theTemplate = &(theIter->second); } return theTemplate; @@ -491,7 +568,7 @@ AvHParticleTemplateList::GetTemplateIndexWithName(const string& inName, uint32& for(theIter = this->mTemplateList.begin(); theIter != this->mTemplateList.end(); theIter++, theIndex++) { - if(theIter->GetName() == inName) + if(theIter->second.GetName() == inName) { outIndex = theIndex; theSuccess = true; @@ -508,3 +585,5 @@ AvHParticleTemplateList::GetNumberTemplates() const return (uint32)this->mTemplateList.size(); } + + diff --git a/main/source/mod/AvHParticleTemplate.h b/main/source/mod/AvHParticleTemplate.h index ac79aa7..1f55b63 100644 --- a/main/source/mod/AvHParticleTemplate.h +++ b/main/source/mod/AvHParticleTemplate.h @@ -125,6 +125,8 @@ public: float GetMaxAlpha() const; void SetMaxAlpha(float inMaxAlpha); + virtual bool operator==(const AvHParticleTemplate& other) const; + virtual void operator=(const AvHParticleTemplate& other); private: string mName; @@ -195,7 +197,7 @@ public: protected: - typedef vector ParticleTemplateListType; + typedef map ParticleTemplateListType; ParticleTemplateListType mTemplateList; }; diff --git a/main/source/mod/AvHParticleTemplateClient.cpp b/main/source/mod/AvHParticleTemplateClient.cpp index cab736d..09f5b40 100644 --- a/main/source/mod/AvHParticleTemplateClient.cpp +++ b/main/source/mod/AvHParticleTemplateClient.cpp @@ -4,7 +4,7 @@ #include "cl_dll/demo.h" #include "common/demo_api.h" -int AvHParticleTemplateListClient::InitializeDemoPlayback(int inSize, unsigned char* inBuffer) +int AvHParticleTemplateListClient::InitializeDemoPlayback(int inSize, unsigned char* inBuffer, int index) { // Read one particle template and add it to the list int theBytesRead = 0; @@ -105,7 +105,7 @@ int AvHParticleTemplateListClient::InitializeDemoPlayback(int inSize, unsigned c theTemplate.SetMaxAlpha(theMaxAlpha); // Save the template - this->mTemplateList.push_back(theTemplate); + this->mTemplateList[index]=theTemplate; return theBytesRead; } @@ -231,7 +231,8 @@ void AvHParticleTemplateListClient::InitializeDemoRecording() const } } -void AvHParticleTemplateListClient::Insert(const AvHParticleTemplate& inTemplate) +void AvHParticleTemplateListClient::Insert(const AvHParticleTemplate& inTemplate, int index) { - this->mTemplateList.insert(this->mTemplateList.end(), inTemplate); -} \ No newline at end of file + this->mTemplateList[index]=inTemplate; +} + diff --git a/main/source/mod/AvHParticleTemplateClient.h b/main/source/mod/AvHParticleTemplateClient.h index e13721c..67c66bf 100644 --- a/main/source/mod/AvHParticleTemplateClient.h +++ b/main/source/mod/AvHParticleTemplateClient.h @@ -6,9 +6,9 @@ class AvHParticleTemplateListClient : public AvHParticleTemplateList { public: - int InitializeDemoPlayback(int inSize, unsigned char* inBuffer); + int InitializeDemoPlayback(int inSize, unsigned char* inBuffer, int index); void InitializeDemoRecording() const; - void Insert(const AvHParticleTemplate& inTemplate); + void Insert(const AvHParticleTemplate& inTemplate, int index); private: diff --git a/main/source/mod/AvHParticleTemplateServer.cpp b/main/source/mod/AvHParticleTemplateServer.cpp index eb3e828..a5ca4e4 100644 --- a/main/source/mod/AvHParticleTemplateServer.cpp +++ b/main/source/mod/AvHParticleTemplateServer.cpp @@ -29,6 +29,7 @@ AvHParticleTemplateListServer::CreateTemplates(const TRDescriptionList& inDescri bool theSuccess = false; TRDescriptionList::const_iterator theIterator; + int theIndex=this->mTemplateList.size(); for(theIterator = inDescriptions.begin(); theIterator != inDescriptions.end(); theIterator++) { if(theIterator->GetType() == kpscSystemName) @@ -179,7 +180,7 @@ AvHParticleTemplateListServer::CreateTemplates(const TRDescriptionList& inDescri //} // Add it on the end - this->mTemplateList.insert(this->mTemplateList.end(), theTemplate); + this->mTemplateList[theIndex++]=theTemplate; theSuccess = true; } @@ -217,7 +218,7 @@ AvHParticleTemplateListServer::GetTemplateIndexWithName(const string& inName, ui for(theIterator = this->mTemplateList.begin(); theIterator != this->mTemplateList.end(); theIterator++, theIndex++) { - string theLowercaseTemplateName = LowercaseString(theIterator->GetName()); + string theLowercaseTemplateName = LowercaseString(theIterator->second.GetName()); // Make case-insensitive? if(theLowercaseInName == theLowercaseTemplateName) diff --git a/main/source/mod/AvHPieMenuHandler.cpp b/main/source/mod/AvHPieMenuHandler.cpp index 0204ea6..b19c72c 100644 --- a/main/source/mod/AvHPieMenuHandler.cpp +++ b/main/source/mod/AvHPieMenuHandler.cpp @@ -1,422 +1,422 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHPieMenuHandler.cpp $ -// $Date: 2002/09/25 20:49:23 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHPieMenuHandler.cpp,v $ -// Revision 1.23 2002/09/25 20:49:23 Flayra -// - New select sound for aliens -// -// Revision 1.22 2002/09/23 22:24:16 Flayra -// - Removed NSTR #ifdefs -// -// Revision 1.21 2002/09/09 20:00:56 Flayra -// - Pop-up menu now stays open if you open and close it really fast (ie, right click) -// -// Revision 1.20 2002/08/31 18:01:02 Flayra -// - Work at VALVe -// -// Revision 1.19 2002/08/16 02:40:51 Flayra -// - HUD sounds no longer cut each other off (they won't play instead of cutting off another sound) -// -// Revision 1.18 2002/08/09 01:08:45 Flayra -// - Debugging code -// -// Revision 1.17 2002/07/08 17:12:49 Flayra -// - Renamed pop-up menu command, reworked it to be a regular bind -// -//=============================================================================== -#include "util/nowarnings.h" -#include "ui/PieMenu.h" -#include "ui/PieNode.h" -#include "mod/AvHPieMenuHandler.h" -#include "mod/AvHTeamHierarchy.h" -#include "mod/AvHMessage.h" -#include "mod/AvHClientVariables.h" -#include "mod/AvHCommandConstants.h" -#include "engine/cdll_int.h" -#include "types.h" -#include -using std::string; - -#include "cl_dll/demo.h" -#include "common/demo_api.h" - -void IN_ResetMouse(); - -extern int g_weaponselect; -extern int in_impulse; -bool sTheDebugBool = false; - -PieNode* AvHPieMenuHandler::sLastNodeHighlighted = NULL; -string AvHPieMenuHandler::sPieMenuName = ""; -float AvHPieMenuHandler::sTimeLastNodeHighlighted = 0.0f; -float AvHPieMenuHandler::sTimeMenuOpened = 0.0f; -bool AvHPieMenuHandler::sPieMenuOpen = false; - -bool AvHPieMenuHandler::GetIsPieMenuOpen(void) -{ - return sPieMenuOpen; -} - -PieMenu* AvHPieMenuHandler::GetActivePieMenu() -{ - PieMenu* thePieMenu = NULL; - - gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, thePieMenu); - - return thePieMenu; -} - -void AvHPieMenuHandler::ClosePieMenu(void) -{ - - if (!sPieMenuOpen) - { - return; - } - - //CenterPrint("AvHPieMenuHandler::closePieMenu.\n"); - - // If the action was really quick, choose the highlighted node so overshooting isn't a problem - PieNode* theNode = NULL; - - float theCurrentTime = gEngfuncs.GetClientTime(); - float theQuickThreshold = cl_quickselecttime->value; - bool theReallyQuick = (theCurrentTime - sTimeLastNodeHighlighted < theQuickThreshold); - if(theReallyQuick) - { - //CenterPrint("Quick mode.\n"); - PieMenu* thePieMenu = NULL; - if(gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, thePieMenu)) - { - thePieMenu->GetSelectedNode(theNode); - } - } - else - { - //CenterPrint("Regular mode.\n"); - theNode = dynamic_cast(sLastNodeHighlighted); - } - - if(theNode) - { - NodeChosen(theNode->GetNodeName(), theNode->GetMessageID()); - } - else - { - NodeCancelled(); - } - - // Reset the mouse cursor to the center of the screen so - // that the view doesn't jog once the pie menu is closed. - - IN_ResetMouse(); - gHUD.ShowCrosshair(); - - sPieMenuOpen = false; - -} - -void AvHPieMenuHandler::InternalClosePieMenu(void) -{ - PieMenu* theMarineMenu = NULL; - - if(gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, theMarineMenu)) - { - // TODO: Select option on menu before closing it! - if(!gHUD.GetInTopDownMode()) - { - gHUD.GetManager().SetMouseVisibility(false); - } - - theMarineMenu->SetFadeState(false); - if(sLastNodeHighlighted) - { - sLastNodeHighlighted->SetDrawSelected(false); - } - sLastNodeHighlighted = NULL; - -// if(sTheDebugBool) -// { -// AvHTeamHierarchy* theHierarchyComponent = NULL; -// if(gHUD.GetManager().GetVGUIComponentNamed(kHierarchy, theHierarchyComponent)) -// { -// theHierarchyComponent->setVisible(true); -// } -// } - - } - -} - -void AvHPieMenuHandler::OpenPieMenu(void) -{ - PieMenu* theMarineMenu = NULL; - - //CenterPrint("AvHPieMenuHandler::openPieMenu.\n"); - - // Pie menu only active when playing - AvHUser3 theUser3 = gHUD.GetHUDUser3(); - if(theUser3 > AVH_USER3_NONE && theUser3 <= AVH_USER3_ALIEN_PLAYER5) - { - if(gHUD.GetPlayMode() == PLAYMODE_PLAYING) - { - if(gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, theMarineMenu)) - { - if(!gHUD.GetInTopDownMode()) - { - gHUD.GetManager().SetMouseVisibility(true); - } - - gHUD.HideCrosshair(); - - // Only do this when in full screen - //App::getInstance()->setCursorPos(ScreenWidth/2, ScreenHeight/2); - - theMarineMenu->SetFadeState(true); - sLastNodeHighlighted = theMarineMenu->GetRootNode(); - sLastNodeHighlighted->SetDrawSelected(true); - sTimeMenuOpened = gEngfuncs.GetClientTime(); - sTimeLastNodeHighlighted = sTimeMenuOpened; - sPieMenuOpen = true; - - - // if(sTheDebugBool) - // { - // AvHTeamHierarchy* theHierarchyComponent = NULL; - // if(gHUD.GetManager().GetVGUIComponentNamed(kHierarchy, theHierarchyComponent)) - // { - // theHierarchyComponent->setVisible(false); - // } - // } - } - } - } -} - -void AvHPieMenuHandler::NodeCancelled() -{ - InternalClosePieMenu(); -} - -void AvHPieMenuHandler::NodeChosen(const string& /*inNodeName*/, int inMessageID) -{ -// char* theSound = kSelectSound; -// if(gHUD.GetIsAlien()) -// { -// theSound = kSelectAlienSound; -// } -// gHUD.PlayHUDSound(theSound, .3f); - - gHUD.PlayHUDSound(HUD_SOUND_SELECT); - - // Client-side effects - switch(inMessageID) - { - case COMM_CHAT_PUBLIC: - // TODO: Pop up message saying to hit enter to send or escape to cancel message - ClientCmd(kcGlobalChat); - break; - - case COMM_CHAT_TEAM: - // TODO: Pop up message saying to hit enter to send or escape to cancel message - ClientCmd(kcTeamChat); - break; - - case COMM_CHAT_NEARBY: - ClientCmd(kcNearbyChat); - break; - - default: - in_impulse = inMessageID; - break; - } - - InternalClosePieMenu(); -} - -void AvHPieMenuHandler::NodeActivated(const string& inNodeName) -{ -} - -string AvHPieMenuHandler::GetPieMenuControl() -{ - return sPieMenuName; -} - -void AvHPieMenuHandler::SetPieMenuControl(const string& inPieMenuName) -{ - sPieMenuName = inPieMenuName; -} - -void AvHPieMenuHandler::cursorMoved(int x,int y,Panel* panel) -{ -// char theMessage[128]; -// sprintf(theMessage, "AvHPieMenuHandler::cursorMoved %d, %d (panel ptr: %d).\n", x, y, (int)panel); -// CenterPrint(theMessage); -} - -void AvHPieMenuHandler::cursorEntered(Panel* panel) -{ - PieNode* theNode = dynamic_cast(panel); - if(theNode) - { - if(theNode->GetFadeState()) - { - if((theNode->IsAdjacentTo(sLastNodeHighlighted)) || (theNode->GetIsAbove(sLastNodeHighlighted))) - { - //char theTempBuffer[256]; - //sprintf(theTempBuffer, "Cursor entered %s.\n", theNode->GetNodeName().c_str()); - //CenterPrint(theTempBuffer); - - // Check if enabled - //if(theNode->GetEnabled()) - //{ - //theNode->SetAllChildrenFadeState(false); - if(theNode->HighlightNode()) - { - if(!theNode->GetIsAbove(sLastNodeHighlighted)) - { - if(gHUD.GetIsAlien()) - { - gHUD.PlayHUDSound(kPieSelectForwardAlienSound, kHUDSoundVolume); - } - else - { - gHUD.PlayHUDSound(kPieSelectForwardSound, kHUDSoundVolume); - } - } - else - { - if(gHUD.GetIsAlien()) - { - gHUD.PlayHUDSound(kPieSelectBackwardAlienSound, kHUDSoundVolume); - } - else - { - gHUD.PlayHUDSound(kPieSelectBackwardSound, kHUDSoundVolume); - } - } - - sLastNodeHighlighted->SetDrawSelected(false); - - sLastNodeHighlighted = theNode; - - sTimeLastNodeHighlighted = gEngfuncs.GetClientTime(); - - theNode->SetDrawSelected(true); - } - //} - } - } - } -} - -void AvHPieMenuHandler::cursorExited(Panel* panel) -{ -// CenterPrint("AvHPieMenuHandler::cursorExited.\n"); - -// PieNode* theNode = dynamic_cast(panel); -// if(theNode) -// { -// char theTempBuffer[256]; -// sprintf(theTempBuffer, "Cursor exited %s.\n", theNode->GetNodeName().c_str()); -// CenterPrint(theTempBuffer); -// -// theNode->SetNodeAndAdjacentChildrenFadeState(false); -// } -} - -void AvHPieMenuHandler::mousePressed(MouseCode code,Panel* panel) -{ -// CenterPrint("AvHPieMenuHandler::mousePressed.\n"); -} - -void AvHPieMenuHandler::mouseDoublePressed(MouseCode code,Panel* panel) -{ -// CenterPrint("AvHPieMenuHandler::mouseDoublePressed.\n"); -} - -void AvHPieMenuHandler::mouseReleased(MouseCode code, Panel* inPanel) -{ - - // CenterPrint("AvHPieMenuHandler::mouseReleased.\n"); - - - -// -// if(code == MOUSE_RIGHT) -// { -// // If the action was really quick, choose the highlighted node so overshooting isn't a problem - PieMenu* thePieMenu = NULL; - PieNode* theNode = NULL; - - float theCurrentTime = gEngfuncs.GetClientTime(); - float kQuickThreshold = cl_quickselecttime->value; - bool theReallyQuick = (theCurrentTime - sTimeLastNodeHighlighted < kQuickThreshold); - if(theReallyQuick) - { - //CenterPrint("Quick mode.\n"); - if(gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, thePieMenu)) - { - thePieMenu->GetSelectedNode(theNode); - } - } - else - { - //CenterPrint("Regular mode.\n"); - theNode = dynamic_cast(inPanel); - } - - if(theNode) - { - // Don't close menu if they released over the root node and it was really quick - if(!thePieMenu || !(theNode == thePieMenu->GetRootNode()) || !((theCurrentTime - sTimeMenuOpened) < .3f)) - { - NodeChosen(theNode->GetNodeName(), theNode->GetMessageID()); - } - } - else - { - NodeCancelled(); - } - - // puzl : 983 releasing a mouse closes the popup menu - if ( code == MOUSE_RIGHT || code == MOUSE_LEFT || code == MOUSE_MIDDLE) - { - ClientCmd("-popupmenu"); - ClosePieMenu(); - } -// } -} - -void AvHPieMenuHandler::mouseWheeled(int delta,Panel* panel) -{ -} - -void AvHPieMenuHandler::keyPressed(KeyCode code,Panel* panel) -{ -} - -void AvHPieMenuHandler::keyTyped(KeyCode code,Panel* panel) -{ -} - -void AvHPieMenuHandler::keyReleased(KeyCode code,Panel* panel) -{ -} - -void AvHPieMenuHandler::keyFocusTicked(Panel* panel) -{ -} - - +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHPieMenuHandler.cpp $ +// $Date: 2002/09/25 20:49:23 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHPieMenuHandler.cpp,v $ +// Revision 1.23 2002/09/25 20:49:23 Flayra +// - New select sound for aliens +// +// Revision 1.22 2002/09/23 22:24:16 Flayra +// - Removed NSTR #ifdefs +// +// Revision 1.21 2002/09/09 20:00:56 Flayra +// - Pop-up menu now stays open if you open and close it really fast (ie, right click) +// +// Revision 1.20 2002/08/31 18:01:02 Flayra +// - Work at VALVe +// +// Revision 1.19 2002/08/16 02:40:51 Flayra +// - HUD sounds no longer cut each other off (they won't play instead of cutting off another sound) +// +// Revision 1.18 2002/08/09 01:08:45 Flayra +// - Debugging code +// +// Revision 1.17 2002/07/08 17:12:49 Flayra +// - Renamed pop-up menu command, reworked it to be a regular bind +// +//=============================================================================== +#include "util/nowarnings.h" +#include "ui/PieMenu.h" +#include "ui/PieNode.h" +#include "mod/AvHPieMenuHandler.h" +#include "mod/AvHTeamHierarchy.h" +#include "mod/AvHMessage.h" +#include "mod/AvHClientVariables.h" +#include "mod/AvHCommandConstants.h" +#include "engine/cdll_int.h" +#include "types.h" +#include +using std::string; + +#include "cl_dll/demo.h" +#include "common/demo_api.h" + +void IN_ResetMouse(); + +extern int g_weaponselect; +extern int in_impulse; +bool sTheDebugBool = false; + +PieNode* AvHPieMenuHandler::sLastNodeHighlighted = NULL; +string AvHPieMenuHandler::sPieMenuName = ""; +float AvHPieMenuHandler::sTimeLastNodeHighlighted = 0.0f; +float AvHPieMenuHandler::sTimeMenuOpened = 0.0f; +bool AvHPieMenuHandler::sPieMenuOpen = false; + +bool AvHPieMenuHandler::GetIsPieMenuOpen(void) +{ + return sPieMenuOpen; +} + +PieMenu* AvHPieMenuHandler::GetActivePieMenu() +{ + PieMenu* thePieMenu = NULL; + + gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, thePieMenu); + + return thePieMenu; +} + +void AvHPieMenuHandler::ClosePieMenu(void) +{ + + if (!sPieMenuOpen) + { + return; + } + + //CenterPrint("AvHPieMenuHandler::closePieMenu.\n"); + + // If the action was really quick, choose the highlighted node so overshooting isn't a problem + PieNode* theNode = NULL; + + float theCurrentTime = gEngfuncs.GetClientTime(); + float theQuickThreshold = cl_quickselecttime->value; + bool theReallyQuick = (theCurrentTime - sTimeLastNodeHighlighted < theQuickThreshold); + if(theReallyQuick) + { + //CenterPrint("Quick mode.\n"); + PieMenu* thePieMenu = NULL; + if(gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, thePieMenu)) + { + thePieMenu->GetSelectedNode(theNode); + } + } + else + { + //CenterPrint("Regular mode.\n"); + theNode = dynamic_cast(sLastNodeHighlighted); + } + + if(theNode) + { + NodeChosen(theNode->GetNodeName(), theNode->GetMessageID()); + } + else + { + NodeCancelled(); + } + + // Reset the mouse cursor to the center of the screen so + // that the view doesn't jog once the pie menu is closed. + + IN_ResetMouse(); + gHUD.ShowCrosshair(); + + sPieMenuOpen = false; + +} + +void AvHPieMenuHandler::InternalClosePieMenu(void) +{ + PieMenu* theMarineMenu = NULL; + + if(gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, theMarineMenu)) + { + // TODO: Select option on menu before closing it! + if(!gHUD.GetInTopDownMode()) + { + gHUD.GetManager().SetMouseVisibility(false); + } + + theMarineMenu->SetFadeState(false); + if(sLastNodeHighlighted) + { + sLastNodeHighlighted->SetDrawSelected(false); + } + sLastNodeHighlighted = NULL; + +// if(sTheDebugBool) +// { +// AvHTeamHierarchy* theHierarchyComponent = NULL; +// if(gHUD.GetManager().GetVGUIComponentNamed(kHierarchy, theHierarchyComponent)) +// { +// theHierarchyComponent->setVisible(true); +// } +// } + + } + +} + +void AvHPieMenuHandler::OpenPieMenu(void) +{ + PieMenu* theMarineMenu = NULL; + + //CenterPrint("AvHPieMenuHandler::openPieMenu.\n"); + + // Pie menu only active when playing + AvHUser3 theUser3 = gHUD.GetHUDUser3(); + if(theUser3 > AVH_USER3_NONE && theUser3 <= AVH_USER3_ALIEN_PLAYER5) + { + if(gHUD.GetPlayMode() == PLAYMODE_PLAYING) + { + if(gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, theMarineMenu)) + { + if(!gHUD.GetInTopDownMode()) + { + gHUD.GetManager().SetMouseVisibility(true); + } + + gHUD.HideCrosshair(); + + // Only do this when in full screen + //App::getInstance()->setCursorPos(ScreenWidth/2, ScreenHeight/2); + + theMarineMenu->SetFadeState(true); + sLastNodeHighlighted = theMarineMenu->GetRootNode(); + sLastNodeHighlighted->SetDrawSelected(true); + sTimeMenuOpened = gEngfuncs.GetClientTime(); + sTimeLastNodeHighlighted = sTimeMenuOpened; + sPieMenuOpen = true; + + + // if(sTheDebugBool) + // { + // AvHTeamHierarchy* theHierarchyComponent = NULL; + // if(gHUD.GetManager().GetVGUIComponentNamed(kHierarchy, theHierarchyComponent)) + // { + // theHierarchyComponent->setVisible(false); + // } + // } + } + } + } +} + +void AvHPieMenuHandler::NodeCancelled() +{ + InternalClosePieMenu(); +} + +void AvHPieMenuHandler::NodeChosen(const string& /*inNodeName*/, int inMessageID) +{ +// char* theSound = kSelectSound; +// if(gHUD.GetIsAlien()) +// { +// theSound = kSelectAlienSound; +// } +// gHUD.PlayHUDSound(theSound, .3f); + + gHUD.PlayHUDSound(HUD_SOUND_SELECT); + + // Client-side effects + switch(inMessageID) + { + case COMM_CHAT_PUBLIC: + // TODO: Pop up message saying to hit enter to send or escape to cancel message + ClientCmd(kcGlobalChat); + break; + + case COMM_CHAT_TEAM: + // TODO: Pop up message saying to hit enter to send or escape to cancel message + ClientCmd(kcTeamChat); + break; + + case COMM_CHAT_NEARBY: + ClientCmd(kcNearbyChat); + break; + + default: + in_impulse = inMessageID; + break; + } + + InternalClosePieMenu(); +} + +void AvHPieMenuHandler::NodeActivated(const string& inNodeName) +{ +} + +string AvHPieMenuHandler::GetPieMenuControl() +{ + return sPieMenuName; +} + +void AvHPieMenuHandler::SetPieMenuControl(const string& inPieMenuName) +{ + sPieMenuName = inPieMenuName; +} + +void AvHPieMenuHandler::cursorMoved(int x,int y,Panel* panel) +{ +// char theMessage[128]; +// sprintf(theMessage, "AvHPieMenuHandler::cursorMoved %d, %d (panel ptr: %d).\n", x, y, (int)panel); +// CenterPrint(theMessage); +} + +void AvHPieMenuHandler::cursorEntered(Panel* panel) +{ + PieNode* theNode = dynamic_cast(panel); + if(theNode) + { + if(theNode->GetFadeState()) + { + if((theNode->IsAdjacentTo(sLastNodeHighlighted)) || (theNode->GetIsAbove(sLastNodeHighlighted))) + { + //char theTempBuffer[256]; + //sprintf(theTempBuffer, "Cursor entered %s.\n", theNode->GetNodeName().c_str()); + //CenterPrint(theTempBuffer); + + // Check if enabled + //if(theNode->GetEnabled()) + //{ + //theNode->SetAllChildrenFadeState(false); + if(theNode->HighlightNode()) + { + if(!theNode->GetIsAbove(sLastNodeHighlighted)) + { + if(gHUD.GetIsAlien()) + { + gHUD.PlayHUDSound(kPieSelectForwardAlienSound, kHUDSoundVolume); + } + else + { + gHUD.PlayHUDSound(kPieSelectForwardSound, kHUDSoundVolume); + } + } + else + { + if(gHUD.GetIsAlien()) + { + gHUD.PlayHUDSound(kPieSelectBackwardAlienSound, kHUDSoundVolume); + } + else + { + gHUD.PlayHUDSound(kPieSelectBackwardSound, kHUDSoundVolume); + } + } + + sLastNodeHighlighted->SetDrawSelected(false); + + sLastNodeHighlighted = theNode; + + sTimeLastNodeHighlighted = gEngfuncs.GetClientTime(); + + theNode->SetDrawSelected(true); + } + //} + } + } + } +} + +void AvHPieMenuHandler::cursorExited(Panel* panel) +{ +// CenterPrint("AvHPieMenuHandler::cursorExited.\n"); + +// PieNode* theNode = dynamic_cast(panel); +// if(theNode) +// { +// char theTempBuffer[256]; +// sprintf(theTempBuffer, "Cursor exited %s.\n", theNode->GetNodeName().c_str()); +// CenterPrint(theTempBuffer); +// +// theNode->SetNodeAndAdjacentChildrenFadeState(false); +// } +} + +void AvHPieMenuHandler::mousePressed(MouseCode code,Panel* panel) +{ +// CenterPrint("AvHPieMenuHandler::mousePressed.\n"); +} + +void AvHPieMenuHandler::mouseDoublePressed(MouseCode code,Panel* panel) +{ +// CenterPrint("AvHPieMenuHandler::mouseDoublePressed.\n"); +} + +void AvHPieMenuHandler::mouseReleased(MouseCode code, Panel* inPanel) +{ + + // CenterPrint("AvHPieMenuHandler::mouseReleased.\n"); + + + +// +// if(code == MOUSE_RIGHT) +// { +// // If the action was really quick, choose the highlighted node so overshooting isn't a problem + PieMenu* thePieMenu = NULL; + PieNode* theNode = NULL; + + float theCurrentTime = gEngfuncs.GetClientTime(); + float kQuickThreshold = cl_quickselecttime->value; + bool theReallyQuick = (theCurrentTime - sTimeLastNodeHighlighted < kQuickThreshold); + if(theReallyQuick) + { + //CenterPrint("Quick mode.\n"); + if(gHUD.GetManager().GetVGUIComponentNamed(sPieMenuName, thePieMenu)) + { + thePieMenu->GetSelectedNode(theNode); + } + } + else + { + //CenterPrint("Regular mode.\n"); + theNode = dynamic_cast(inPanel); + } + + if(theNode) + { + // Don't close menu if they released over the root node and it was really quick + if(!thePieMenu || !(theNode == thePieMenu->GetRootNode()) || !((theCurrentTime - sTimeMenuOpened) < .3f)) + { + NodeChosen(theNode->GetNodeName(), theNode->GetMessageID()); + } + } + else + { + NodeCancelled(); + } + + // : 983 releasing a mouse closes the popup menu + //if ( code == MOUSE_RIGHT || code == MOUSE_LEFT || code == MOUSE_MIDDLE) + //{ + // ClientCmd("-popupmenu"); + // ClosePieMenu(); + //} +// } +} + +void AvHPieMenuHandler::mouseWheeled(int delta,Panel* panel) +{ +} + +void AvHPieMenuHandler::keyPressed(KeyCode code,Panel* panel) +{ +} + +void AvHPieMenuHandler::keyTyped(KeyCode code,Panel* panel) +{ +} + +void AvHPieMenuHandler::keyReleased(KeyCode code,Panel* panel) +{ +} + +void AvHPieMenuHandler::keyFocusTicked(Panel* panel) +{ +} + + diff --git a/main/source/mod/AvHPlayer.cpp b/main/source/mod/AvHPlayer.cpp index e85757d..3ac6182 100644 --- a/main/source/mod/AvHPlayer.cpp +++ b/main/source/mod/AvHPlayer.cpp @@ -1,10118 +1,10473 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHPlayer.cpp $ -// $Date: 2002/11/22 21:18:24 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHPlayer.cpp,v $ -// Revision 1.86 2002/11/22 21:18:24 Flayra -// - Potentially fixed strange Onos collision crash -// - Don't allow player to join team after he's seen another team -// - "lastinv" support -// - Fixed perma-cloak bug after losing last sensory chamber whilst cloaked -// - Started fixing commander PAS problem -// - Fixed readyroom "ghost player" exploit when F4 during REIN -// - Draw damage in debug, never otherwise -// -// Revision 1.85 2002/11/15 04:42:50 Flayra -// - Regenerate now returns true if healing was successful -// - Logging changes and fixes -// -// Revision 1.84 2002/11/13 01:49:08 Flayra -// - Proper death message logging for Psychostats -// -// Revision 1.83 2002/11/12 22:39:25 Flayra -// - Logging changes for Psychostats compatibility -// -// Revision 1.82 2002/11/12 18:44:54 Flayra -// - Added mp_logdetail support for damage messages -// - Changed the alien ability anti-exploit code to try to co-exist with scripters -// -// Revision 1.81 2002/11/12 02:28:39 Flayra -// - Fixed problems with armor not being updated when armor upgrades completed -// - Aliens now keep same percentage of health and armor when morphing -// - Much better logging, up to standard -// - Don't add enemy buildings to hive sight unless parasited (solves blip spam) -// - Removed draw damage from public build -// - Changes to minimap to less overflows at end of big games -// -// Revision 1.80 2002/11/06 01:38:54 Flayra -// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) -// - Regeneration update -// -// Revision 1.79 2002/11/05 06:17:26 Flayra -// - Balance changes -// -// Revision 1.78 2002/10/28 20:36:18 Flayra -// - Updated auth mask -// -// Revision 1.77 2002/10/25 21:48:01 Flayra -// - Added more auth masks -// - Moved mSkin to pev->skin so playerclass could hold what used to be mPlayMode could be used to quickly perform culling, to try to prevent overflowage at the end of large games -// -// Revision 1.76 2002/10/24 21:40:02 Flayra -// - Reworked jetpack effect -// - Authicons update -// - Set builder for alien buildings, so turret kills can credit builder -// - Alien easter eggs -// - Network optimizations after game reset on huge (20-32) player games -// - Allow server ops to disable auth icons -// - Alien energy tweaks -// - Show unbuilt hives in hive sight -// - Move alien energy updating fully into shared code -// - Tried to fix full health ring showing for dead selected players -// - Moved help text client-side -// - Cache info_locations and gamma until map change -// - Skin fixes -// -// Revision 1.75 2002/10/20 21:10:57 Flayra -// - Optimizations -// -// Revision 1.74 2002/10/20 16:36:37 Flayra -// - Code optimization! Took forever to find. -// -// Revision 1.73 2002/10/20 02:36:14 Flayra -// - Regular update -// -// Revision 1.72 2002/10/19 22:33:44 Flayra -// - Various server optimizations -// -// Revision 1.71 2002/10/18 22:21:54 Flayra -// - Limit alien buildings in sphere -// - Fix various spawning problems (morphing as level 5, redemption for level 5) -// - Max motd length fix -// -// Revision 1.70 2002/10/17 17:34:09 Flayra -// - Authmask update -// - Part 1 of persistent weapons fix (found with Grendel) -// -// Revision 1.69 2002/10/16 20:55:40 Flayra -// - Debug code for tracking down death animation problems -// - Updated auth masks -// -// Revision 1.68 2002/10/16 01:05:38 Flayra -// - Sent health as short for big aliens -// - Fixed sayings not triggering commander alerts -// - Now name changes are queued until the next match -// - Added authmask support -// - Egg idle sounds play more frequently, refactored too -// - Fixed preserving model in ready room after game end -// - Profiling of AddToFullPack -// - Fix for falling through lifts when morphing on them (untested) -// -// Revision 1.67 2002/10/04 18:04:07 Flayra -// - Fixed floating gestation sacs -// - Aliens now fall all the way to ground during countdown (instead of floating and shaking) -// -// Revision 1.66 2002/10/03 20:24:39 Flayra -// - Changes for "more resources required" -// -// Revision 1.65 2002/10/03 19:32:06 Flayra -// - Reworked orders completely -// - Send max resources to players -// - Heavy armor sped up slightly -// - Kill players who illegally try to use alien abilities -// - Moved energy to a new variable, send health for aliens -// - Only freeze players during countdown -// - Removed slowdown when taking damage -// - Send blips in two messages, one friendly and one enemy (old bad hack) -// -// Revision 1.64 2002/09/25 20:50:03 Flayra -// - Added 3 new sayings -// - Frame-rate independent updating -// - Don't allow player to kill self while commanding -// -// Revision 1.63 2002/09/23 22:27:52 Flayra -// - Added skin support -// - Added client connected/disconnected hooks for particle system propagation optimizations -// - Removed power armor, added heavy armor -// - Fixed death animations -// - Added hook to see if commander has given an order and to see if he's idle -// - Bound resources for aliens -// - Soldiers asking for ammo and health trigger commander alert -// - Added gestation anims -// - Slowed down Onos movement -// - When cheats are enabled, purchases are free -// -// Revision 1.62 2002/09/09 20:04:53 Flayra -// - Added commander voting -// - Commander score is now the average of the rest of his players (reverted back when he leaves CC) -// - Fixed bug where upgrades were getting removed and then add repeatedly -// - Added multiple skins for marines -// - Play sound when aliens lose an upgrade -// - Changed fov to 90 for all aliens for software compatibility -// - Added hiveinfo drawing -// - Tweaked marine and alien speeds (to compensate for loss of drastic alien fov) -// -// Revision 1.61 2002/08/31 18:01:02 Flayra -// - Work at VALVe -// -// Revision 1.60 2002/08/16 02:42:57 Flayra -// - New damage types -// - Much more efficient blip calculation (at least it should be, I haven't measured it so who really knows) -// - New way of representing ensnare state for shared code (disabling jumping, jetpacking) -// - Removed old overwatch code -// - Store health in fuser2 for drawing health for commander -// - Swap bile bomb and umbra -// -// Revision 1.59 2002/08/09 01:10:30 Flayra -// - Keep previous model when a game is over and going back to ready room -// - Refactoring for scoreboard -// - Support for "jump" animation -// - Freeze player before game starts -// - Reset score when leaving a team -// -// Revision 1.58 2002/08/02 21:52:18 Flayra -// - Cleaned up jetpacks, made webbing useful again, help messages, added "orderself" cheat, changed gestation messages for new tooltip system, added GetHasAvailableUpgrades() for help system, removed particle template messages, slowed down particle template update rate to reduce overflows (woo!) -// -// Revision 1.57 2002/07/28 19:21:28 Flayra -// - Balance changes after/during RC4a -// -// Revision 1.56 2002/07/26 23:07:53 Flayra -// - Numerical feedback -// - New artwork for marine, with jetpack as body group (this code doesn't work) -// - Misc. fixes (alien vision mode not being preserved when it should, and staying when it shouldn't) -// -// Revision 1.55 2002/07/24 18:45:42 Flayra -// - Linux and scripting changes -// -// Revision 1.54 2002/07/23 17:17:57 Flayra -// - Added power armor, added "talking" state for hive sight, changes for new resource model, removed max buildings, create buildings with random orientation, added stimpack, tweaked redemption, added new hive sight info -// -// Revision 1.53 2002/07/10 14:44:39 Flayra -// - Draw chat text while dead (bug #280) -// -// Revision 1.52 2002/07/08 17:15:39 Flayra -// - Added validation of all impulses (assumes invalid by default, instead of valid), reset players like all other entities, ensnaring fixes, players are temporarily invulnerable when they spawn, preserve health and armor when using command station, fixed up redemption, add hooks for commander voting and going back to the ready room, models can't be changed via the console anymore -// -// Revision 1.51 2002/07/01 22:41:40 Flayra -// - Removed outdated overwatch target and tension events -// -// Revision 1.50 2002/07/01 21:43:16 Flayra -// - Removed outdated adrenaline concept, added new alien sight mode, primal scream, removed flashlight battery message, fixed morphing problems for level 5 (and others), tweaked marine and alien speeds, fixed triple speed upgrade problem, disabled overwatch, moved player assets out of precache() (wasn't being called reliably), get full energy after a lifeform change, hive sight only draws out of sight now, added scent of fear -// -// Revision 1.49 2002/06/25 18:13:57 Flayra -// - Added death animations, added more general animation support, leaps do damage, general construct effects, new alien inventory system, added charging, info_locations, galloping, new alien building code, better morphing system (prevents getting stuck), more builder error messages, clear deaths on game end, hide health while gestating -// -// Revision 1.48 2002/06/10 20:03:13 Flayra -// - Updated for new minimap (remember killed position). Updated blood so marines aren't bloody, and aliens emit yellow blood. Removed slowing when hit (now players fly back when hit though), UpdateBlips() hack (fix when time) -// -// Revision 1.47 2002/06/03 16:54:59 Flayra -// - Added more effective player classes for scoreboard, send player class every time role changes (more network usage, always updated), changed hive sight to always be visible when under attack, all entities added to hive sight, not just players -// -// Revision 1.46 2002/05/28 18:03:40 Flayra -// - Refactored size for role code for movement chambers, deal with inventory properly (entity leak), increased web ensnaring effects (put weapon away!), reinforcement refactoring, tweaked speeds so marines are a little slower, and speed upgrade works properly again (now level 1 can generally outrun marines), added recycling support, play destroy egg sound when killed when morphing, new hive sight, EffectivePlayerClassChanged() refactoring -// -// Revision 1.45 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "util/nowarnings.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHMessage.h" -#include "mod/AvHParticleTemplateServer.h" -#include "mod/AvHEntities.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHConstants.h" -#include "mod/AvHMarineWeapons.h" -#include "dlls/client.h" -#include "dlls/util.h" -#include "mod/AvHSoundListManager.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHTitles.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHParticleTemplate.h" -#include "common/vector_util.h" -#include "dlls/roach.h" -#include "mod/AvHSelectionHelper.h" -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHDramaticPriority.h" -#include "mod/AvHHulls.h" -#include "mod/AvHMovementUtil.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHParticleSystemEntity.h" -#include "mod/AvHAlienAbilities.h" -#include "mod/AvHAlienAbilityConstants.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "mod/AvHMarineTurret.h" -#include "mod/AvHSiegeTurret.h" -#include "mod/AvHBlipConstants.h" -#include "mod/AvHParticleConstants.h" -#include "util/MathUtil.h" -#include "types.h" - -#include "mod/AvHNetworkMessages.h" -#include "mod/AvHNexusServer.h" - -std::string GetLogStringForPlayer( edict_t *pEntity ); - -extern int gJetpackEventID; -extern int gAlienSightOnEventID; -extern int gAlienSightOffEventID; -extern int gStartOverwatchEventID; -extern int gEndOverwatchEventID; -extern int gRegenerationEventID; -extern int gStartCloakEventID; -extern int gEndCloakEventID; -extern int gNumFullPackCalls; -extern int gWeaponAnimationEventID; -extern int gMetabolizeSuccessEventID; -extern int gPhaseInEventID; - -// Yucky globals -extern AvHParticleTemplateListServer gParticleTemplateList; -extern AvHSoundListManager gSoundListManager; -extern cvar_t allow_spectators; -extern cvar_t avh_marinereinforcementcost; -#ifdef USE_OLDAUTH -extern cvar_t avh_uplink; -#endif -#ifdef DEBUG -extern cvar_t avh_spawninvulnerabletime; -#endif - -AvHSelectionHelper gSelectionHelper; -extern vec3_t gPMDebugPoint; -extern void ResetPlayerPVS( edict_t *client, int clientnum ); - -LINK_ENTITY_TO_CLASS( player, AvHPlayer ); - -const float kTransitionFadeTime = .6f; -const float kSpawnInFadeTime = .9f; - -AvHPlayer::AvHPlayer() -{ - this->Init(); - //TODO: find out lifecycle of entity vs. lifecycle of client and reset only when we have a new client. - this->InitBalanceVariables(); -} - -void AvHPlayer::AddDebugEnemyBlip(float inX, float inY, float inZ) -{ - this->mEnemyBlips.AddBlip(inX, inY, inZ); -} - -void AvHPlayer::AddPoints( int score, BOOL bAllowNegativeScore ) -{ - // Positive score always adds - if ( score < 0 ) - { - if ( !bAllowNegativeScore ) - { - if ( this->mScore < 0 ) // Can't go more negative - return; - - if ( -score > this->mScore ) // Will this go negative? - { - score = -this->mScore; // Sum will be 0 - } - } - } - - this->mScore += score; - - this->EffectivePlayerClassChanged(); -} - -void AvHPlayer::AwardKill( entvars_t* inTargetPEV) -{ - // Don't award points in new resource model - //CBaseEntity* inTarget = CBaseEntity::Instance(inTargetPEV); - //GetGameRules()->AwardPointsToPlayer(this, inTarget); -} - -int AvHPlayer::BloodColor(void) -{ - int theBloodColor = DONT_BLEED; - - if(this->GetIsMarine() && !this->GetHasHeavyArmor()) - { - theBloodColor = BLOOD_COLOR_RED; - } - else if(this->GetIsAlien()) - { - theBloodColor = BLOOD_COLOR_YELLOW; - } - - return theBloodColor; -} - -void AvHPlayer::AcquireOverwatchTarget() -{ - ASSERT(this->mInOverwatch); - - // Find closest enemy in FOV - - // Find cockroaches for now - CBaseEntity* theCurrentTarget = NULL; - float theCurrentRange = 1000000; - - //FOR_ALL_ENTITIES(kRoachClassName, CRoach*) - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(this->pev->team != theEntity->pev->team) - { - float theDistance = (theEntity->pev->origin - this->pev->origin).Length(); - if(this->GetIsEntityInSight(theEntity) && (theDistance <= theCurrentRange) && theEntity->GetIsRelevant()) - { - theCurrentTarget = theEntity; - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - //END_FOR_ALL_ENTITIES(kRoachClassName) - - if(theCurrentTarget) - { - this->mOverwatchTarget = ENTINDEX(theCurrentTarget->edict()); - this->pev->fuser1 = this->mOverwatchTarget; - - // Playback target event - //PLAYBACK_EVENT_FULL(0, this->edict(), gTargetOverwatchEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - } -} - -bool AvHPlayer::AttemptToBuildAlienStructure(AvHMessageID inMessageID) -{ - bool theSuccess = false; - - string theErrorMessage; - int theBuildingPointCost = 0; - bool thePurchaseAllowed = this->GetPurchaseAllowed(inMessageID, theBuildingPointCost, &theErrorMessage); - - if(thePurchaseAllowed) - { - if(inMessageID == ALIEN_BUILD_HIVE) - { - // See if there is an inactive hive within range - UTIL_MakeVectors(this->pev->v_angle); - - Vector theStart = this->GetGunPosition(); - Vector theEnd = theStart + gpGlobals->v_forward*50; - - // Collide with world to find potential build site - TraceResult theTR; - UTIL_TraceLine(theStart, theEnd, dont_ignore_monsters, this->edict(), &theTR); - - Vector theLocation = theTR.vecEndPos; - - // Do we have enough points? - if(AvHSHUGetIsSiteValidForBuild(ALIEN_BUILD_HIVE, &theLocation, this->entindex())) - { - // Get the hive at this location - CBaseEntity* theBaseEntity = NULL; - AvHHive* theNearestHive = NULL; - - // Find the nearest hive - while((theBaseEntity = UTIL_FindEntityByClassname(theBaseEntity, kesTeamHive)) != NULL) - { - if(theBaseEntity) - { - AvHHive* theCurrentHive = dynamic_cast(theBaseEntity); - if(theCurrentHive) - { - float theCurrentDistance = VectorDistance(theLocation, theCurrentHive->pev->origin); - if(!theNearestHive || (theCurrentDistance < VectorDistance(theLocation, theNearestHive->pev->origin))) - { - theNearestHive = theCurrentHive; - } - } - } - } - - if(theNearestHive) - { - // Make sure another hive isn't already building for this team - bool theAnotherHiveBuilding = false; - - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - if((theEntity->pev->team == this->pev->team) && theEntity->GetIsSpawning()) - { - theAnotherHiveBuilding = true; - break; - } - END_FOR_ALL_ENTITIES(kesTeamHive); - - if(!theAnotherHiveBuilding) - { - // If so, set it as growing - if(theNearestHive->StartSpawningForTeam(this->GetTeam())) - { - AvHSUBuildingJustCreated(inMessageID, theNearestHive, this); - theSuccess = true; - } - else - { - this->SendMessage(kHelpTextHiveBlocked); - } - } - else - { - this->SendMessage(kHelpTextOtherHiveBuilding); - } - } - } - else - { - this->SendMessage(kHelpTextEmptyHiveNotNearby); - } - } - else - { - char* theClassName = NULL; - if(AvHSHUGetBuildTechClassName(inMessageID, theClassName)) - { - // Make sure we haven't exceeded the limit - int theNumBuildings = 0; - FOR_ALL_ENTITIES(theClassName, CBaseEntity*) - if(theEntity->pev->team == this->pev->team) - { - theNumBuildings++; - } - END_FOR_ALL_ENTITIES(theClassName); - - // Now check to make sure the space is big enough to hold the building - UTIL_MakeVectors(this->pev->v_angle); - - const int kAimRange = 48; - Vector theStart = this->GetGunPosition(); - Vector theEnd = theStart + gpGlobals->v_forward*kAimRange; - - // Collide with world to find potential build site - TraceResult theTR; - UTIL_TraceLine(theStart, theEnd, dont_ignore_monsters, this->edict(), &theTR); - - Vector theLocation = theTR.vecEndPos; - - // Check if collision point is valid for building - if(AvHSHUGetIsSiteValidForBuild(inMessageID, &theLocation)) - { - // Make sure there aren't too many buildings in this area already - int theNumBuildingsNearby = UTIL_CountEntitiesInSphere(theLocation, BALANCE_VAR(kBuildingVisibilityRadius), theClassName); - if(theNumBuildingsNearby < BALANCE_VAR(kNumSameAlienStructuresAllowedInRadius) || FStrEq(theClassName, kwsAlienResourceTower))//voogru: allow the building of rt's regardless of how many may be close by (for maps that have a lot of nodes close to each other) - { - // Create the new building - CBaseEntity* theEntity = CBaseEntity::Create(theClassName, theLocation, AvHSUGetRandomBuildingAngles()); - - // Set building's team - theEntity->pev->team = this->pev->team; - - AvHSUBuildingJustCreated(inMessageID, theEntity, this); - - // Set owner (this prevents collisions between the entity and it's owner though) - //theEntity->pev->owner = ENT(this->pev); - - //voogru: I've moved this here because whats the point of playing the sound if the building didnt get placed? (it was after " Vector theLocation = theTR.vecEndPos;") - // Play sound - char* theSoundEffect = kAlienBuildingSound1; - - if(RANDOM_LONG(0, 1) == 1) - theSoundEffect = kAlienBuildingSound2; - - EMIT_SOUND(this->edict(), CHAN_AUTO, theSoundEffect, this->GetAlienAdjustedEventVolume(), ATTN_NORM); - - theSuccess = true; - } - else - { - this->SendMessage(kTooManyStructuresOfThisTypeNearby); - } - } - else - { - // Play hive easter egg sometimes when trying to build a hive where one already exists - if(inMessageID == ALIEN_BUILD_HIVE) - { - //// If we are close to a hive we already own - //AvHHive* theHive = AvHSUGetRandomActiveHive((AvHTeamNumber)this->pev->team); - //if(theHive) - //{ - // float theDistance = VectorDistance(theHive->pev->origin, this->pev->origin); - // if(theDistance <= 300) - // { - // // Randomly play easter egg - // if(RANDOM_LONG(0, 10) == 0) - // { - // EMIT_SOUND(this->edict(), CHAN_AUTO, kMyHiveEasterEgg, 1.0f, ATTN_NORM); - // } - // } - //} - } - } - } - else - { - this->PlayHUDSound(HUD_SOUND_ALIEN_MORE); - } - } - } - else - { - this->SendMessage(theErrorMessage.c_str()); - } - - return theSuccess; -} - -bool AvHPlayer::BuildTech(AvHMessageID inBuildID, const Vector& inPickRay) -{ - bool theSuccess = false; - - //AvHSUSetIsDebugging(true); - - // If valid - if(AvHSHUGetIsBuildTech(inBuildID)) - { - // Make sure this is a valid place to build - Vector theLocation; - if(AvHSHUTraceAndGetIsSiteValidForBuild(inBuildID, this->GetVisualOrigin(), inPickRay, &theLocation)) - { - // Decrement resources - string theErrorMessage; - int theCost = 0; - bool thePurchaseAllowed = this->GetPurchaseAllowed(inBuildID, theCost, &theErrorMessage); - if(thePurchaseAllowed) - { - // Count how many entities on our team we have in area - int theNumFriendlyEntitiesInArea = 0; - CBaseEntity* theEntity = NULL; - while((theEntity = UTIL_FindEntityInSphere(theEntity, theLocation, BALANCE_VAR(kBuildingVisibilityRadius))) != NULL) - { - // Don't count players - if(!theEntity->IsPlayer() && (theEntity->pev->team == this->pev->team)) - { - theNumFriendlyEntitiesInArea++; - } - } - - if(theNumFriendlyEntitiesInArea < BALANCE_VAR(kMaxMarineEntitiesAllowedInRadius)) - { - // Build it! - theSuccess = (AvHSUBuildTechForPlayer(inBuildID, theLocation, this) != NULL); - - // Inform structure about build if possible - if(theSuccess) - { - if(this->mSelected.size() > 0) - { - // Get selected structure and inform - int theFirstEntitySelected = *this->mSelected.begin(); - AvHBaseBuildable* theBaseBuildable = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theFirstEntitySelected))); - if(theBaseBuildable) - { - theBaseBuildable->TechnologyBuilt(inBuildID); - } - } - - this->PayPurchaseCost(theCost); - } - } - else - { - this->SendMessage(kTooManyStructuresInArea); - } - } - else - { - this->PlayHUDSound(HUD_SOUND_MARINE_MORE); - } - } - } - - //AvHSUSetIsDebugging(false); - - // If successful - // Send confirmation if needed - // else - // Send failure so client is updated - - return theSuccess; -} - -bool AvHPlayer::GroupMessage(AvHMessageID inGroupMessage) -{ - bool theSuccess = false; - - if((inGroupMessage >= GROUP_CREATE_1) && (inGroupMessage < (GROUP_CREATE_1 + kNumHotkeyGroups))) - { - int theOffset = (int)(inGroupMessage - GROUP_CREATE_1); - ASSERT(theOffset >= 0); - ASSERT(theOffset < kNumHotkeyGroups); - if(this->mSelected.size() > 0) - { - this->GetTeamPointer()->SetGroup(theOffset, this->mSelected); - AvHUser3 theUser3 = this->GetTeamPointer()->GetGroupType(theOffset); - - AvHHUDSound theHudSound = HUD_SOUND_SELECT; - if(theUser3 == AVH_USER3_MARINE_PLAYER) - { - theHudSound = AvHHUDSound(HUD_SOUND_SQUAD1 + theOffset); - } - - this->PlayHUDSound(theHudSound); - - // If this is a squad, tell all the players in the squad also. This also tells them they are in the squad. - if(theUser3 == AVH_USER3_MARINE_PLAYER) - { - // Loop through them - for(EntityListType::iterator theIter = this->mSelected.begin(); theIter != this->mSelected.end(); theIter++) - { - AvHPlayer* thePlayer = NULL; - AvHSUGetEntityFromIndex(*theIter, thePlayer); - if(thePlayer) - { - thePlayer->PlayHUDSound(theHudSound); - } - } - } - - // Now run through all the hotgroups and remove these entities from them (entities can only be part of one group) - for(int theCurrentIndex = 0; theCurrentIndex < kNumHotkeyGroups; theCurrentIndex++) - { - if(theCurrentIndex != theOffset) - { - //this->mGroups[theCurrentIndex]; - EntityListType theCurrentGroup = this->GetTeamPointer()->GetGroup(theCurrentIndex); - - // Remove all members of mSelected from this group - for(EntityListType::iterator theIterator = theCurrentGroup.begin(); theIterator != theCurrentGroup.end(); /* nothing */) - { - // If the current entity is in selection - EntityListType::iterator theFindIter = std::find(this->mSelected.begin(), this->mSelected.end(), *theIterator); - bool theEntityInSelection = (theFindIter != this->mSelected.end()); - if(theEntityInSelection) - { - // Remove it from this list - theIterator = theCurrentGroup.erase(theIterator); - } - else - { - // Else, increment - theIterator++; - } - } - - // Set the group again - this->GetTeamPointer()->SetGroup(theCurrentIndex, theCurrentGroup); - } - } - } - theSuccess = true; - } - else if((inGroupMessage >= GROUP_SELECT_1) && (inGroupMessage < (GROUP_SELECT_1 + kNumHotkeyGroups))) - { - int theOffset = (int)(inGroupMessage - GROUP_SELECT_1); - ASSERT(theOffset >= 0); - ASSERT(theOffset < kNumHotkeyGroups); - - //const EntityListType& theGroup = this->mGroups[theOffset]; - EntityListType theGroup = this->GetTeamPointer()->GetGroup(theOffset); - if(theGroup.size() > 0) - { - if(this->mSelected != theGroup) - { - this->mSelected = theGroup; - this->mTrackingEntity = 0; - } - else - { - // If we received the same select message twice in a row, go instead to the last place we went to - if(inGroupMessage == this->mLastSelectEvent) - { - // Go to last saved position - VectorCopy(this->mPositionBeforeLastGotoGroup, this->pev->origin); - - // Clear last select so hitting it again will go to group - this->mLastSelectEvent = MESSAGE_NULL; - } - else - { - // Find nearest entity in group and track it - int theNearestGroupEntity = 0; - float theClosestDistance = sqrt(2*kMaxMapDimension*kMaxMapDimension); - for(EntityListType::const_iterator theIter = theGroup.begin(); theIter != theGroup.end(); theIter++) - { - CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter)); - ASSERT(theBaseEntity); - float theCurrentDistance = VectorDistance(theBaseEntity->pev->origin, this->pev->origin); - if(theCurrentDistance < theClosestDistance) - { - theNearestGroupEntity = *theIter; - theClosestDistance = theCurrentDistance; - } - } - - this->mTrackingEntity = theNearestGroupEntity; - - // Move player in vicinity of player so entity is in PVS - if(this->mTrackingEntity > 0) - { - CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mTrackingEntity)); - if(theBaseEntity) - { - // Save last position so we can jump back to it easily - VectorCopy(this->pev->origin, this->mPositionBeforeLastGotoGroup); - this->mLastSelectEvent = inGroupMessage; - - // Goto group - VectorCopy(theBaseEntity->pev->origin, this->pev->origin); - } - } - } - } - } - - theSuccess = true; - } - - return theSuccess; -} - -bool AvHPlayer::GetCenterPositionForGroup(int inGroupNumber, float& outX, float& outY) -{ - bool theSuccess = false; - - if((inGroupNumber >= 1) && (inGroupNumber <= kNumHotkeyGroups)) - { - int theNumFound = 0; - Vector theLocation; - theLocation.x = theLocation.y = theLocation.z = 0.0f; - - //EntityListType& theGroup = this->mGroups[inGroupNumber-1]; - EntityListType theGroup = this->GetTeamPointer()->GetGroup(inGroupNumber-1); - for(EntityListType::iterator theIter = theGroup.begin(); theIter != theGroup.end(); theIter++) - { - Vector theEntityLocation; - if(AvHSHUGetEntityLocation(*theIter, theEntityLocation)) - { - theLocation.x += theEntityLocation.x; - theLocation.y += theEntityLocation.y; - - theNumFound++; - } - } - - if(theNumFound > 0) - { - outX = theLocation.x/theNumFound; - outY = theLocation.y/theNumFound; - - theSuccess = true; - } - } - - return theSuccess; -} - -string AvHPlayer::GetNetworkID() const -{ - return this->mNetworkID; -} - -void AvHPlayer::SetNetworkID(string& inNetworkID) -{ - this->mNetworkID = inNetworkID; -} - -string AvHPlayer::GetNetworkAddress() const -{ - return this->mNetworkAddress; -} - -void AvHPlayer::SetNetworkAddress(string& inNetworkAddress) -{ - this->mNetworkAddress = inNetworkAddress; -} - -void AvHPlayer::ClearRoleAbilities() -{ - // Clear all abilities that don't stick around between changing role, like all alien purchaseable upgrades - //SetUpgradeMask(&this->pev->iuser4, ALIEN_ABILITY_2, false); - this->pev->iuser3 = AVH_USER3_NONE; - this->pev->iuser4 &= ~MASK_ALIEN_EMBRYO; - this->pev->iuser4 &= ~MASK_ALIEN_MOVEMENT; - this->mIsScreaming = false; - this->mAlienSightActive = false; - this->mDesiredRoomType = 0; -} - -void AvHPlayer::ClearUserVariables() -{ - this->pev->iuser1 = 0; - this->pev->iuser2 = 0; - this->pev->iuser3 = 0; - this->pev->iuser4 = 0; - this->pev->fuser1 = 0; - this->pev->fuser2 = 0; - this->pev->fuser3 = 0; - this->pev->fuser4 = 0; -} - -void AvHPlayer::SetScore(int inScore) -{ - this->mScore = inScore; -} - -int AvHPlayer::GetScore() const -{ - return this->mScore; -} - -CBaseEntity* AvHPlayer::CreateAndDrop(const char* inItemName) -{ - UTIL_MakeVectors(pev->v_angle); - - CBaseEntity* theEntity = CBaseEntity::Create(inItemName, pev->origin + gpGlobals->v_forward * 10, pev->angles, edict() ); - - theEntity->pev->angles.x = 0; - theEntity->pev->angles.z = 0; - //theEntity->PackWeapon( pWeapon ); - theEntity->pev->velocity = gpGlobals->v_forward * 300 + gpGlobals->v_forward * 100; - - return theEntity; -} - -void AvHPlayer::DeployCurrent() -{ - if(this->m_pActiveItem /*&& !this->GetIsBeingDigested()*/) - { - CBasePlayerWeapon* theCurrentWeapon = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); - if(theCurrentWeapon && theCurrentWeapon->CanDeploy()) - { - theCurrentWeapon->Deploy(); - } - } - else - { - // No current weapon, so get the best weapon we have. - g_pGameRules->GetNextBestWeapon( this, NULL ); - } -} - - -// Drop the current item, not weapon, if any. -bool AvHPlayer::DropItem(const char* inItemName) -{ - bool theSuccess = false; - - if(!GetGameRules()->GetIsCombatMode()) - { - CBasePlayerItem* theItem = this->m_pActiveItem; - - if(inItemName) - { - bool theIsDone = false; - theItem = NULL; //we're looking for an item by name, don't bias with active item - - // Find item with this name - for(int i = 0; (i < MAX_ITEM_TYPES) && !theIsDone; i++) - { - CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; - while(theCurrentItem && !theIsDone) - { - if(FStrEq(STRING(theCurrentItem->pev->classname), inItemName)) - { - theIsDone = true; - theItem = theCurrentItem; - } - theCurrentItem = theCurrentItem->m_pNext; - } - } - } - - if(theItem) - { - AvHBasePlayerWeapon* theOriginalDroppedWeapon = dynamic_cast(theItem); - if(theOriginalDroppedWeapon && theOriginalDroppedWeapon->GetIsDroppable()) - { - // Dropping current weapon turns off overwatch - this->TurnOffOverwatch(); - - //char theItemName[256]; - //strcpy(theItemName, STRING(theItem->pev->classname)); - //this->DropPlayerItem(theItemName); - - // Get ammo left, if it's a weapon - int theAmmoLeft = -1; - if(theOriginalDroppedWeapon) - { - theAmmoLeft = theOriginalDroppedWeapon->m_iClip; - GetGameRules()->GetNextBestWeapon(this, theItem); - } - - CBaseEntity* theDroppedEntity = this->CreateAndDrop(STRING(theItem->pev->classname)); - if(theAmmoLeft != -1) - { - CBasePlayerWeapon* theNewlyDroppedWeapon = dynamic_cast(theDroppedEntity); - ASSERT(theNewlyDroppedWeapon); - - // Set ammo, make sure client ammo isn't the same so update is forced - theNewlyDroppedWeapon->m_iClip = theAmmoLeft; - theNewlyDroppedWeapon->m_iClientClip = theAmmoLeft - 1; - - // Means "only ammo is that in the clip" - theNewlyDroppedWeapon->m_iDefaultAmmo = 0; - - ItemInfo theItemInfo; - - if(theOriginalDroppedWeapon->GetItemInfo(&theItemInfo) != 0) - { - int iAmmoIndex = GetAmmoIndex ( (char *) theItemInfo.pszAmmo1 ); - - if ( iAmmoIndex != -1 && m_rgAmmo[ iAmmoIndex ] > 0) - this->DropAmmo((char *)theItemInfo.pszAmmo1, m_rgAmmo[ iAmmoIndex ], theItemInfo.iMaxAmmo1, theItemInfo.iId, theNewlyDroppedWeapon->pev->angles); - } - - } - - // Finally remove the original - theOriginalDroppedWeapon->DestroyItem(); - - // Weak potential fix for occasional crash. Saw this when someone hit "lastinv" while holding a knife, - // and m_pLastItem was garbage, not NULL - this->m_pLastItem = NULL; - SetAnimation(PLAYER_PRIME); //Elven - hack to kill TPRA if we throw a weapon. - theSuccess = true; - } - } - } - - return theSuccess; -} - -void AvHPlayer :: DropAmmo(char *pszAmmoType, int iAmmoAmt, int iMax, int iWeaponID, Vector vecAngles) -{ - if(!pszAmmoType || !iAmmoAmt || !iMax || !iWeaponID) - return; - - int iAmmoIndex = GetAmmoIndex ( pszAmmoType ); - - //Not a valid ammo type. - if(iAmmoIndex == -1) - return; - - if(m_rgAmmo[ iAmmoIndex ] > 0) - m_rgAmmo[ iAmmoIndex ] -= iAmmoAmt; - else - return; - - int theSubModel = -1; - - switch(iWeaponID) - { - case AVH_WEAPON_PISTOL: theSubModel = 3; break; - case AVH_WEAPON_MG: theSubModel = 1; break; - case AVH_WEAPON_SONIC: theSubModel = 0; break; - case AVH_WEAPON_HMG: theSubModel = 2; break; - case AVH_WEAPON_GRENADE_GUN: theSubModel = 4; break; - } - - if(theSubModel == -1) - return; - - CBaseEntity *theDroppedEntity = this->CreateAndDrop( kwsAmmoPack ); - AvHAmmoPack *theRealAmmoPack = dynamic_cast(theDroppedEntity); - - strncpy(theRealAmmoPack->m_szAmmoType, pszAmmoType, 31); - theRealAmmoPack->m_iMaxAmmo = iMax; - theRealAmmoPack->m_iAmmoAmt = iAmmoAmt; - theRealAmmoPack->m_iWeaponID = iWeaponID; - theRealAmmoPack->pev->body = theSubModel; - theRealAmmoPack->pev->angles = vecAngles; - - theRealAmmoPack->SetThink( &CBaseEntity::SUB_Remove ); - theRealAmmoPack->pev->nextthink = gpGlobals->time + AvHSUGetWeaponStayTime(); -} - -void AvHPlayer::EffectivePlayerClassChanged() -{ - this->mEffectivePlayerClassChanged = true; -} - -void AvHPlayer::NeedsTeamUpdate() -{ - this->mNeedsTeamUpdate = true; -} - -void AvHPlayer::SendTeamUpdate() -{ - this->mSendTeamUpdate = true; -} - -bool AvHPlayer::ExecuteAlienMorphMessage(AvHMessageID inMessageID, bool inInstantaneous) -{ - bool theMessageExecuted = false; - - string theErrorMessage; - bool theHadEnoughPoints = false; - - switch(inMessageID) - { - case ALIEN_LIFEFORM_ONE: - case ALIEN_LIFEFORM_TWO: - case ALIEN_LIFEFORM_THREE: - case ALIEN_LIFEFORM_FOUR: - case ALIEN_LIFEFORM_FIVE: - - case ALIEN_EVOLUTION_ONE: - case ALIEN_EVOLUTION_TWO: - case ALIEN_EVOLUTION_THREE: - //case ALIEN_EVOLUTION_FOUR: - //case ALIEN_EVOLUTION_FIVE: - //case ALIEN_EVOLUTION_SIX: - case ALIEN_EVOLUTION_SEVEN: - case ALIEN_EVOLUTION_EIGHT: - case ALIEN_EVOLUTION_NINE: - case ALIEN_EVOLUTION_TEN: - case ALIEN_EVOLUTION_ELEVEN: - case ALIEN_EVOLUTION_TWELVE: - case ALIEN_HIVE_TWO_UNLOCK: - case ALIEN_HIVE_THREE_UNLOCK: - - // Now only allow upgrading from level1 - if(this->GetCanGestate(inMessageID, theErrorMessage)) - { - // Stay as current lifeform by default - bool theCheckDucking = true; - int theTargetIuser3 = this->pev->iuser3; - switch(inMessageID) - { - case ALIEN_LIFEFORM_ONE: - theTargetIuser3 = AVH_USER3_ALIEN_PLAYER1; - break; - case ALIEN_LIFEFORM_TWO: - theTargetIuser3 = AVH_USER3_ALIEN_PLAYER2; - break; - case ALIEN_LIFEFORM_THREE: - theTargetIuser3 = AVH_USER3_ALIEN_PLAYER3; - break; - case ALIEN_LIFEFORM_FOUR: - theTargetIuser3 = AVH_USER3_ALIEN_PLAYER4; - break; - case ALIEN_LIFEFORM_FIVE: - theTargetIuser3 = AVH_USER3_ALIEN_PLAYER5; - theCheckDucking = false; - break; - } - - int theTargetHull = AvHMUGetHull(theCheckDucking, theTargetIuser3); - vec3_t theOrigin; - GetNewOrigin((AvHUser3)theTargetIuser3, theCheckDucking, theOrigin); - - // removed by puzl to fix gestating in vents. - //theOrigin.z += 5; - - bool theIsEnoughRoom = AvHSUGetIsEnoughRoomForHull(theOrigin, theTargetHull, this->edict()); - //voogru: try again but higher - if(!theIsEnoughRoom) - { - - theOrigin.z += AvHMUGetOriginOffsetForMessageID(inMessageID); - - theIsEnoughRoom = AvHSUGetIsEnoughRoomForHull(theOrigin, theTargetHull, this->edict()); - } - - if(theIsEnoughRoom || inInstantaneous) - { - if(!theIsEnoughRoom) - { - int a = 0; - } - - this->Evolve(inMessageID, inInstantaneous); - theMessageExecuted = true; - } - else - { - this->SendMessage(kNeedMoreRoomToGestate); - } - } - else - { - this->SendMessage(theErrorMessage.c_str()); - } - break; - } - - return theMessageExecuted; -} - -bool AvHPlayer::ExecuteMessage(AvHMessageID inMessageID, bool inInstantaneous, bool inForce) -{ - bool theMessageExecuted = false; - - AvHTeam* theTeam = this->GetTeamPointer(); - string theErrorMessage; - - // If valid - if((inMessageID != MESSAGE_NULL) && theTeam) - { - bool theIsMarine = theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE; - bool theIsAlien = theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN; - bool theGameStarted = GetGameRules()->GetGameStarted(); - bool theIsInTopDownMode = this->GetIsInTopDownMode(); - bool theIsCombat = GetGameRules()->GetIsCombatMode(); - bool theIsBeingDigested = this->GetIsBeingDigested(); - - // If we can purchase it - string theErrorMessage; - int theCost = 0; - - if(this->GetPurchaseAllowed(inMessageID, theCost, &theErrorMessage) || inForce) - { - // If we're in Combat mode - bool theIsAvailable = true; - - if(theIsCombat) - { - // Try to execute message - theMessageExecuted = this->ExecuteCombatMessage(inMessageID, theIsAvailable, inForce); - if(theMessageExecuted) - { - this->PurchaseCombatUpgrade(inMessageID); - } - } - - // If it's a fall-through alien upgrade, try it next - if(!theMessageExecuted && theIsAlien && !theIsBeingDigested) - { - // Try to morph or build - theMessageExecuted = this->ExecuteAlienMorphMessage(inMessageID, inInstantaneous); - if(!theMessageExecuted && !theIsCombat) - { - switch(inMessageID) - { - case ALIEN_BUILD_RESOURCES: - case ALIEN_BUILD_OFFENSE_CHAMBER: - case ALIEN_BUILD_DEFENSE_CHAMBER: - case ALIEN_BUILD_SENSORY_CHAMBER: - case ALIEN_BUILD_MOVEMENT_CHAMBER: - case ALIEN_BUILD_HIVE: - theMessageExecuted = this->AttemptToBuildAlienStructure(inMessageID); - break; - } - } - } - - // Add upgrade to our list to be preserved on respawn - if(theMessageExecuted && theIsCombat) - this->mCombatNodes.SetResearchDone(inMessageID); - - if(theMessageExecuted) - this->PayPurchaseCost(theCost); - } - - // tankefugl: 0000971 - int theIssuedOrderIcon = -1; - // :tankefugl - - if(theIsMarine - && !theIsInTopDownMode - && !theIsBeingDigested) - { - switch (inMessageID) - { - case WEAPON_RELOAD: - if(theGameStarted) - this->ReloadWeapon(); - break; - case WEAPON_DROP: - if(!this->DropItem()) - this->SendMessageOnce(kWeaponCantBeDropped, TOOLTIP); - break; - - case ADMIN_VOTEDOWNCOMMANDER: - if(theTeam->PlayerVote(this->entindex(), theErrorMessage)) - theMessageExecuted = true; - else - this->SendMessage(theErrorMessage.c_str()); - break; - // Talk to your teammates - case SAYING_1: - case SAYING_2: - case SAYING_3: - case SAYING_4: - case SAYING_5: - case SAYING_6: - case SAYING_7: - case SAYING_8: - case SAYING_9: - case ORDER_REQUEST: - case ORDER_ACK: - if(this->PlaySaying(inMessageID)) - { - AvHAlertType theAlertType = ALERT_NONE; - AvHMessageID theMessageID = MESSAGE_NULL; - - if(this->GetIsMarine()) - { - switch(inMessageID) - { - // tankefugl: 0000971 - // decides whether icon updates should be sent - case SAYING_1: - theIssuedOrderIcon = TEAMMATE_MARINE_ORDER_FOLLOW; - break; - - case SAYING_2: - theIssuedOrderIcon = TEAMMATE_MARINE_ORDER_COVER; - break; - - case SAYING_8: - theIssuedOrderIcon = TEAMMATE_MARINE_ORDER_WELD; - break; - // :tankefugl - - case SAYING_5: - theAlertType = ALERT_SOLDIER_NEEDS_AMMO; - theMessageID = COMMANDER_NEXTAMMO; - break; - - case SAYING_4: - theAlertType = ALERT_SOLDIER_NEEDS_HEALTH; - theMessageID = COMMANDER_NEXTHEALTH; - break; - - case ORDER_REQUEST: - theAlertType = ALERT_ORDER_NEEDED; - theMessageID = COMMANDER_NEXTIDLE; - break; - } - - // Send alert for commander - if(theAlertType != ALERT_NONE) - { - GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, theAlertType, this->entindex(), theMessageID); - } - } - - theMessageExecuted = true; - } - break; - } - } - else if(theIsAlien && !theIsBeingDigested) - { - switch (inMessageID) - { - // Talk to your teammates - case SAYING_1: - case SAYING_2: - case SAYING_3: - case SAYING_4: - case SAYING_5: - case SAYING_6: - case SAYING_7: - case SAYING_8: - case SAYING_9: - if(this->PlaySaying(inMessageID)) - theMessageExecuted = true; - break; - - case ALIEN_ABILITY_LEAP: - this->StartLeap(); - break; - case 100: - // Eat flashlight event. Add special mode for alien view here? - if(!this->mAlienSightActive) - PLAYBACK_EVENT_FULL(FEV_HOSTONLY, this->edict(), gAlienSightOnEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, 0, 0, 0, 0 ); - else - PLAYBACK_EVENT_FULL(FEV_HOSTONLY, this->edict(), gAlienSightOffEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, 0, 0, 0, 0 ); - this->mAlienSightActive = !this->mAlienSightActive; - theMessageExecuted = true; - break; - } - // tankefugl: 0000971 - // decides whether icon updates should be sent - switch (inMessageID) - { - case SAYING_1: - theIssuedOrderIcon = TEAMMATE_ALIEN_ORDER_FOLLOW; - break; - case SAYING_2: - theIssuedOrderIcon = TEAMMATE_ALIEN_ORDER_COVER; - break; - case SAYING_4: - case SAYING_8: - theIssuedOrderIcon = TEAMMATE_ALIEN_ORDER_HEAL; - break; - } - // :tankefugl - } - - // tankefugl: 0000971 and 0000992 - if (theIssuedOrderIcon > -1) - { - int theOrderTarget = 0; - - vec3_t vecDir; - VectorCopy(this->GetAutoaimVector(0.0f), vecDir); - VectorNormalize(vecDir); - - float currentResult = 0.0f; - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*); - float dotResult = 0.0f; - float theDistance = 0.0f; - vec3_t vecDistance; - int theTraced = 0; - vec3_t vecFrom, vecTo; - - if ((theEntity->entindex() != this->entindex()) && (theEntity->GetTeam() == this->GetTeam())) - { - VectorSubtract(theEntity->pev->origin, this->pev->origin, vecDistance); - // theDistance = Length(vecDistance); - - VectorNormalize(vecDistance); - dotResult = DotProduct(vecDistance, vecDir); - if ((dotResult > 0.9f) && (dotResult > currentResult)) - { - TraceResult theTrace; - vecFrom = AvHSHUGetRealLocation(this->pev->origin, this->pev->mins, this->pev->maxs); - vecTo = AvHSHUGetRealLocation(theEntity->pev->origin, theEntity->pev->mins, theEntity->pev->maxs); - UTIL_TraceLine(vecFrom, vecTo, ignore_monsters, this->edict(), &theTrace); - if (theTrace.flFraction == 1.0f) - { - theTraced = 1; - currentResult = dotResult; - theOrderTarget = theEntity->entindex(); - } - } - } - //ALERT(at_console, "-------------------\n"); - //ALERT(at_console, UTIL_VarArgs("vecDir %f %f %f\n", vecDir[0], vecDir[1], vecDir[2])); - //ALERT(at_console, UTIL_VarArgs("vecDistance %f %f %f\n", vecDistance[0], vecDistance[1], vecDistance[2])); - //ALERT(at_console, UTIL_VarArgs("vecFrom %f %f %f\n", vecFrom[0], vecFrom[1], vecFrom[2])); - //ALERT(at_console, UTIL_VarArgs("vecTo %f %f %f\n", vecTo[0], vecTo[1], vecTo[2])); - //ALERT(at_console, UTIL_VarArgs("dotResult %f\n", dotResult)); - //ALERT(at_console, UTIL_VarArgs("currentResult %f\n", currentResult)); - //ALERT(at_console, UTIL_VarArgs("theTraced %d\n", theTraced)); - //ALERT(at_console, UTIL_VarArgs("theOrderTarget %d\n", theOrderTarget)); - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - -// ALERT(at_console, UTIL_VarArgs("theIssuedOrderIcon %d source %d target %d\n", theIssuedOrderIcon, this->entindex(), theOrderTarget)); - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*); - if(theEntity->GetTeam() == this->GetTeam()) - { - NetMsg_IssueOrder(theEntity->pev, theIssuedOrderIcon, this->entindex(), theOrderTarget); - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - - } - - // Common messages here - if(!theMessageExecuted) - { - switch(inMessageID) - { - case WEAPON_NEXT: - this->NextWeapon(); - theMessageExecuted = true; - break; - case ADMIN_READYROOM: - if(this->GetPlayMode() != PLAYMODE_READYROOM) - { - this->SetPlayMode(PLAYMODE_READYROOM); - theMessageExecuted = true; - } - break; - } - } - } - return theMessageExecuted; -} - -bool AvHPlayer::GetIsAlienSightActive() const -{ - return this->mAlienSightActive; -} - -void AvHPlayer::SetDesiredNetName(string inDesiredNetName) -{ - this->mDesiredNetName = inDesiredNetName; -} - -void AvHPlayer::SetDesiredRoomType(int inRoomType, bool inForceUpdate) -{ - this->mDesiredRoomType = inRoomType; - - if(inForceUpdate) - { - this->mClientDesiredRoomType = -1; - } -} - -void AvHPlayer::ResetPlayerPVS() -{ - // Force recompute of PVS - ::ResetPlayerPVS(this->edict(), this->entindex()); -} - -void AvHPlayer::SetPosition(const Vector& inNewPosition) -{ - // Set new position - VectorCopy(inNewPosition, this->pev->origin); - - this->ResetPlayerPVS(); -} - -// Play sounds quieter when the alien has the silence upgrade -float AvHPlayer::GetAlienAdjustedEventVolume() const -{ - return AvHPlayerUpgrade::GetSilenceVolumeLevel((AvHUser3)this->pev->iuser3, this->pev->iuser4); -} - - - -void AvHPlayer::GetAnimationForActivity(int inActivity, char outAnimation[64], bool inGaitSequence) -{ - strcpy(outAnimation, ""); - - bool theCanCrouch = !((this->GetUser3() == AVH_USER3_ALIEN_PLAYER1) || (this->GetUser3() == AVH_USER3_ALIEN_PLAYER2) || (this->GetUser3() == AVH_USER3_ALIEN_PLAYER3)); - bool theIsCrouched = FBitSet(this->pev->flags, FL_DUCKING); - bool theIsAlien = this->GetIsAlien(); - bool theIsGestating = (this->GetUser3() == AVH_USER3_ALIEN_EMBRYO); - bool theIsDeathAnim = false; - bool theIsReloading = false; - int theDebugAnimations = BALANCE_VAR(kDebugAnimations); - - //bool theIsBlinking = this->GetIsBlinking(); - - switch(inActivity) - { - case ACT_RELOAD: - if(!theIsAlien) - { - strcat(outAnimation, this->m_szAnimExtention); - strcat(outAnimation, "_reload"); - - theIsReloading = true; - }; - break; - // updated by Elven for TPRAs - case ACT_RELOAD_START: - if(!theIsAlien) - { - strcat(outAnimation, this->m_szAnimExtention); - strcat(outAnimation, "_reload_start"); - theIsReloading = true; - }; - break; - case ACT_RELOAD_INSERT: - if(!theIsAlien) - { - strcat(outAnimation, this->m_szAnimExtention); - strcat(outAnimation, "_reload_insert"); - theIsReloading = true; - }; - break; - case ACT_RELOAD_END: - if(!theIsAlien) - { - strcat(outAnimation, this->m_szAnimExtention); - strcat(outAnimation, "_reload_end"); - theIsReloading = true; - }; - break; - case ACT_RANGE_PRIME: - if(theCanCrouch && theIsCrouched) - { - strcpy(outAnimation, "crouch_" ); - } - strcat(outAnimation, this->m_szAnimExtention); - strcat(outAnimation, "_prime"); - break; - - case ACT_RANGE_ATTACK1: - if(theCanCrouch && theIsCrouched) - { - strcpy(outAnimation, "crouch_" ); - } - strcat(outAnimation, this->m_szAnimExtention); - if(theIsAlien) - { - strcat(outAnimation, "_alien"); - } - else - { - strcat(outAnimation, "_fire"); - } - break; - - case ACT_HOP: - strcat(outAnimation, "jump"); - break; - - case ACT_WALK: - if(inGaitSequence || !strcmp(this->m_szAnimExtention, "")) - { - strcat(outAnimation, "walk"); - } - else - { - if(theCanCrouch && theIsCrouched) - { - strcpy(outAnimation, "crouch_" ); - } - strcat(outAnimation, this->m_szAnimExtention); - if(theCanCrouch) - { - if(theIsReloading) - { - strcat(outAnimation, "_reload"); - } - else - { - strcat(outAnimation, "_look"); - } - } - } - break; - - case ACT_RUN: - if(inGaitSequence || !strcmp(this->m_szAnimExtention, "")) - { - strcat(outAnimation, "run"); - } - else - { - if(theCanCrouch && theIsCrouched) - { - strcpy(outAnimation, "crouch_" ); - } - strcat(outAnimation, this->m_szAnimExtention); - if(theCanCrouch) - { - //if(theIsReloading) - //{ - // strcat(outAnimation, "_reload"); - //} - //else - //{ - if(theIsReloading) - { - strcpy(outAnimation, this->m_szAnimExtention); - strcat(outAnimation, "_reload"); - } - else - { - strcat(outAnimation, "_look"); - } - //} - } - } - break; - - case ACT_CROUCHIDLE: - if(theCanCrouch) - { - if(inGaitSequence) - { - strcat(outAnimation, "crouch_idle"); - } - } - break; - case ACT_CROUCH: - if(theCanCrouch) - { - if(inGaitSequence) - { - strcat(outAnimation, "crawl"); - } - else - { - } - } - break; - case ACT_IDLE: - // Weird hack/fix for gyrating and ready room spazzing - //if(this->GetPlayMode() != PLAYMODE_READYROOM) - //{ - if(inGaitSequence) - { - if(theIsReloading) - { - strcat(outAnimation, "_reload"); - } - else - { - strcat(outAnimation, "idle1"); - } - } - else - { - strcat(outAnimation, "look_idle"); - } - //} - break; - - case ACT_DIESIMPLE: - case ACT_DIEVIOLENT: - if(this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER3) - { - strcpy(outAnimation, "death1_die"); - } - else - { - if(theIsCrouched) - { - strcpy(outAnimation, "crouch_die"); - } - else - { - switch(RANDOM_LONG(0, 2)) - { - case 0: - strcpy(outAnimation, "death1_die"); - break; - case 1: - strcpy(outAnimation, "death2_die"); - break; - case 2: - strcpy(outAnimation, "death3_die"); - break; - } - } - } - theIsDeathAnim = true; - break; - case ACT_DIE_HEADSHOT: - strcpy(outAnimation, "head_die"); - theIsDeathAnim = true; - break; - case ACT_DIE_GUTSHOT: - strcpy(outAnimation, "gut_die"); - theIsDeathAnim = true; - break; - case ACT_DIEBACKWARD: - // Hack for skulk until artwork can be updated (it has no back_die) - if(this->pev->iuser3 != AVH_USER3_ALIEN_PLAYER1) - { - strcpy(outAnimation, "back_die"); - } - else - { - strcpy(outAnimation, "gut_die"); - } - theIsDeathAnim = true; - break; - case ACT_DIEFORWARD: - strcpy(outAnimation, "forward_die"); - theIsDeathAnim = true; - break; - case ACT_SWIM: - // die - strcpy(outAnimation, "treadwater"); - break; - } - - if(theIsGestating) - { - float theFullTimeToGestate = GetGameRules()->GetBuildTimeForMessageID(this->mEvolution); - bool theAlmostDoneGestation = (gpGlobals->time > (this->mTimeGestationStarted + theFullTimeToGestate*.75f)); - - switch(inActivity) - { - case ACT_SMALL_FLINCH: - case ACT_BIG_FLINCH: - case ACT_FLINCH_HEAD: - case ACT_FLINCH_CHEST: - case ACT_FLINCH_STOMACH: - case ACT_FLINCH_LEFTARM: - case ACT_FLINCH_RIGHTARM: - case ACT_FLINCH_LEFTLEG: - case ACT_FLINCH_RIGHTLEG: - if(RANDOM_LONG(0, 1)) - { - // flinch1 - strcpy(outAnimation, "flinch1"); - } - else - { - // flinch2 - strcpy(outAnimation, "flinch2"); - } - break; - - case ACT_DIESIMPLE: - case ACT_DIEBACKWARD: - case ACT_DIEFORWARD: - case ACT_DIEVIOLENT: - // die - strcpy(outAnimation, "die"); - theIsDeathAnim = true; - break; - - default: - if(theAlmostDoneGestation) - { - // gestation_fast - strcpy(outAnimation, "gestation_fast"); - } - else - { - // gestation - strcpy(outAnimation, "gestation"); - } - break; - } - } - -#ifdef DEBUG - if(theDebugAnimations && theIsDeathAnim) - { - char theMessage[256]; - sprintf(theMessage, "AvHPlayer::GetAnimationForActivity(death) returned %s\n", outAnimation); - ALERT(at_console, theMessage); - } -#endif -} - -bool AvHPlayer::GetCanGestate(AvHMessageID inMessageID, string& outErrorMessage) -{ - bool theCanGestate = false; - - int theNumHives = this->GetTeamPointer()->GetNumActiveHives(); - - if(this->pev->iuser3 != AVH_USER3_ALIEN_EMBRYO) - { - if(this->mDigestee <= 0) - { - bool theIsRangeRequirementMet = true; - - if(theIsRangeRequirementMet) - { - theCanGestate = true; - } - else - { - // TODO: Add error message - } - } - else - { - outErrorMessage = kNotWhileDigesting; - } - } - - return theCanGestate; -} - -bool AvHPlayer::GetCanCommand(string& outErrorMessage) -{ - bool theCanCommand = false; - - // Jack up command station use time to avoid huge kick-everyone-off-server bug (I think this is an overflow issue) - // I think the overflow issue is fixed, but design-wise, this prevents annoying pop-out/pop-back in attacking (Reaver popping) - const int theCommandStationReuseTime = BALANCE_VAR(kCommandStationReuseTime); - - // Have we been banned from the command station? - AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); - if(!theServerPlayerData || (theServerPlayerData->GetTimeVotedDown() == -1)) - { - //if(!this->GetCurrentWeaponCannotHolster()) - if(this->m_pActiveItem) - { - CBasePlayerWeapon* theCurrentWeapon = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); - if(theCurrentWeapon && theCurrentWeapon->CanHolster()) - { - float theLastTime = this->GetLastTimeInCommandStation(); - if((theLastTime == -1) || (gpGlobals->time > (theLastTime + theCommandStationReuseTime))) - { - if(!this->GetIsEnsnared()) - { - theCanCommand = true; - } - } - else - { - outErrorMessage = kCommandStationWaitTime; - } - } - else - { - outErrorMessage = kWeaponPreventingCommandStation; - } - } - } - else - { - outErrorMessage = kBannedFromCommand; - } - - return theCanCommand; -} - -bool AvHPlayer::GetCanReceiveResources() const -{ - bool theCanReceiveResources = true; - - if(this->GetIsAlien()) - { - theCanReceiveResources = false; - - if(this->GetIsRelevant() && this->IsAlive()) - { - theCanReceiveResources = true; - } - } - - return theCanReceiveResources; -} - -int AvHPlayer::GetEffectivePlayerClass() -{ - AvHPlayerClass theEffectivePlayerClass = PLAYERCLASS_NONE; - - if(this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) - { - theEffectivePlayerClass = PLAYERCLASS_COMMANDER; - } - // Players can be both reinforcing and observer, but reinforcing takes precedence - else if(this->GetPlayMode() == PLAYMODE_REINFORCING) - { - theEffectivePlayerClass = PLAYERCLASS_REINFORCING; - } - // Digesting overrides other states - else if(this->GetIsBeingDigested()) - { - theEffectivePlayerClass = PLAYERCLASS_ALIVE_DIGESTING; - } - else if((this->pev->team == 0) && this->IsObserver()) - { - theEffectivePlayerClass = PLAYERCLASS_SPECTATOR; - } - else - { - // Check team type - if(this->GetIsMarine()) - { - if(this->IsAlive()) - { - if(this->GetHasHeavyArmor()) - { - theEffectivePlayerClass = PLAYERCLASS_ALIVE_HEAVY_MARINE; - } - else if (this->GetHasJetpack()) { - theEffectivePlayerClass = PLAYERCLASS_ALIVE_JETPACK_MARINE; - } - else - { - theEffectivePlayerClass = PLAYERCLASS_ALIVE_MARINE; - } - } - else - { - theEffectivePlayerClass = PLAYERCLASS_DEAD_MARINE; - } - } - else if(this->GetIsAlien()) - { - if(this->IsAlive()) - { - switch(this->pev->iuser3) - { - case AVH_USER3_ALIEN_PLAYER1: - theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL1; - break; - case AVH_USER3_ALIEN_PLAYER2: - theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL2; - break; - case AVH_USER3_ALIEN_PLAYER3: - theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL3; - break; - case AVH_USER3_ALIEN_PLAYER4: - theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL4; - break; - case AVH_USER3_ALIEN_PLAYER5: - theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL5; - break; - case AVH_USER3_ALIEN_EMBRYO: - theEffectivePlayerClass = PLAYERCLASS_ALIVE_GESTATING; - break; - } - } - else - { - theEffectivePlayerClass = PLAYERCLASS_DEAD_ALIEN; - } - } - } - - return theEffectivePlayerClass; -} - -AvHServerPlayerData* AvHPlayer::GetServerPlayerData() -{ - AvHServerPlayerData* theServerPlayerData = NULL; - - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - theServerPlayerData = theTeam->GetServerPlayerData(this->edict()); - } - - return theServerPlayerData; -} - -#ifdef USE_OLDAUTH -int AvHPlayer::GetAuthenticationMask() -{ - int theMask = 0; - - // Get WON id - if(this->GetAllowAuth()) - { - // Use cached value if valid - theMask = this->mCachedAuthenticationMask; - - // Look it up if uninitialized or Steam isn't ready yet - if((theMask == -1) || (theMask == PLAYERAUTH_PENDING)) - { - string theAuthID = AvHSUGetPlayerAuthIDString(this->edict()); - theMask = GetGameRules()->GetAuthenticationMask(theAuthID); - - // Save cached value - this->mCachedAuthenticationMask = theMask; - } - } - - bool theAllowAuthCheatMask = GetGameRules()->GetCheatsEnabled(); - - #ifdef AVH_LAN_PLAYTEST_BUILD - theAllowAuthCheatMask = true; - #endif - - if(theAllowAuthCheatMask) - { - theMask |= this->mAuthCheatMask; - } - return theMask; -} - -bool AvHPlayer::GetAllowAuth() const -{ - return (this->mAllowAuth && (avh_uplink.value > 0)); -} - - -void AvHPlayer::SetAllowAuth(bool inAllowAuth) -{ - this->mAllowAuth = inAllowAuth; -} - -void AvHPlayer::SetAuthCheatMask(int inAuthCheatMask) -{ - this->mAuthCheatMask = inAuthCheatMask; -} - -#endif - -bool AvHPlayer::GetCurrentWeaponCannotHolster() const -{ - bool theCannotHolster = false; - - if(m_pActiveItem) - { - theCannotHolster = !this->m_pActiveItem->CanHolster(); - } - - return theCannotHolster; -} - -bool AvHPlayer::GetInReadyRoom() const -{ - return (this->GetPlayMode() == PLAYMODE_READYROOM); -} - -bool AvHPlayer::GetIsAlien(bool inIncludeSpectating) const -{ - bool theIsAlien = false; - AvHTeam* theTeam = this->GetTeamPointer(inIncludeSpectating); - - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN)) - { - theIsAlien = true; - } - - return theIsAlien; -} - -bool AvHPlayer::GetIsMarine(bool inIncludeSpectating) const -{ - bool theIsMarine = false; - AvHTeam* theTeam = this->GetTeamPointer(inIncludeSpectating); - - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) - { - theIsMarine = true; - } - - return theIsMarine; -} - -bool AvHPlayer::GetIsInTopDownMode(bool inIncludeSpectating) const -{ - return this->mInTopDownMode; -} - -AvHPlayer* AvHPlayer::GetCommander(void) -{ - AvHPlayer* theCommander = NULL; - AvHTeam* theMarinesTeam = this->GetTeamPointer(); - if(theMarinesTeam) - { - int theCommanderIndex = theMarinesTeam->GetCommander(); - AvHSUGetEntityFromIndex(theCommanderIndex, theCommander); - } - - return theCommander; -} - -bool AvHPlayer::GetHasBeenSpectator(void) const -{ - return this->mHasBeenSpectator; -} - -AvHPlayMode AvHPlayer::GetPlayMode(bool inIncludeSpectating) const -{ - int theVarToUse = this->pev->playerclass; - - if(inIncludeSpectating && (this->pev->iuser1 == OBS_IN_EYE)) - { - AvHPlayer* theEntity = NULL; - if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) - { - theVarToUse = theEntity->pev->playerclass; - } - } - - return (AvHPlayMode)theVarToUse; -} - -bool AvHPlayer::GetIsValidReinforcementFor(AvHTeamNumber inTeam) const -{ - bool theSuccess = false; - - if(this->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) - { - if(this->pev->team == inTeam) - { - theSuccess = true; - } - } - - return theSuccess; -} - -int AvHPlayer::GetPointValue(void) const -{ - int thePointValue = BALANCE_VAR(kScoringKillPlayer); - - if(this->GetIsAlien()) - { - switch(this->pev->iuser3) - { - case AVH_USER3_ALIEN_PLAYER2: - thePointValue = BALANCE_VAR(kScoringKillPlayerGorge); - break; - case AVH_USER3_ALIEN_PLAYER3: - thePointValue = BALANCE_VAR(kScoringKillPlayerLerk); - break; - case AVH_USER3_ALIEN_PLAYER4: - thePointValue = BALANCE_VAR(kScoringKillPlayerFade); - break; - case AVH_USER3_ALIEN_PLAYER5: - thePointValue = BALANCE_VAR(kScoringKillPlayerOnos); - break; - } - } - else if(this->GetIsMarine()) - { - if(this->GetHasJetpack()) - { - thePointValue = BALANCE_VAR(kScoringKillPlayerJetpack); - } - else if(this->GetHasHeavyArmor()) - { - thePointValue = BALANCE_VAR(kScoringKillPlayerHA); - } - } - - return thePointValue; -} - -vec3_t AvHPlayer::GetVisualOrigin() const -{ - vec3_t theOrigin = this->pev->origin; - - //theOrigin[2] += this->pev->view_ofs[2]; - if(this->mInTopDownMode) - { - theOrigin[2] = GetGameRules()->GetMapExtents().GetMaxViewHeight(); - } - - return theOrigin; -} - - -int AvHPlayer::GetRespawnCost() const -{ - int theRespawnCost = 0; - const AvHGameplay& theGameplay = GetGameRules()->GetGameplay(); - - if(this->GetClassType() == AVH_CLASS_TYPE_MARINE) - { - // This function shouldn't be used now that there are reinforcements...rework a bit? - theRespawnCost = 0; - } - else if(this->GetClassType() == AVH_CLASS_TYPE_ALIEN) - { - theRespawnCost = theGameplay.GetAlienRespawnCost(); - } - return theRespawnCost; -} - -bool AvHPlayer::GetHasItem(const char *szName) -{ - bool theHasItem = false; - - for(int i = 0; (i < MAX_ITEM_TYPES) && !theHasItem; i++) - { - CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; - while(theCurrentItem && !theHasItem) - { - if(FStrEq(STRING(theCurrentItem->pev->classname), szName)) - { - theHasItem = true; - } - theCurrentItem = theCurrentItem->m_pNext; - } - } - - return theHasItem; -} - -void AvHPlayer::GiveNamedItem(const char *szName, bool inSendMessage) -{ - // Check to make sure we don't have this weapon before giving it. Fixes potential problems where - // weapon settings would be reset if a duplicate weapon was given - if(!this->GetHasItem(szName)) - { - // Send visible pickup message after game has started - bool theSendMessage = GetGameRules()->GetGameStarted(); - CBasePlayer::GiveNamedItem(szName, theSendMessage); - } -} - -int AvHPlayer::GetNumberOfItems() -{ - int theNumItems = 0; - - for(int i = 0; i < MAX_ITEM_TYPES; i++) - { - CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; - while(theCurrentItem) - { - theNumItems++; - theCurrentItem = theCurrentItem->m_pNext; - } - } - - return theNumItems; -} - -void AvHPlayer::GiveResources(float inResources) -{ - this->SetResources(this->GetResources() + inResources); -} - -void AvHPlayer::StartLeap() -{ - // Make sure player has leap - if(this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER1) - { - this->mTimeLeapEnd = gpGlobals->time + kLeapDuration; - } -} - -void AvHPlayer::PlayerTouch(CBaseEntity* inOther) -{ - if(this && this->pev && inOther && this->m_pActiveItem && this->m_pActiveItem->pev) - { - // Uncloak when we touch enemy players to prevent blocking - if(inOther->IsPlayer() && inOther->IsAlive() && (inOther->pev->team != this->pev->team)) - { - this->Uncloak(); - } - - // Don't do "touch" damage too quickly - float theTouchDamageInterval = BALANCE_VAR(kTouchDamageInterval); - if((this->mTimeOfLastTouchDamage == -1) || (gpGlobals->time > (this->mTimeOfLastTouchDamage + theTouchDamageInterval))) - { - entvars_t* theAttacker = this->pev; - entvars_t* theInflictor = this->m_pActiveItem->pev; - - float theScalar = 1.0f; - if((this->mTimeLeapEnd != -1) && (gpGlobals->time < this->mTimeLeapEnd)) - { - // Do damage to entity - if(GetGameRules()->CanEntityDoDamageTo(this, inOther, &theScalar)) - { - float theDamage = BALANCE_VAR(kLeapDamage)*theScalar*theTouchDamageInterval; - inOther->TakeDamage(theInflictor, theAttacker, theDamage, NS_DMG_NORMAL); - - this->mTimeOfLastTouchDamage = gpGlobals->time; - } - } - - // Are we charging? - if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT) /*&& !this->GetIsBlinking()*/) - { - if(GetGameRules()->CanEntityDoDamageTo(this, inOther, &theScalar)) - { - float theDamage = BALANCE_VAR(kChargeDamage)*theScalar*theTouchDamageInterval; - - inOther->TakeDamage(theInflictor, theAttacker, theDamage, NS_DMG_NORMAL); - - if(inOther->IsPlayer() && !inOther->IsAlive()) - { - EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, kChargeKillSound, 1.0, ATTN_NORM); - } - - this->mTimeOfLastTouchDamage = gpGlobals->time; - } - } - } - } -} - -AvHTeamNumber AvHPlayer::GetTeam(bool inIncludeSpectating) const -{ - AvHTeamNumber theTeamNumber = (AvHTeamNumber)this->pev->team; - - if(inIncludeSpectating) - { - CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - theTeamNumber = (AvHTeamNumber)theSpectatingEntity->pev->team; - } - } - - return theTeamNumber; -} - -AvHTeam* AvHPlayer::GetTeamPointer(bool inIncludeSpectating) const -{ - AvHTeamNumber theTeamNumber = this->GetTeam(inIncludeSpectating); - - AvHTeam* theTeamPointer = GetGameRules()->GetTeam(theTeamNumber); - - return theTeamPointer; -} - -float AvHPlayer::GetKilledX() const -{ - return this->mKilledX; -} - -float AvHPlayer::GetKilledY() const -{ - return this->mKilledY; -} - -AvHClassType AvHPlayer::GetClassType(void) const -{ - AvHClassType theClassType = AVH_CLASS_TYPE_UNDEFINED; - - AvHTeamNumber theTeamNumber = (AvHTeamNumber)(this->pev->team); - const AvHTeam* theTeam = GetGameRules()->GetTeam(theTeamNumber); - if(theTeam) - { - theClassType = theTeam->GetTeamType(); - } - - ASSERT(theClassType >= AVH_CLASS_TYPE_UNDEFINED); - ASSERT(theClassType <= AVH_CLASS_TYPE_AUTOASSIGN); - - return theClassType; -} - - -bool AvHPlayer::GetPurchaseAllowed(AvHMessageID inUpgrade, int& outCost, string* outErrorMessage) const -{ - bool thePurchaseAllowed = false; - bool theGameStarted = GetGameRules()->GetGameStarted(); - bool theIsMarine = this->GetTeamPointer()->GetTeamType() == AVH_CLASS_TYPE_MARINE; - bool theIsAlien = this->GetIsAlien(); - int theNumHives = this->GetTeamPointer()->GetNumActiveHives(); - bool theIsBuilder = (this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER2); - bool theIsResearchTech = AvHSHUGetIsResearchTech(inUpgrade); - bool theIsCombatMode = GetGameRules()->GetIsCombatMode(); - string theErrorMessage; - - // Check tech tree first - const AvHTechTree* theTechNodes = &this->GetTeamPointer()->GetTechNodes(); - - // If combat, use Combat nodes - if(GetGameRules()->GetIsCombatMode()) - { - theTechNodes = &this->mCombatNodes; - } - - if(theTechNodes->GetIsMessageAvailable(inUpgrade)) - { - if(theIsResearchTech && (this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) && theIsMarine) - { - thePurchaseAllowed = true; - } - else - { - if(theIsCombatMode && (this->GetExperienceLevel() <= (this->mExperienceLevelsSpent + GetGameRules()->GetCostForMessageID(inUpgrade)))) - { - return false; - } - - switch(inUpgrade) - { - case ALIEN_EVOLUTION_ONE: - case ALIEN_EVOLUTION_TWO: - case ALIEN_EVOLUTION_THREE: - //case ALIEN_EVOLUTION_FOUR: - //case ALIEN_EVOLUTION_FIVE: - //case ALIEN_EVOLUTION_SIX: - case ALIEN_EVOLUTION_SEVEN: - case ALIEN_EVOLUTION_EIGHT: - case ALIEN_EVOLUTION_NINE: - case ALIEN_EVOLUTION_TEN: - case ALIEN_EVOLUTION_ELEVEN: - case ALIEN_EVOLUTION_TWELVE: - // If we are an alien and we don't have the upgrade - if(theIsAlien) - { - AvHUpgradeMask theUpgradeMask = MASK_NONE; - if(AvHGetAlienUpgradeMask(inUpgrade, theUpgradeMask)) - { - if(!GetHasUpgrade(this->pev->iuser4, theUpgradeMask)) - { - AvHAlienUpgradeCategory theCategory = ALIEN_UPGRADE_CATEGORY_INVALID; - if(AvHGetAlienUpgradeCategory(inUpgrade, theCategory)) - { - // Now make sure we have an unspent upgrade available - AvHTeam* theTeamPointer = this->GetTeamPointer(); - if(theTeamPointer && (AvHGetHasFreeUpgradeCategory(theCategory, theTeamPointer->GetAlienUpgrades(), this->pev->iuser4) || GetGameRules()->GetIsCombatMode())) - { - thePurchaseAllowed = true; - } - else - { - theErrorMessage = kUpgradeNotAvailable; - } - } - } - } - } - break; - - case ALIEN_LIFEFORM_ONE: - if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER1)) - { - thePurchaseAllowed = true; - } - break; - case ALIEN_LIFEFORM_TWO: - if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER2)) - { - thePurchaseAllowed = true; - } - break; - case ALIEN_LIFEFORM_THREE: - if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER3)) - { - // if(theNumHives >= 1) - // { - thePurchaseAllowed = true; - // } - // else - // { - // outErrorMessage = kNeedOneHiveToGestate; - // } - } - break; - case ALIEN_LIFEFORM_FOUR: - if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER4)) - { - // if(theNumHives >= 2) - // { - thePurchaseAllowed = true; - // } - // else - // { - // outErrorMessage = kNeedTwoHivesToGestate; - // } - } - break; - case ALIEN_LIFEFORM_FIVE: - if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER5)) - { - // if(theNumHives >= 3) - // { - thePurchaseAllowed = true; - // } - // else - // { - // outErrorMessage = kNeedThreeHivesToGestate; - // } - } - break; - - case ALIEN_BUILD_RESOURCES: - case ALIEN_BUILD_HIVE: - if(theIsBuilder) - { - thePurchaseAllowed = true; - } - else - { - theErrorMessage = kMustBeBuilder; - } - break; - - case ALIEN_BUILD_OFFENSE_CHAMBER: - if(theIsBuilder) - { - if(theNumHives >= 1) - { - thePurchaseAllowed = true; - } - else - { - theErrorMessage = kNeedOneHiveForStructure; - } - } - else - { - theErrorMessage = kMustBeBuilder; - } - break; - - // Make sure we have a hive that can provide this tech - case ALIEN_BUILD_DEFENSE_CHAMBER: - case ALIEN_BUILD_MOVEMENT_CHAMBER: - case ALIEN_BUILD_SENSORY_CHAMBER: - if(theIsBuilder) - { - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - if(theEntity && theEntity->GetIsActive() && (theEntity->pev->team == this->pev->team)) - { - AvHMessageID theTechnology = theEntity->GetTechnology(); - if((theTechnology == inUpgrade) || (theTechnology == MESSAGE_NULL)) - { - thePurchaseAllowed = true; - break; - } - } - END_FOR_ALL_ENTITIES(kesTeamHive); - - if(!thePurchaseAllowed) - { - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - // int theNumHives = theTeam->GetNumActiveHives(); - // switch(theNumHives) - // { - // case 0: - // outErrorMessage = kNeedOneHiveForStructure; - // break; - // case 1: - // outErrorMessage = kNeedTwoHivesForStructure; - // break; - // case 2: - // outErrorMessage = kNeedThreeHivesForStructure; - // break; - // } - - theErrorMessage = kNeedsAnotherHiveForStructure; - } - } - } - else - { - theErrorMessage = kMustBeBuilder; - } - break; - - default: - thePurchaseAllowed = true; - break; - } - } - } - - if(thePurchaseAllowed) - { - // This is either resources, energy, or levels - int theCost = GetGameRules()->GetCostForMessageID(inUpgrade); - - if(GetGameRules()->GetIsCombatMode()) - { - int theLevelsFree = max(this->GetExperienceLevel() - this->GetExperienceLevelsSpent() - 1, 0); - if(theCost > theLevelsFree) - { - thePurchaseAllowed = false; - } - } - else - { - if(AvHSHUGetDoesTechCostEnergy(inUpgrade) || GetGameRules()->GetIsCheatEnabled(kcLowCost)) - { - theCost = 0; - } - - if(this->GetResources() < theCost) - { - thePurchaseAllowed = false; - } - } - - outCost = theCost; - } - - if(outErrorMessage) - { - *outErrorMessage = theErrorMessage; - } - - return thePurchaseAllowed; -} - -int AvHPlayer::GiveAmmo( int iAmount, char *szName, int iMax ) -{ - int theReturnValue = -1; - int theRealAmount = iAmount; - string theRealName = szName; - char theRealNameArray[256]; - int theRealMax = iMax; - - // TODO: Change this to a more generic call on AvHBasePlayerWeapon for next client update - AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem); - if(theBaseWeapon != NULL && theBaseWeapon->GetCanBeResupplied()) - { - if(FStrEq(szName, kwsGenericAmmo)) - { - AvHBasePlayerWeapon* theWeaponToGiveTo = theBaseWeapon; - if(theBaseWeapon->UsesAmmo()) - { - // Translate iAmount, szName and iMax into the ammo for our current weapon - ItemInfo theItemInfo; - if(this->m_pActiveItem->GetItemInfo(&theItemInfo) != 0) - { - //theRealAmount = theBaseWeapon->m_iDefaultAmmo; - theRealAmount = theItemInfo.iMaxClip; - theRealName = theItemInfo.pszAmmo1; - theRealMax = theItemInfo.iMaxAmmo1; - } - } - } - } - strcpy(theRealNameArray, theRealName.c_str()); - theReturnValue = CBasePlayer::GiveAmmo(theRealAmount, theRealNameArray, theRealMax); - - return theReturnValue; -} - - -bool AvHPlayer::GetShouldResupplyAmmo() -{ - bool theResupply = false; - - if(this->GetIsMarine() && this->GetIsRelevant()) - { - AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem); - if(theBaseWeapon != NULL && (theBaseWeapon->m_iId != AVH_WEAPON_MINE)) - { - AvHBasePlayerWeapon* theWeaponToGiveTo = theBaseWeapon; - if(theBaseWeapon->UsesAmmo()) - { - // Translate iAmount, szName and iMax into the ammo for our current weapon - ItemInfo theItemInfo; - if(this->m_pActiveItem->GetItemInfo(&theItemInfo) != 0) - { - int theMaxAmmo = theItemInfo.iMaxAmmo1; - if(theMaxAmmo > 0) - { - int i = GetAmmoIndex(theItemInfo.pszAmmo1); - - if ((i > 0) && (i < MAX_AMMO_SLOTS)) - { - - int theCurrentAmmo = this->m_rgAmmo[i]; - - if (theCurrentAmmo < theWeaponToGiveTo->GetClipSize()) { - theResupply = true; - } - } - } - } - } - } - } - return theResupply; -} - -float AvHPlayer::GetCurrentWeaponAmmoPercentage() -{ - float thePercentage = 1.0f; - - if(this->GetIsMarine() && this->GetIsRelevant()) - { - AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem); - if(theBaseWeapon != NULL && (theBaseWeapon->m_iId != AVH_WEAPON_MINE)) - { - AvHBasePlayerWeapon* theWeaponToGiveTo = theBaseWeapon; - if(theBaseWeapon->UsesAmmo()) - { - // Translate iAmount, szName and iMax into the ammo for our current weapon - ItemInfo theItemInfo; - if(this->m_pActiveItem->GetItemInfo(&theItemInfo) != 0) - { - int theMaxAmmo = theItemInfo.iMaxAmmo1; - if(theMaxAmmo > 0) - { - int i = GetAmmoIndex(theItemInfo.pszAmmo1); - - if ((i > 0) && (i < MAX_AMMO_SLOTS)) - { - int theCurrentAmmo = this->m_rgAmmo[i];// + theWeaponToGiveTo->GetShotsInClip(); - thePercentage = (float)theCurrentAmmo/theMaxAmmo; - } - } - } - } - } - } - - return thePercentage; -} - -bool AvHPlayer::GetEnemySighted(void) const -{ - return this->mEnemySighted; -} - -bool AvHPlayer::GetIsFiring(void) const -{ - bool theIsFiring = false; - if(this->pev->button & IN_ATTACK) - { - AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); - if(theWeapon && theWeapon->m_iClip > 0) - { - theIsFiring = true; - } - } - return theIsFiring; -} - -bool AvHPlayer::GetIsInOverwatch(void) const -{ - return this->mInOverwatch; -} - -bool AvHPlayer::GetIsSpectatingTeam(AvHTeamNumber inTeamNumber) const -{ - bool theIsSpectatingTeam = false; - - CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - if((AvHTeamNumber)theSpectatingEntity->pev->team == inTeamNumber) - { - theIsSpectatingTeam = true; - } - } - - return theIsSpectatingTeam; -} - -bool AvHPlayer::GetIsSpectatingPlayer(int inPlayerNumber) const -{ - bool theIsSpectatingPlayer = false; - - CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - if(theSpectatingEntity->entindex() == inPlayerNumber) - { - theIsSpectatingPlayer = true; - } - } - - return theIsSpectatingPlayer; -} - -bool AvHPlayer::GetIsSpeaking(void) const -{ - return this->mIsSpeaking; -} - -AvHMessageID AvHPlayer::GetLastSaying() const -{ - return this->mLastSaying; -} - -bool AvHPlayer::GetOrdersRequested(void) const -{ - return this->mOrdersRequested; -} - -bool AvHPlayer::GetOrderAcknowledged(void) const -{ - return this->mOrderAcknowledged; -} - -string AvHPlayer::GetPlayerName() const -{ - string thePlayerName = (this->pev->netname && STRING(this->pev->netname)[0] != 0 ) ? STRING(this->pev->netname) : "unconnected" ; - return thePlayerName; -} - -int AvHPlayer::GetRelevantWeight(void) const -{ - float theRelevantWeight = 0; - - // Add current weapon if any - //CBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_pActiveItem); - //if(theActiveWeapon) - //{ - // theRelevantWeight += this->GetRelevantWeightForWeapon(theActiveWeapon); - //} - - // Run through items and tally up the ones that we count - for(int i = 0; i< MAX_ITEM_TYPES; i++) - { - AvHBasePlayerWeapon* theCurrentWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); - while(theCurrentWeapon) - { - int theWeight = this->GetRelevantWeightForWeapon(theCurrentWeapon); - - // Active items count full, count less when stowed - float theMultiplier = (theCurrentWeapon == this->m_pActiveItem) ? 1.0f : .7f; - theRelevantWeight += theWeight*theMultiplier; - theCurrentWeapon = dynamic_cast(theCurrentWeapon->m_pNext); - } - } - - return (int)(theRelevantWeight); -} - -int AvHPlayer::GetRelevantWeightForWeapon(AvHBasePlayerWeapon* inWeapon) const -{ - ASSERT(inWeapon != NULL); - - AvHWeaponID theWeaponID = (AvHWeaponID)inWeapon->m_iId; - int theNumRounds = 0; - if(inWeapon->UsesAmmo()) - { - int theAmmoIndex = inWeapon->PrimaryAmmoIndex(); - ASSERT(theAmmoIndex >= 0); - ASSERT(theAmmoIndex < MAX_AMMO_SLOTS); - theNumRounds = max(inWeapon->m_iClip + this->m_rgAmmo[theAmmoIndex], 0); - } - - return GetGameRules()->GetWeightForItemAndAmmo(theWeaponID, theNumRounds); -} - -AvHUser3 AvHPlayer::GetPreviousUser3(bool inIncludeSpectating) const -{ - - AvHUser3 theUser3 = this->mPreviousUser3; - - if(inIncludeSpectating) - { - const CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - const AvHPlayer* theSpectatingPlayer = dynamic_cast(theSpectatingEntity); - if(theSpectatingPlayer) - { - theUser3 = theSpectatingPlayer->GetPreviousUser3(); - } - } - } - - return theUser3; - -} - -AvHUser3 AvHPlayer::GetUser3(bool inIncludeSpectating) const -{ - AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; - - if(inIncludeSpectating) - { - const CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - const AvHPlayer* theSpectatingPlayer = dynamic_cast(theSpectatingEntity); - if(theSpectatingPlayer) - { - theUser3 = theSpectatingPlayer->GetUser3(); - } - } - } - - return theUser3; -} - -AvHMessageID AvHPlayer::GetEvolution(bool inIncludeSpectating) const -{ - - AvHMessageID theEvolution = mEvolution; - - if(inIncludeSpectating) - { - const CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - const AvHPlayer* theSpectatingPlayer = dynamic_cast(theSpectatingEntity); - if(theSpectatingPlayer) - { - theEvolution = theSpectatingPlayer->GetEvolution(); - } - } - } - - return theEvolution; - -} - -bool AvHPlayer::GetSpecialPASOrigin(Vector& outOrigin) -{ - bool theUseSpecial = (this->GetUser3() == AVH_USER3_COMMANDER_PLAYER); - - if(theUseSpecial) - { - VectorCopy(this->mSpecialPASOrigin, outOrigin); - } - - return theUseSpecial; -} - -// Don't check validity. ASSERT if an error is encountered. -void AvHPlayer::GiveUpgrade(AvHMessageID inUpgrade) -{ -} - -void AvHPlayer::GiveTeamUpgrade(AvHMessageID inUpgrade, bool inGive) -{ - AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; - int thePlayerLevel = this->GetExperienceLevel(); - float theHealthPercentage = this->pev->health/AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, thePlayerLevel); - float theArmorPercentage = this->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); - - // If we're in combat mode, handle upgrades differently - - ProcessGenericUpgrade(this->pev->iuser4, inUpgrade, inGive); - - //AvHTeam* theTeam = this->GetTeamPointer(); - //if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) - //{ - AvHUpgradeMask theMask = ProcessGenericUpgrade(this->pev->iuser4, inUpgrade, inGive); - - if(inUpgrade == BUILD_HEAVY) - { - this->EffectivePlayerClassChanged(); - } - //} - - this->pev->health = theHealthPercentage*AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, thePlayerLevel); - this->pev->armorvalue = theArmorPercentage*AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); -} - -float AvHPlayer::GetResources(bool inIncludeSpectating) const -{ - float theResources = this->mResources; - - if(inIncludeSpectating && (this->pev->iuser1 == OBS_IN_EYE)) - { - AvHPlayer* theEntity = NULL; - if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) - { - theResources = theEntity->GetResources(false); - } - } - - const AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - if(theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) - { - theResources = theTeam->GetTeamResources(); - } - } - - return theResources; -} - -void AvHPlayer::GetSpeeds(int& outBaseSpeed, int& outUnemcumberedSpeed) const -{ - // Use marine speed when in ready room (ie, AVH_CLASS_TYPE_UNDEFINED) - //int theBaseSpeed = 160; - // Counter-strike speeds are around 260 for knife running, and 215 with the para (according to Adrian's memory) - int theBaseSpeed = BALANCE_VAR(kBasePlayerSpeed); - int theUnencumberedSpeed = BALANCE_VAR(kUnencumberedPlayerSpeed); - - if(this->IsObserver()) - { - theBaseSpeed = theUnencumberedSpeed = 1000; - } - else if(this->GetClassType() == AVH_CLASS_TYPE_MARINE) - { - if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) - { - if(this->mInTopDownMode) - { - // For scrolling - theBaseSpeed = theUnencumberedSpeed = BALANCE_VAR(kCommanderPlayerSpeed); - } - } - else if(this->GetHasHeavyArmor()) - { - float theHeavyMultiplier = BALANCE_VAR(kHeavySpeedMultiplier); - theBaseSpeed *= theHeavyMultiplier; - theUnencumberedSpeed *= theHeavyMultiplier; - } - else if(this->GetHasJetpack()) - { - float theJetpackMultiplier = BALANCE_VAR(kJetpackSpeedMultiplier); - theBaseSpeed *= theJetpackMultiplier; - theUnencumberedSpeed *= theJetpackMultiplier; - } - - if(GetHasUpgrade(this->pev->iuser4, MASK_BUFFED)) - { - const float kStimpackSpeedMultiplier = 1 + BALANCE_VAR(kCatalystSpeedIncrease); - theBaseSpeed *= kStimpackSpeedMultiplier; - theUnencumberedSpeed *= kStimpackSpeedMultiplier; - } - } - else if(this->GetClassType() == AVH_CLASS_TYPE_ALIEN) - { - int theSpeedUpgradeLevel = 0; - if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_4)) - { - theSpeedUpgradeLevel = 1; - - if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_12)) - { - theSpeedUpgradeLevel = 2; - } - else if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_13)) - { - theSpeedUpgradeLevel = 3; - } - } - - // When gestating - float theAlienBaseSpeed = 0; - int theSpeedUpgradeAmount = BALANCE_VAR(kAlienCelerityBonus); - const float kChargingFactor = 2.0f; - - switch(this->pev->iuser3) - { - // Take into account speed upgrade, if this player has it - case AVH_USER3_ALIEN_PLAYER1: - theAlienBaseSpeed = BALANCE_VAR(kSkulkBaseSpeed); - break; - - case AVH_USER3_ALIEN_PLAYER2: - theAlienBaseSpeed = BALANCE_VAR(kGorgeBaseSpeed); - break; - - case AVH_USER3_ALIEN_PLAYER3: - theAlienBaseSpeed = BALANCE_VAR(kLerkBaseSpeed); - - // Compensate for airpseed multiplier, so lerk gets proper speed increase in main mode of locomotion - //theSpeedUpgradeAmount /= BALANCE_VAR(kAirspeedMultiplier); - break; - - case AVH_USER3_ALIEN_PLAYER4: - theAlienBaseSpeed = BALANCE_VAR(kFadeBaseSpeed); - break; - - case AVH_USER3_ALIEN_PLAYER5: - //theAlienBaseSpeed = this->mMaxGallopSpeed; - theAlienBaseSpeed = BALANCE_VAR(kOnosBaseSpeed); - - if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT)) - { - theAlienBaseSpeed *= kChargingFactor; - theSpeedUpgradeAmount *= kChargingFactor; - } - break; - } - - theUnencumberedSpeed = theBaseSpeed = theAlienBaseSpeed + (theSpeedUpgradeLevel*theSpeedUpgradeAmount); - - // Update kMaxPlayerSpeed to make sure footsteps are played correctly - ASSERT(theUnencumberedSpeed <= kMaxGroundPlayerSpeed); - } - - outBaseSpeed = theBaseSpeed; - outUnemcumberedSpeed = theUnencumberedSpeed; - - // Take into account ensnare - if(this->GetIsEnsnared()) - { - float thePercentageComplete = (gpGlobals->time - this->mLastTimeEnsnared)/(this->mTimeToBeUnensnared - this->mLastTimeEnsnared); - thePercentageComplete = max(min(thePercentageComplete, 1.0f), 0.0f); - //ASSERT(thePercentageComplete >= 0.0f); - //ASSERT(thePercentageComplete <= 1.0f); - - float theSpeedFactor = min(BALANCE_VAR(kEnsnareMinimumSpeedFactor) + (1.0f - BALANCE_VAR(kEnsnareMinimumSpeedFactor))*thePercentageComplete, BALANCE_VAR(kEnsnareMaximumSpeedFactor)); - ASSERT(theSpeedFactor >= BALANCE_VAR(kEnsnareMinimumSpeedFactor)); - ASSERT(theSpeedFactor <= 1.0f); - - outBaseSpeed *= theSpeedFactor; - outUnemcumberedSpeed *= theSpeedFactor; - } - - // Reduce speed a bit when player has carapace - if(this->GetIsAlien()) - { - int theCarapaceLevel = AvHPlayerUpgrade::GetArmorUpgrade((AvHUser3)this->pev->iuser3, this->pev->iuser4); - float theSpeedFactor = (1.0f - BALANCE_VAR(kCarapaceSlowFactor)*theCarapaceLevel); - - outBaseSpeed *= theSpeedFactor; - outUnemcumberedSpeed *= theSpeedFactor; - } - - if(this->GetIsMetabolizing()) - { - float theMetabolizeSpeedFactor = BALANCE_VAR(kMetabolizeSpeedFactor); - outBaseSpeed *= theMetabolizeSpeedFactor; - outUnemcumberedSpeed *= theMetabolizeSpeedFactor; - } - - if(this->GetIsDigesting()) - { - float theDigestingSpeedFactor = BALANCE_VAR(kDigestingSpeedFactor); - outBaseSpeed *= theDigestingSpeedFactor; - outUnemcumberedSpeed *= theDigestingSpeedFactor; - } - - if(this->GetIsStunned()) - { - // MUHAHAHA! - outBaseSpeed = outUnemcumberedSpeed = 0.0f; - } - - // If we're a fade using blink, increase our speed massively -// if(this->GetIsBlinking()) -// { -// outBaseSpeed *= 10.0f; -// outUnemcumberedSpeed = outBaseSpeed; -// } - -// if(GetGameRules()->GetIsCombatMode()) -// { -// int theSpeedIncrease = (this->GetExperienceLevel() - 1)*BALANCE_VAR(kCombatLevelSpeedIncrease); -// -// outBaseSpeed += theSpeedIncrease; -// outUnemcumberedSpeed += theSpeedIncrease; -// } - - if(GetGameRules()->GetIsCheatEnabled(kcHighSpeed)) - { - outBaseSpeed = outUnemcumberedSpeed = kMaxGroundPlayerSpeed*.7f; - } -} - -int AvHPlayer::GetMaxWalkSpeed() const -{ - return this->mMaxWalkSpeed; -} - -void AvHPlayer::PickSkin() -{ - int theSkin = 0; - - if(this->GetIsMarine()) - { - theSkin = RANDOM_LONG(0, 1); - } - - this->SetSkin(theSkin); -} - -void AvHPlayer::SetSkin(int inSkin) -{ - if(this->pev) - { - this->pev->skin = inSkin; - } -} - -void AvHPlayer::GiveOrderToSelection(AvHOrder& inOrder) -{ - // Set the player list as the selected players - EntityListType theReceivers = this->mSelected; - - // Set the order for the team - AvHTeam* theTeam = this->GetTeamPointer(); - ASSERT(theTeam); - - int theOrderID=inOrder.GetOrderID(); - if(GetGameRules()->GetIsCheatEnabled(kcOrderSelf)) - { - AvHOrder theOrder=inOrder; - int theSelfIndex = this->entindex(); - theOrder.SetReceiver(theSelfIndex); - theOrder.SetOrderID(); - theTeam->SetOrder(theOrder); - - } - if ( this->mSelected.size() > 0 ) { - for(EntityListType::iterator theIter = theReceivers.begin(); theIter != theReceivers.end(); theIter++) - { - if ( inOrder.GetTargetIndex() != *theIter ) - { - AvHOrder theOrder=inOrder; - theOrder.SetReceiver(*theIter); - theOrder.SetOrderID(); - theTeam->SetOrder(theOrder); - } - } - } - - - // set this to true to indicate they don't need help - this->mHasGivenOrder = true; -} - -bool AvHPlayer::GiveOrderToSelection(AvHOrderType inOrder, Vector inNormRay) -{ - bool theSuccess = false; - AvHOrder theNewOrder; - - Vector theOrigin = this->GetVisualOrigin(); - -// #ifdef DEBUG -// vec3_t theStartPoint; -// VectorMA(theOrigin, kSelectionStartRange, inNormRay, theStartPoint); -// -// vec3_t theEndPoint; -// VectorMA(theOrigin, kSelectionEndRange, inNormRay, theEndPoint); -// -// vec3_t theValidOrigin; -// AvHSHUServerGetFirstNonSolidPoint(theStartPoint, theEndPoint, theValidOrigin); -// -// theValidOrigin.z -= BALANCE_VAR(kBiteDamage); -// -// CBaseEntity* pEnt = CBaseEntity::Create(kwsDebugEntity, theValidOrigin, Vector(0, 0, 0)); -// ASSERT(pEnt); -// pEnt->pev->movetype = MOVETYPE_FLY; -// pEnt->pev->solid = SOLID_NOT; -// #endif - - if(AvHCreateSpecificOrder((AvHTeamNumber)(this->pev->team), theOrigin, inOrder, inNormRay, theNewOrder)) - { - this->GiveOrderToSelection(theNewOrder); - - theSuccess = true; - } - - return theSuccess; -} - -void AvHPlayer::HolsterCurrent() -{ - if(this->m_pActiveItem) - { - - CBasePlayerWeapon* theCurrentWeapon = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); - - if(theCurrentWeapon && theCurrentWeapon->CanHolster()) - { - theCurrentWeapon->Holster(); - } - - } -} - -void AvHPlayer::Kick() -{ - char theCommandBuffer[256]; - sprintf(theCommandBuffer, "kick \"%s\"\n", STRING(this->pev->netname)); - SERVER_COMMAND(theCommandBuffer); -} - -void AvHPlayer::ImpulseCommands() -{ - AvHMessageID theMessage = (AvHMessageID)pev->impulse; - - this->PlayerUse(); - - bool theHandledMessage = false; - - if(theMessage != MESSAGE_NULL) - { - // These things can only happen during play, not in ready room, reinforcement or observer modes - if(this->GetPlayMode() == PLAYMODE_PLAYING || (GetGameRules()->GetCheatsEnabled())) - { - if(this->ExecuteMessage(theMessage)) - { - theHandledMessage = true; - } - } - - if(!theHandledMessage) - { - CBasePlayer::ImpulseCommands(); - } - - // Very important I found out - this->pev->impulse = 0; - } -} - -void AvHPlayer::ItemPostFrame(void) -{ - // Check if player tried to do something while we were in the ready room. If so, display tutorial message. - if(this->GetPlayMode() == PLAYMODE_READYROOM) - { - if((this->pev->button & IN_ATTACK) || (this->pev->button & IN_ATTACK2) || (this->pev->button & IN_RELOAD)) - { - this->SendMessageOnce(kReadyRoomMessage, TOOLTIP); - } - } - - if(!this->GetIsBeingDigested()) - { - CBasePlayer::ItemPostFrame(); - } - - this->UpdateAmbientSounds(); -} - -void AvHPlayer::Init() -{ - int i; - - // Copy the server variables from the game rules. - - AvHGamerules* theGameRules = GetGameRules(); - - mServerVariableList.clear(); - mLastUpdateTime = -1; - for (i = 0; i < theGameRules->GetNumServerVariables(); ++i) - { - mServerVariableList.push_back(ServerVariable()); - mServerVariableList.back().mName = theGameRules->GetServerVariable(i); - } - - // Reset to default team - strcpy(this->m_szTeamName, kUndefinedTeam); - - this->mResources = 0; - this->mScore = 0; - this->mSavedCombatFrags = 0; - this->mLastModelIndex = -1; -#ifdef USE_OLDAUTH - this->mCachedAuthenticationMask = -1; - -#endif - - this->mFirstUpdate = true; - this->mNewMap = true; - - this->mHasSeenTeamA = false; - this->mHasSeenTeamB = false; - - this->mPendingCommand = NULL; - this->mIsSpeaking = false; - this->mOrdersRequested = false; - this->mOrderAcknowledged = false; - this->mEnemySighted = false; - this->mTriggerUncloak = false; - this->mTimeOfLastSaying = 0; - this->mLastSaying = MESSAGE_NULL; - this->mTimeOfLastEnemySighting = 0; - this->mClientInTopDownMode = false; - this->mInTopDownMode = false; - this->mClientCommander = -1; - - vec3_t theOrigin( 0, 0, 0 ); - VectorCopy(theOrigin, this->mPositionBeforeTopDown); - VectorCopy(theOrigin, this->mAnglesBeforeTopDown); - VectorCopy(theOrigin, this->mViewAnglesBeforeTopDown); - VectorCopy(theOrigin, this->mViewOfsBeforeTopDown); - VectorCopy(theOrigin, this->mLastGallopViewDirection); - this->mAnimExtensionBeforeTopDown = ""; - this->mTimeStartedTopDown = -1; - - if(this->pev) - { - this->pev->team = TEAM_IND; - this->ClearUserVariables(); - - this->pev->rendermode = kRenderNormal; - this->pev->renderfx = kRenderFxNone; - this->pev->renderamt = 0; - this->pev->skin = 0; - this->pev->solid = SOLID_NOT; - this->pev->frags = 0; - this->m_iDeaths = 0; - this->pev->playerclass = PLAYMODE_UNDEFINED; - } - - this->mClientInOverwatch = false; - this->mInOverwatch = false; - this->mOverwatchEnabled = true; - this->mOverwatchTarget = -1; - this->mTimeOfLastOverwatchPreventingAction = -1; - this->mTimeLastSeenOverwatchTarget = 0; - this->mOverwatchFiredThisThink = false; - - // tankefugl: 0000953 - this->mTimeLastJoinTeam = -1; - // tankefugl - - // alien upgrades - this->mTimeOfLastRegeneration = -1; - this->mTimeOfLastPheromone = -1; - this->mMaxGallopSpeed = 0; - - this->mClientResearchingTech = MESSAGE_NULL; - - this->mAttackOneDown = false; - this->mAttackTwoDown = false; - this->mPlacingBuilding = false; - - this->mPreviousUser3 = AVH_USER3_NONE; - this->mSavedJetpackEnergy = 0; - - this->mHasBeenSpectator = false; - this->mHasLeftReadyRoom = false; - this->mQueuedThinkMessage = ""; - - // Clear out tech nodes - this->mClientTechNodes.Clear(); - this->mClientTechSlotList.clear(); - - // Clear out map locations - this->mClientInfoLocations.clear(); - - // Clear out hive info - this->mClientHiveInfo.clear(); - - this->mClientGamma = kDefaultMapGamma; - - // Clear sent message list - // Don't clear this message, don't keep sending tutorial messages - //this->mSentMessageList.clear(); - - //this->mClientBlipList.clear(); - //this->mBlipList.clear(); - - this->mAlienSightActive = false; - this->mNumHives = 0; - - this->mSpecialPASOrigin.x = this->mSpecialPASOrigin.y = this->mSpecialPASOrigin.z = 0.0f; - this->mClientSpecialPASOrigin.x = this->mClientSpecialPASOrigin.y = this->mClientSpecialPASOrigin.z = 0.0f; - this->mTimeOfLastPASUpdate = -1; - - // puzl: 984 - // record the last time the player attempted to go to the readyroom - this->mTimeOfLastF4 = -1.0f; - - this->mTimeOfLastTeleport = -1; - this->mTimeOfLastHelpText = -1; - this->mTimeOfLastUse = -1; - this->mTimeLeapEnd = -1; - this->mTimeOfLastRedeem = -1; - - memset(&this->mClientDebugCSPInfo, 0, sizeof(weapon_data_t)); - memset(&this->mDebugCSPInfo, 0, sizeof(weapon_data_t)); - this->mClientNextAttack = 0; - - this->mMaxWalkSpeed = CBasePlayer::GetMaxWalkSpeed(); - this->mTimeToBeUnensnared = -1; - this->mLastTimeEnsnared = -1; - this->mLastTimeStartedPlaying = -1; - - this->mTimeToBeFreeToMove = -1; - this->mTimeToEndCatalyst = -1; - - this->mLastTimeInCommandStation = -1; - this->mLastTimeRedemptionTriggered = -1; - this->mLastTimeCheckedRedemption = -1; - - this->mJetpackEnergy = 1.0f; - this->mJetpacking = false; - this->mLastPowerArmorThink = -1; - this->mLastInventoryThink = -1; - - this->mTimeLastPlaying = -1; - this->mTimeGestationStarted = -1; - - this->mEvolution = MESSAGE_NULL; - this->mHealthPercentBefore = -1; - this->mArmorPercentBefore = -1; - - this->mTimeStartedScream = -1; - //this->mNumParticleTemplatesSent = 0; - //this->mTimeOfLastParticleTemplateSending = -1; - - this->mEnemyBlips.Clear(); - this->mFriendlyBlips.Clear(); - - this->mSelected.clear(); - this->mClientTrackingEntity = this->mTrackingEntity = 0; - this->mClientSelectAllGroup.clear(); - - for(i = 0; i < kNumHotkeyGroups; i++) - { - this->mClientGroups[i].clear(); - this->mClientGroupAlerts[i] = ALERT_NONE; - } - - this->mProgressBarEntityIndex = -1; - // Don't set this, must propagate - //this->mClientProgressBarEntityIndex = -1; - this->mProgressBarParam = -1; - this->mTimeProgressBarTriggered = -1; - this->mTimeOfLastFogTrigger = -1; - this->mFogExpireTime = -1; - this->mCurrentFogEntity = -1; - - // Don't set this, we need to propagate lack of fog - //this->mClientCurrentFogEntity = -1; - - //this->mUpgrades.clear(); - this->mClientOrders.clear(); - - this->mMouseWorldPos = this->mAttackOnePressedWorldPos = this->mAttackTwoPressedWorldPos = theOrigin; - - this->mClientEntityHierarchy.Clear(); - - this->mKilledX = this->mKilledY = -1; - - this->mHasGivenOrder = false; - this->mTimeOfLastSignificantCommanderAction = -1; - this->mPreThinkTicks = 0; - - this->mDesiredNetName = ""; -#ifdef USE_OLDAUTH - this->mAuthCheatMask = 0; - this->mAllowAuth = true; -#endif - - this->mTimeOfLastClassAndTeamUpdate = -1; - this->mEffectivePlayerClassChanged = false; - this->mNeedsTeamUpdate = false; - this->mSendTeamUpdate = false; - this->mSendSpawnScreenFade = false; - this->mClientMenuTechSlots = 0; - - memset(&this->mClientRequests, 0, sizeof(int)*kNumRequestTypes); - - this->mDigestee = 0; - this->mTimeOfLastDigestDamage = 0; - this->mTimeOfLastCombatThink = 0; - this->mDesiredRoomType = this->mClientDesiredRoomType = 0; - - this->mTimeOfLastConstructUseAnimation = 0; - this->mTimeOfLastConstructUse = -1; - this->mTimeOfLastResupply = 0; - - this->mTimeOfMetabolizeEnd = -1; - - this->m_DefaultSpectatingMode = OBS_IN_EYE; - this->m_DefaultSpectatingTarget = 1; - - this->mLastSelectEvent = MESSAGE_NULL; - VectorCopy(g_vecZero, this->mPositionBeforeLastGotoGroup); - - this->mTimeOfLastSporeDamage = -1; - this->mTimeOfLastTouchDamage = -1; - - this->mLastMessageSent = ""; - - this->mExperience = 0.0; - this->mExperienceLevelsSpent = 0; - this->mClientExperienceLevelsSpent = 0; - this->mClientPercentToNextLevel = 0.0f; - this->mCombatNodes.Clear(); - this->mPurchasedCombatUpgrades.clear(); - this->mGiveCombatUpgrades.clear(); -} - -void AvHPlayer::InitializeFromTeam(float inHealthPercentage, float inArmorPercentage) -{ - // Set base health and armor - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - this->pev->health = this->pev->max_health = max(theMaxHealth*inHealthPercentage,1.0f);//voogru: prevent bug with players evolving down from higher lifeform from getting negative health but still "alive" - - this->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3)*inArmorPercentage; - - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - ASSERT(theTeam); - - // If he's already on the team, this does nothing (his rank keeps over death) - if(theTeam->AddPlayer(this->entindex())) - { - GetGameRules()->RecalculateHandicap(); - - float theStartingPoints = theTeam->GetInitialPlayerPoints(this->edict()); - if(this->GetIsMarine()) - { - theStartingPoints += this->GetResources(); - } - - // Set starting resources - this->SetResources(theStartingPoints, false); - - // If we're in combat mode, we use our own tech nodes instead of our team's - if(GetGameRules()->GetIsCombatMode()) - { - AvHTechTree theInitialTechNodes = theTeam->GetTechNodes(); - - // Removed restoration of previous levels and experience due to abuse - //// Restore saved/spent nodes if we've already been playing - //AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); - //if(theServerPlayerData) - //{ - // AvHTechNodes theSavedTechNodes = theServerPlayerData->GetCombatNodes(); - // if(theSavedTechNodes.GetNumNodes() > 0) - // { - // theInitialTechNodes = theSavedTechNodes; - // } - // - // int a = this->mExperienceLevelsSpent; - // - // this->SetExperienceLevelsSpent(theServerPlayerData->GetExperienceLevelsSpent()); - // - // MessageIDListType thePurchasedCombatUpgrades = theServerPlayerData->GetPurchasedCombatUpgrades(); - // for(MessageIDListType::iterator theIter = thePurchasedCombatUpgrades.begin(); theIter != thePurchasedCombatUpgrades.end(); theIter++) - // { - // this->PurchaseCombatUpgrade(*theIter); - // } - //} - - // Save it - this->SetCombatNodes(theInitialTechNodes); - - // Set initial experience (restore old experience if player disconnected) - this->mExperience = theTeam->GetInitialExperience(this->edict()); - } - - // Random multiracial marines - this->PickSkin(); - } - } -} - -bool AvHPlayer::GetIsRelevant(bool inIncludeSpectating) const -{ - bool theIsRelevant = false; - - // Use ourself unless we're spectating - const AvHPlayer* thePlayer = this; - - if(inIncludeSpectating) - { - const CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - const AvHPlayer* theSpectatingPlayer = dynamic_cast(theSpectatingEntity); - if(theSpectatingPlayer) - { - thePlayer = theSpectatingPlayer; - } - } - } - - if(thePlayer->IsAlive() && (thePlayer->GetPlayMode() == PLAYMODE_PLAYING) && !thePlayer->GetIsSpectator() && (thePlayer->pev->team != TEAM_IND)) - { - theIsRelevant = true; - } - - return theIsRelevant; -} - -bool AvHPlayer::GetCanBeAffectedByEnemies() const -{ - bool theCanBeAffected = this->GetIsRelevant(false) && !this->GetIsTemporarilyInvulnerable() && !this->GetIsBeingDigested() && !this->GetIsInTopDownMode(); - return theCanBeAffected; -} - -float AvHPlayer::GetOpacity() const -{ - float theOpacity = AvHCloakable::GetOpacity(); - - //if(this->GetIsBlinking()) - //{ - // theOpacity = .05f; - //} - - return theOpacity; -} - -bool AvHPlayer::GetIsMetabolizing() const -{ - bool theIsMetabolizing = false; - - if((gpGlobals->time < this->mTimeOfMetabolizeEnd) && (this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER4)) - { - theIsMetabolizing = true; - } - - return theIsMetabolizing; -} - -void AvHPlayer::SetTimeOfMetabolizeEnd(float inTime) -{ - this->mTimeOfMetabolizeEnd = inTime; -} - -bool AvHPlayer::GetIsSelected(int inEntityIndex) const -{ - bool theIsSelected = false; - - for(EntityListType::const_iterator theIter = this->mSelected.begin(); theIter != this->mSelected.end(); theIter++) - { - if(*theIter == inEntityIndex) - { - theIsSelected = true; - break; - } - } - - return theIsSelected; -} - -bool AvHPlayer::RemoveSelection(int inEntityIndex) -{ - bool theSuccess = false; - - EntityListType::iterator theFoundIter = std::find(this->mSelected.begin(), this->mSelected.end(), (EntityInfo)inEntityIndex); - if(theFoundIter != this->mSelected.end()) - { - this->mSelected.erase(theFoundIter); - theSuccess = true; - } - - return theSuccess; -} - -void AvHPlayer::SetSelection(int inEntityIndex, bool inClearPreviousSelection) -{ - if(inClearPreviousSelection) - { - this->mSelected.clear(); - } - - this->mSelected.push_back(inEntityIndex); -} - - -bool AvHPlayer::GetIsSpectator() const -{ - return (this->pev->flags & FL_PROXY); -} - -void AvHPlayer::SetIsSpectator() -{ - this->pev->flags |= FL_PROXY; -} - -float AvHPlayer::GetLastTimeInCommandStation() const -{ - return this->mLastTimeInCommandStation; -} - -// Clears the player's impulse and sends "invalid action" notification if not a legal action -void AvHPlayer::ValidateClientMoveEvents() -{ - // If player tries to execute an alien ability that they don't have, stop them dead. - AvHMessageID theMessageID = (AvHMessageID)this->mCurrentCommand.impulse; - - if(theMessageID != MESSAGE_NULL) - { - // Assume it's invalid - bool theIsValid = false; - this->mCurrentCommand.impulse = MESSAGE_NULL; - this->pev->impulse = MESSAGE_NULL; - const float kMinSayingInterval = 3.0f; - - // Process universally allowable impulses - switch(theMessageID) - { - case COMM_CHAT_PUBLIC: - case COMM_CHAT_TEAM: - case COMM_CHAT_NEARBY: - case IMPULSE_FLASHLIGHT: - case IMPULSE_SPRAYPAINT: - case IMPULSE_DEMORECORD: - case ADMIN_READYROOM: - theIsValid = true; - break; - - case SAYING_1: - case SAYING_2: - case SAYING_3: - case SAYING_4: - case SAYING_5: - case SAYING_6: - case SAYING_7: - case SAYING_8: - case SAYING_9: -// tankefugl: 0000008 -// preventing spamming of request and ack - case ORDER_REQUEST: - case ORDER_ACK: -// :tankefugl - if(GetGameRules()->GetCheatsEnabled() || (gpGlobals->time > (this->mTimeOfLastSaying + kMinSayingInterval))) - { - theIsValid = true; - } - else - { - int a = 0; - } - break; - - default: - if(GetGameRules()->GetIsCombatMode()) - { - theIsValid = true; - } - break; - } - - // If not universally allowable - AvHTeam* theTeam = this->GetTeamPointer(); - if(!theIsValid && theTeam) - { - AvHUser3 theUser3 = this->GetUser3(); - - // Marines - if(this->GetIsMarine()) - { - // Soldiers - if((theUser3 == AVH_USER3_MARINE_PLAYER) && this->IsAlive()) - { - switch(theMessageID) - { - // Validate orders - // tankefugl: 0000008 - // preventing spamming of request and ack - //case ORDER_REQUEST: - //case ORDER_ACK: - // :tankefugl - - // Validate weapon switches - case WEAPON_NEXT: - case WEAPON_RELOAD: - case WEAPON_DROP: - - // Only soldiers can vote - case ADMIN_VOTEDOWNCOMMANDER: - theIsValid = true; - } - } - // Commanders - else if(theUser3 == AVH_USER3_COMMANDER_PLAYER) - { - switch(theMessageID) - { - // Validate commander movement and input - case COMMANDER_MOUSECOORD: - case COMMANDER_MOVETO: - case COMMANDER_SCROLL: - case COMMANDER_DEFAULTORDER: - case COMMANDER_SELECTALL: - case COMMANDER_REMOVESELECTION: - - // Validate hotgroup creation and selection - case GROUP_CREATE_1: - case GROUP_CREATE_2: - case GROUP_CREATE_3: - case GROUP_CREATE_4: - case GROUP_CREATE_5: - case GROUP_SELECT_1: - case GROUP_SELECT_2: - case GROUP_SELECT_3: - case GROUP_SELECT_4: - case GROUP_SELECT_5: - - // Special impulses to handle requests - case COMMANDER_NEXTIDLE: - case COMMANDER_NEXTAMMO: - case COMMANDER_NEXTHEALTH: - theIsValid = true; - } - - // Validate research and building - // Make sure it comes from the commander and also that the right building is selected and not busy - if(AvHSHUGetIsBuildTech(theMessageID) || AvHSHUGetIsResearchTech(theMessageID) || AvHSHUGetDoesTechCostEnergy(theMessageID) || (theMessageID == BUILD_RECYCLE)) - { - if(theTeam->GetTechNodes().GetIsMessageAvailableForSelection(theMessageID, this->mSelected)) - { - // Make sure only one structure is selected, and make sure the structure that's selected allows this action - if(AvHSHUGetIsBuildTech(theMessageID)) //doesn't depend on selection - { - theIsValid = true; - } - else - { - if(this->mSelected.size() == 1) - { - int theEntityIndex = *this->mSelected.begin(); - CBaseEntity* theEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theEntityIndex)); - AvHBaseBuildable* theBaseBuildable = dynamic_cast(theEntity); - if(theBaseBuildable && theBaseBuildable->GetIsTechnologyAvailable(theMessageID)) - { - theIsValid = true; - } - } - } - } - } - } - } - // Aliens - else if(this->GetIsAlien() && this->IsAlive()) - { - //AvHAlienAbilityWeapon* theAlienAbilityWeapon = dynamic_cast(this->m_pActiveItem); - - switch(theMessageID) - { - // Validate all alien abilities - case ALIEN_ABILITY_LEAP: - case ALIEN_ABILITY_CHARGE: - // Check that weapon is active - if(this->GetHasActiveAlienWeaponWithImpulse(theMessageID)) - { - // Check that we have enough energy, and that it's enabled - //if(theAlienAbilityWeapon->IsUseable()) - //{ - // Deduct energy cost - //ItemInfo theItemInfo; - //theAlienAbilityWeapon->GetItemInfo(&theItemInfo); - //float theEnergy = AvHMUGetEnergyCost(theItemInfo.iId); - theIsValid = true; - //} - } - - // Removed by mmcguire. - // This code is no longer needed because the pm_shared code - // now ignores ability impulses that come from the console. - - /* - else - { - // If players are trying to use the abilities when not equipped via the impulse key, stop them dead (or worse?) - //VectorCopy(g_vecZero, this->pev->velocity); - //this->pev->fixangle = TRUE; - this->Killed(this->pev, 1); - - char theString[512]; - sprintf(theString, "Player \"%s\" executed impulse %d illegally and was killed.\n", STRING(this->pev->netname), theMessageID); - ALERT(at_logged, theString); - } - */ - break; - - case ALIEN_ABILITY_BLINK: - theIsValid = true; - break; - - // Validate lifeform changes and evolutions - case ALIEN_EVOLUTION_ONE: - case ALIEN_EVOLUTION_TWO: - case ALIEN_EVOLUTION_THREE: - case ALIEN_EVOLUTION_SEVEN: - case ALIEN_EVOLUTION_EIGHT: - case ALIEN_EVOLUTION_NINE: - case ALIEN_EVOLUTION_TEN: - case ALIEN_EVOLUTION_ELEVEN: - case ALIEN_EVOLUTION_TWELVE: - - case ALIEN_LIFEFORM_ONE: - case ALIEN_LIFEFORM_TWO: - case ALIEN_LIFEFORM_THREE: - case ALIEN_LIFEFORM_FOUR: - case ALIEN_LIFEFORM_FIVE: - - case ALIEN_BUILD_RESOURCES: - case ALIEN_BUILD_OFFENSE_CHAMBER: - case ALIEN_BUILD_DEFENSE_CHAMBER: - case ALIEN_BUILD_SENSORY_CHAMBER: - case ALIEN_BUILD_MOVEMENT_CHAMBER: - case ALIEN_BUILD_HIVE: - - // Validate weapon switches - case WEAPON_NEXT: - theIsValid = true; - break; - } - } - } - - if(theIsValid) - { - this->mCurrentCommand.impulse = theMessageID; - this->pev->impulse = theMessageID; - } - } -} - -bool AvHPlayer::GetHasActiveAlienWeaponWithImpulse(AvHMessageID inMessageID) const -{ - bool theHasWeapon = false; - - for(int i = 0; (i < MAX_ITEM_TYPES) && !theHasWeapon; i++) - { - CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; - if(theCurrentItem) - { - AvHAlienAbilityWeapon* theBaseWeapon = dynamic_cast(theCurrentItem); - while(theBaseWeapon && !theHasWeapon) - { - if((theBaseWeapon->GetAbilityImpulse() == inMessageID) && theBaseWeapon->GetEnabledState() && theBaseWeapon->IsUseable()) - { - theHasWeapon = true; - } - theCurrentItem = theCurrentItem->m_pNext; - theBaseWeapon = dynamic_cast(theCurrentItem); - } - } - } - - return theHasWeapon; -} - -bool AvHPlayer::GetHasSeenTeam(AvHTeamNumber inNumber) const -{ - bool theHasBeenOnTeam = false; - - if(inNumber == GetGameRules()->GetTeamA()->GetTeamNumber()) - { - theHasBeenOnTeam = this->mHasSeenTeamA; - } - else if(inNumber == GetGameRules()->GetTeamB()->GetTeamNumber()) - { - theHasBeenOnTeam = this->mHasSeenTeamB; - } - - return theHasBeenOnTeam; -} - -void AvHPlayer::SetHasSeenTeam(AvHTeamNumber inNumber) -{ - if(inNumber == GetGameRules()->GetTeamA()->GetTeamNumber()) - { - this->mHasSeenTeamA = true; - } - else if(inNumber == GetGameRules()->GetTeamB()->GetTeamNumber()) - { - this->mHasSeenTeamB = true; - } -} - -float AvHPlayer::GetTimeOfLastSporeDamage() const -{ - return this->mTimeOfLastSporeDamage; -} - -void AvHPlayer::SetTimeOfLastSporeDamage(float inTime) -{ - this->mTimeOfLastSporeDamage = inTime; -} - -// Assumes that the current input is legal -void AvHPlayer::HandleTopDownInput() -{ - // From CBasePlayer::PreThink(): - bool theAttackOneDown = FBitSet(this->mCurrentCommand.buttons, IN_ATTACK); - bool theAttackTwoDown = FBitSet(this->mCurrentCommand.buttons, IN_ATTACK2); - bool theJumpHit = FBitSet(this->mCurrentCommand.buttons, IN_JUMP); - bool theCrouchDown = FBitSet(this->mCurrentCommand.buttons, IN_DUCK); - - // If we are a commander - if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) - { - this->mLastTimeInCommandStation = gpGlobals->time; - - // Fetch world x and world y from move. - // Note: there is some inaccuracy here for two reasons. One, these values were propagated - // over the network and were optimized for it, and two, the selection assumes that the rays - // emanate from the CURRENT view origin. When clicking on one unit, this is right. When - // dragging a box around units, the view origin could have moved substantially. This inaccuracy - // should really only be noticed when drag selecting huge groups of units. Send view origin as well? - this->mMouseWorldPos.x = this->mCurrentCommand.upmove/kSelectionNetworkConstant; - this->mMouseWorldPos.y = this->mCurrentCommand.sidemove/kSelectionNetworkConstant; - this->mMouseWorldPos.z = this->mCurrentCommand.forwardmove/kSelectionNetworkConstant; - - // Fetch potential world location of mouse click. worldx -> cmd->upmove, worldy -> cmd->sidemove - //if(this->pev->impulse == COMMANDER_MOUSECOORD) - AvHMessageID theMessageID = (AvHMessageID)this->mCurrentCommand.impulse; - - bool theIsBuildTech = AvHSHUGetIsBuildTech(theMessageID); - bool theIsResearchTech = AvHSHUGetIsResearchTech(theMessageID); - bool theIsRecycleMessage = (theMessageID == BUILD_RECYCLE); - - if(AvHSHUGetIsGroupMessage(theMessageID)) - { - this->GroupMessage(theMessageID); - } - else if(theMessageID == COMMANDER_SELECTALL) - { - bool theClearSelection = true; - - // Run through all players on our team - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetTeam() == this->GetTeam()) - { - if(/*theEntity->GetIsRelevant() &&*/ (theEntity != this)) - { - this->SetSelection(theEntity->entindex(), theClearSelection); - theClearSelection = false; - } - } - - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - // Set "selectall" group - //this->mSelectAllGroup = this->mSelected; - this->GetTeamPointer()->SetSelectAllGroup(this->mSelected); - } - else if(theMessageID == COMMANDER_REMOVESELECTION) - { - // TODO: - gSelectionHelper.ClearSelection(); - this->mTrackingEntity = 0; - } - else if((theMessageID == COMMANDER_NEXTIDLE) || (theMessageID == COMMANDER_NEXTAMMO) || (theMessageID == COMMANDER_NEXTHEALTH) || theJumpHit) - { - // Jump to first idle soldier and remove from list - bool theSuccess = false; - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - if(theJumpHit) - { - theMessageID = MESSAGE_NULL; - } - - AvHAlert theAlert; - if(theTeam->GetLastAlert(theAlert, true, theJumpHit, &theMessageID)) - { - int theEntityIndex = theAlert.GetEntityIndex(); - CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theEntityIndex)); - if(theBaseEntity) - { - VectorCopy(theBaseEntity->pev->origin, this->pev->origin); - - switch(theMessageID) - { - case COMMANDER_NEXTIDLE: - case COMMANDER_NEXTAMMO: - case COMMANDER_NEXTHEALTH: - // Set selection to player - this->mSelected.clear(); - this->mSelected.push_back(theEntityIndex); - break; - } - } - } - } - } - else if((theMessageID == COMMANDER_SCROLL) || (theMessageID == COMMANDER_MOVETO)) - { - // Stop tracking - this->mTrackingEntity = 0; - - // Commander didn't make a mistake when going to hotgroup, so clear event (so next group select will go to group, not to last position before) - this->mLastSelectEvent = MESSAGE_NULL; - } - else if((theMessageID == COMMANDER_MOUSECOORD) || theIsBuildTech || theIsResearchTech || theIsRecycleMessage /*|| (theMessageID == COMMANDER_DEFAULTORDER)*/) - { - bool theAttackOnePressed = (theAttackOneDown && !this->mAttackOneDown); - bool theAttackTwoPressed = (theAttackTwoDown && !this->mAttackTwoDown); - bool theAttackOneReleased = (!theAttackOneDown && this->mAttackOneDown); - bool theAttackTwoReleased = !theAttackTwoDown && this->mAttackTwoDown; - - // if left button is now down and it wasn't previously down - if(theAttackOnePressed) - { - // Save world location of press - this->mAttackOnePressedWorldPos = this->mMouseWorldPos; - } - - // if right button just down - if(theAttackTwoPressed) - { - // remember it's world location - this->mAttackTwoPressedWorldPos = this->mMouseWorldPos; - } - - Vector theReleasePosition; - if(theAttackOneReleased || theAttackTwoReleased) - { - // Save world position of release. - theReleasePosition = this->mMouseWorldPos; - - //char theMessage[256]; - //sprintf(theMessage, "LMB released, selecting\n"); - //ClientPrint(this->pev, HUD_PRINTTALK, theMessage); - } - - // Watch for selection events - if(theMessageID == COMMANDER_MOUSECOORD) - { - if(theAttackOneReleased) - { - if(!this->mPlacingBuilding) - { - if(!AvHToggleUseable(this, this->GetVisualOrigin(), this->mAttackOnePressedWorldPos)) - { - // Clear existing selection - //this->mSelected.clear(); - //this->mClientSelected.clear(); - - gSelectionHelper.QueueSelection(this->GetVisualOrigin(), this->mAttackOnePressedWorldPos, theReleasePosition, (AvHTeamNumber)this->pev->team); - } - this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; - } - this->mPlacingBuilding = false; - } - else if(theAttackTwoReleased) - { - // Check to be sure that players are selected - if(this->mSelected.size() > 0) - { - bool theNonPlayerSelected = false; - for(EntityListType::iterator theIter = this->mSelected.begin(); theIter != this->mSelected.end(); theIter++) - { - AvHPlayer* thePlayer = NULL; - AvHSUGetEntityFromIndex(*theIter, thePlayer); - if(!thePlayer) - { - theNonPlayerSelected = true; - break; - } - } - - if(!theNonPlayerSelected || GetGameRules()->GetIsCheatEnabled(kcOrderSelf)) - { - // Check if right-clicked a useable thing - // Trace to see if the commander clicked a useable thing - //if(!AvHToggleUseable(this, this->pev->origin, this->mAttackTwoPressedWorldPos)) - //{ - - if(!this->GiveOrderToSelection(ORDERTYPEL_DEFAULT, this->mAttackTwoPressedWorldPos)) - { - // This location better be off the map or something, default orders should nearly always go through - this->SendMessage(kInvalidOrderGiven, TOOLTIP); - } - else - { - this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; - } - } - } - //} - } - } - // Issue movement order to selection - //else if(theMessageID == COMMANDER_DEFAULTORDER) - //{ - // // Build order - // AvHOrder theOrder; - // theOrder.SetOrderType(ORDERTYPEL_DEFAULT); - // - // float theWorldX = this->mCurrentCommand.upmove*kWorldPosNetworkConstant; - // float theWorldY = this->mCurrentCommand.sidemove*kWorldPosNetworkConstant; - // theOrder.SetLocation(vec3_t(theWorldX, theWorldY, 0.0f)); - // - // this->GiveOrderToSelection(theOrder); - //} - // Watch for build events - else if(theIsBuildTech) - { - this->mPlacingBuilding = true; - - if(GetGameRules()->GetGameStarted()) - { - // 551 puzl - // Hack to stop free scans. This should be reworked as a generic solution for energy based build events. - bool theCanBuild=true; - if(this->mSelected.size() > 0) - { - int theEntityForResearch = *this->mSelected.begin(); - CBaseEntity* theEntity = AvHSUGetEntityFromIndex(theEntityForResearch); - - AvHObservatory* theObs = dynamic_cast(theEntity); - if ( theObs ) - { - if ( (theObs->GetIsTechnologyAvailable(BUILD_SCAN)) == false && (theMessageID == BUILD_SCAN) ) - theCanBuild = false; - } - if(!theObs && theMessageID == BUILD_SCAN) { - theCanBuild = false; - } - } - - if ( theCanBuild ) { - // puzl - this->BuildTech(theMessageID, this->mAttackOnePressedWorldPos); - this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; - -// tankefugl: 0001014 -// // If player(s) selected when something built, give default order to it (assumes that players can't be selected along with other non-players) -// if(AvHSHUGetIsBuilding(theMessageID)) -// { -// if(this->mSelected.size() > 0) -// { -// int theFirstEntitySelected = *this->mSelected.begin(); -// if((theFirstEntitySelected >= 1) && (theFirstEntitySelected <= gpGlobals->maxClients)) -// { -// if(!this->GiveOrderToSelection(ORDERTYPEL_DEFAULT, this->mAttackOnePressedWorldPos)) -// { -// this->SendMessage(kInvalidOrderGiven, true); -// } -// } -// } -// } -// :tankefugl - } - } - } - else if(theIsResearchTech) - { - if(GetGameRules()->GetGameStarted()) - { - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam && (theTeam->GetResearchManager().GetIsMessageAvailable(theMessageID))) - { - if(this->mSelected.size() == 1) - { - int theEntityForResearch = *this->mSelected.begin(); - this->Research(theMessageID, theEntityForResearch); - this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; - } - } - } - } - // Check for recycling action - else if(theIsRecycleMessage) - { - for(EntityListType::iterator theIter = this->mSelected.begin(); theIter != this->mSelected.end(); theIter++) - { - AvHBaseBuildable* theBuildable = NULL; - AvHSUGetEntityFromIndex(*theIter, theBuildable); - if(theBuildable) - { - if((theBuildable->pev->team == this->pev->team) && (theBuildable->pev->team != 0)) - { - theBuildable->StartRecycle(); - this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; - - const char* theBuildableName = STRING(theBuildable->pev->classname); - this->LogPlayerAction("recycle", theBuildableName); - } - } - } - } - - // Update - this->mAttackOneDown = theAttackOneDown; - this->mAttackTwoDown = theAttackTwoDown; - } - } - else - { - // TODO: Make sure we think this player is a commander to prevent cheating on client - // TODO: If they sent COMMANDER_MOUSECOORD and they aren't a commander, there's trouble - } - - // Process selections if waiting - gSelectionHelper.ProcessPendingSelections(); - - if(gSelectionHelper.SelectionResultsWaiting()) - { - EntityListType theNewSelection; - gSelectionHelper.GetAndClearSelection(theNewSelection); - - //string theMessage = "selecting: "; - - // If crouch is down - bool theToggleSelection = theCrouchDown; - if(theToggleSelection) - { - bool theNewSelectionIsAllPlayers = true; - - // Check to make sure the whole new selection is all players - for(EntityListType::iterator theIterator = theNewSelection.begin(); theIterator != theNewSelection.end(); theIterator++) - { - if(*theIterator > gpGlobals->maxClients) - { - theNewSelectionIsAllPlayers = false; - } - } - - // If not, clear the current selection and clear crouch - if(!theNewSelectionIsAllPlayers) - { - theToggleSelection = false; - } - } - - if(!theToggleSelection) - { - this->mSelected.clear(); - } - - // Get player for each one, make sure it's selectable by this player - for(EntityListType::iterator theIterator = theNewSelection.begin(); theIterator != theNewSelection.end(); /* no increment*/) - { - // If this player is selectable - AvHPlayer* thePlayer = NULL; - AvHSUGetEntityFromIndex(*theIterator, thePlayer); - if(!thePlayer || GetGameRules()->GetIsPlayerSelectableByPlayer(thePlayer, this)) - { - // Add to debug message - //char theNumber[16]; - //sprintf(theNumber, "%d ", *theIterator); - //theMessage += string(theNumber); - int theCurrentEntity = *theIterator; - - // Toggle selection of this player - if(theToggleSelection) - { - // Is entity is already selected? - EntityListType::iterator theFindIter = std::find(this->mSelected.begin(), this->mSelected.end(), theCurrentEntity); - bool theEntityIsSelected = (theFindIter != this->mSelected.end()); - if(theEntityIsSelected) - { - this->mSelected.erase(theFindIter); - } - else - { - this->SetSelection(theCurrentEntity, false); - } - } - else - { - this->SetSelection(theCurrentEntity, false); - } - - // Increment - theIterator++; - - this->mTrackingEntity = 0; - } - else - { - // erase it from the list - theIterator = theNewSelection.erase(theIterator); - } - } - - // If we selected at least one unit, display debug message -// if(this->mSelected.size() > 0) -// { -// theMessage += "\n"; -// ClientPrint(this->pev, HUD_PRINTTALK, theMessage.c_str()); -// } - } - - // Temporary for fun -// AvHTeam* theTeam = this->GetTeamPointer(); -// if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN)) -// { -// if(theAttackOneDown && (this->GetUser3() != ROLE_ALIEN2) && (this->GetUser3() != ROLE_ALIEN5)) -// { -// this->PlayRandomRoleSound(kPlayerLevelAttackSoundList, CHAN_WEAPON); -// } -// } -} - -void AvHPlayer::Jump() -{ - Vector vecWallCheckDir;// direction we're tracing a line to find a wall when walljumping - Vector vecAdjustedVelocity; - Vector vecSpot; - TraceResult tr; - - if (FBitSet(pev->flags, FL_WATERJUMP)) - return; - - if (pev->waterlevel >= 2) - { - return; - } - - // If this isn't the first frame pressing the jump button, break out. - if ( !FBitSet( m_afButtonPressed, IN_JUMP ) ) - return; // don't pogo stick - - if ( !(pev->flags & FL_ONGROUND) || !pev->groundentity ) - { - return; - } - -// many features in this function use v_forward, so makevectors now. - UTIL_MakeVectors (pev->angles); - - SetAnimation( PLAYER_JUMP ); - - if ( FBitSet(pev->flags, FL_DUCKING ) || FBitSet(m_afPhysicsFlags, PFLAG_DUCKING) ) - { - //if ( m_fLongJump && (pev->button & IN_DUCK) && gpGlobals->time - m_flDuckTime < 1 && pev->velocity.Length() > 50 ) - if ( (pev->button & IN_DUCK) && gpGlobals->time - m_flDuckTime < 1 && pev->velocity.Length() > 50 ) - {// If jump pressed within a second of duck while moving, long jump! - SetAnimation( PLAYER_SUPERJUMP ); - } - } - - // If you're standing on a conveyor, add it's velocity to yours (for momentum) - entvars_t *pevGround = VARS(pev->groundentity); - if ( pevGround && (pevGround->flags & FL_CONVEYOR) ) - { - pev->velocity = pev->velocity + pev->basevelocity; - } -} - -void AvHPlayer::Killed( entvars_t *pevAttacker, int iGib ) -{ - if(this->GetUser3() != AVH_USER3_COMMANDER_PLAYER) - { - // Save death position - this->mKilledX = this->pev->origin.x; - this->mKilledY = this->pev->origin.y; - - this->mIsScreaming = false; - - this->PackDeadPlayerItems(); - - //this->StopDigestion(false); - this->ResetBehavior(false); - - if(GetGameRules()->GetIsCombatMode()) - { - this->ProcessCombatDeath(); - } - - // Fade out already performed when we start being digested - bool theFadeOut = !this->GetIsBeingDigested(); - - if(!this->GetIsBeingDigested()) - { - if(theFadeOut) - { - Vector theFadeColor; - theFadeColor.x = 0; - theFadeColor.y = 0; - theFadeColor.z = 0; - UTIL_ScreenFade(this, theFadeColor, kTransitionFadeTime, 0.0f, 255, FFADE_OUT | FFADE_STAYOUT); - } - } - else - { - SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, false); - } - - // This line caused a dynamic_cast failure when a shotty killed a flier, then it set it's ensnare state to false, - // which tried to deploy his current weapon, which got the most recent weapon it used, which was garbage for some reason - //this->SetEnsnareState(false); - SetUpgradeMask(&this->pev->iuser4, MASK_ENSNARED, false); - this->mTimeToBeUnensnared = -1; - this->mLastTimeEnsnared = -1; - - this->mAlienSightActive = false; - this->mEvolution = MESSAGE_NULL; - this->SetUsedKilled(false); - - this->PlayRandomRoleSound(kPlayerLevelDieSoundList); - - this->Uncloak(); - - int thePriority = 0; - bool theIsDramatic = false; - switch(this->GetUser3(false)) - { - case AVH_USER3_ALIEN_EMBRYO: - thePriority = kGestateDeathPriority; - break; - case AVH_USER3_ALIEN_PLAYER2: - thePriority = kGorgeDeathPriority; - break; - case AVH_USER3_ALIEN_PLAYER3: - thePriority = kLerkDeathPriority; - break; - case AVH_USER3_ALIEN_PLAYER4: - thePriority = kFadeDeathPriority; - break; - case AVH_USER3_ALIEN_PLAYER5: - thePriority = kOnosDeathPriority; - theIsDramatic = true; - break; - } - - if(this->GetHasHeavyArmor()) - { - thePriority = kHeavyDeathPriority; - } - - if(thePriority > 0) - { - GetGameRules()->MarkDramaticEvent(thePriority, this->entindex(), theIsDramatic, (entvars_t*)pevAttacker); - } - - // Added this to make player models fade out - this->pev->solid = SOLID_NOT; - - // We can only die when we're playing, not in ready room, reinforcement mode or observation - // (see ClientKill) - CBasePlayer::Killed(pevAttacker, iGib); - - //voogru: remove parasite flag if they are a marine. - if(this->GetIsMarine()) - SetUpgradeMask(&this->pev->iuser4, MASK_PARASITED, false); - - this->EffectivePlayerClassChanged(); - - GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_PLAYER_DIED, this->entindex()); - - - -// // Print death anim -// const char* theMainSequence = this->LookupSequence(this->pev->sequence); -// if(!theMainSequence) -// { -// theMainSequence = "None"; -// } -// -// const char* theGaitSequence = this->LookupSequence(this->pev->gaitsequence); -// if(!theGaitSequence) -// { -// theGaitSequence = "None"; -// } -// -// char theMessage[128]; -// sprintf(theMessage, "Death animation: %s, %s", theMainSequence, theGaitSequence); -// UTIL_SayText(theMessage, this); - } -} - -//Activity AvHPlayer::GetDeathActivity (void) -//{ -// Activity theDeathActivity = ACT_DIESIMPLE; -// -// switch(RANDOM_LONG(0, 3)) -// { -// case 1: -// theDeathActivity = ACT_DIEBACKWARD; -// break; -// case 2: -// theDeathActivity = ACT_DIEFORWARD; -// break; -// case 3: -// theDeathActivity = ACT_DIEVIOLENT; -// break; -// } -// -// return theDeathActivity; -//} - - -void AvHPlayer::NextWeapon() -{ - if(this->GetIsAbleToAct()) - { - CBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_pActiveItem); - - if(theActiveWeapon) - { - CBasePlayerWeapon* theNextWeapon = dynamic_cast(theActiveWeapon->m_pNext); - if(theNextWeapon) - { - this->m_pLastItem = this->m_pActiveItem; - this->m_pActiveItem->Holster(); - this->m_pActiveItem = theNextWeapon; - this->m_pActiveItem->Deploy(); - } - else - { - int theSlot = theActiveWeapon->iItemSlot(); - for(int i = 0; i < MAX_ITEM_TYPES; i++) - { - theNextWeapon = dynamic_cast(this->m_rgpPlayerItems[(theSlot + i + 1) % MAX_ITEM_TYPES]); - if(theNextWeapon) - { - this->m_pLastItem = this->m_pActiveItem; - this->m_pActiveItem->Holster(); - this->m_pActiveItem = theNextWeapon; - this->m_pActiveItem->Deploy(); - break; - } - } - } - } - } -} - -void AvHPlayer::ObserverModeIllegal() -{ - // Blackout screen or something if observers aren't allowed - UTIL_ScreenFade( CBaseEntity::Instance(this->edict()), Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE ); - this->SendMessage(kNoSpectating); -} - -void AvHPlayer::PackDeadPlayerItems(void) -{ - //to do - drop everything that's not in the standard loadout + LMG. - this->DropItem(kwsMachineGun); - this->DropItem(kwsShotGun); - this->DropItem(kwsHeavyMachineGun); - this->DropItem(kwsGrenadeGun); - this->DropItem(kwsMine); - this->DropItem(kwsWelder); -} - -void AvHPlayer::PlayRandomRoleSound(string inSoundListName, int inChannel, float inVolume) -{ - char theListName[256]; - AvHUser3 theUser3 = this->GetUser3(); - if(theUser3 != AVH_USER3_NONE) - { - sprintf(theListName, inSoundListName.c_str(), (int)theUser3); - gSoundListManager.PlaySoundInList(theListName, this, inChannel, inVolume); - } -} - -float AvHPlayer::GetTimeOfLastConstructUse() const -{ - return this->mTimeOfLastConstructUse; -} - -void AvHPlayer::SetTimeOfLastConstructUse(float inTime) -{ - this->mTimeOfLastConstructUse = inTime; -} - -void AvHPlayer::PlayerConstructUse() -{ - AvHTeam* theTeam = this->GetTeamPointer(); - ASSERT(theTeam); - if(theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) - { - this->HolsterWeaponToUse(); - } - else if(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - const float kConstructAnimationInterval = 1.1f; - const int kConstructAnimationIndex = 4; - - if((this->mTimeOfLastConstructUseAnimation == 0) || (gpGlobals->time > (this->mTimeOfLastConstructUseAnimation + kConstructAnimationInterval))) - { - // Play special builder animation - PLAYBACK_EVENT_FULL(0, this->edict(), gWeaponAnimationEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 0, kConstructAnimationIndex, 0, 0 ); - - // Delay idle - if(this->m_pActiveItem) - { - CBasePlayerWeapon* theCurrentWeapon = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); - if(theCurrentWeapon) - { - theCurrentWeapon->m_flTimeWeaponIdle += 2.0f; - } - } - - this->mTimeOfLastConstructUseAnimation = gpGlobals->time; - } - } -} - -bool AvHPlayer::PlaySaying(AvHMessageID inMessageID) -{ - bool thePlayedSaying = false; - char theSoundList[256]; - memset(theSoundList, 0, 256*sizeof(char)); - - if(this->pev->iuser3 == AVH_USER3_MARINE_PLAYER) - { - switch(inMessageID) - { - case SAYING_1: - case SAYING_2: - case SAYING_3: - case SAYING_4: - case SAYING_5: - case SAYING_6: - case SAYING_7: - case SAYING_8: - case SAYING_9: - sprintf(theSoundList, kSoldierSayingList, (inMessageID - SAYING_1 + 1)); - break; - - case ORDER_REQUEST: - strcpy(theSoundList, kSoldierOrderRequestList); - break; - - case ORDER_ACK: - strcpy(theSoundList, kSoldierOrderAckList); - break; - } - } - else if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) - { - switch(inMessageID) - { - case SAYING_1: - case SAYING_2: - case SAYING_3: - case SAYING_4: - case SAYING_5: - case SAYING_6: - case SAYING_7: - case SAYING_8: - case SAYING_9: - sprintf(theSoundList, kCommanderSayingList, (inMessageID - SAYING_1 + 1)); - break; - } - } - else if(this->GetIsAlien()) - { - switch(inMessageID) - { - case SAYING_1: - case SAYING_2: - case SAYING_3: - case SAYING_4: - case SAYING_5: - case SAYING_6: - sprintf(theSoundList, kAlienSayingList, (inMessageID - SAYING_1 + 1)); - break; - } - } - - if(strcmp(theSoundList, "")) - { - if(inMessageID == ORDER_REQUEST) - { - this->mOrdersRequested = true; - this->mOrderAcknowledged = false; - this->mIsSpeaking = false; - } - else if(inMessageID == ORDER_ACK) - { - this->mOrderAcknowledged = true; - this->mIsSpeaking = false; - this->mOrdersRequested = false; - } - else - { - this->mIsSpeaking = true; - this->mOrderAcknowledged = false; - this->mOrdersRequested = false; - } - - this->mTimeOfLastSaying = gpGlobals->time; - this->mLastSaying = inMessageID; - - //int pitch = 95;// + RANDOM_LONG(0,29); - //EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, theSaying, 1, ATTN_NORM, 0, pitch); - gSoundListManager.PlaySoundInList(theSoundList, this, CHAN_VOICE, 1.0f); - - thePlayedSaying = true; - } - - return thePlayedSaying; -} - -bool AvHPlayer::PlayHUDSound(AvHHUDSound inSound) const -{ - return this->PlayHUDSound( inSound, pev->origin[0], pev->origin[1] ); -} - -bool AvHPlayer::PlayHUDSound(AvHHUDSound inSound, float x, float y) const -{ - bool theSuccess = false; - - if((inSound > HUD_SOUND_INVALID) && (inSound < HUD_SOUND_MAX)) - { - NetMsg_PlayHUDNotification( this->pev, 0, inSound, x, y ); - theSuccess = true; - } - - return theSuccess; -} - -void AvHPlayer::PlayHUDStructureNotification(AvHMessageID inMessageID, const Vector& inLocation) -{ - if(GetGameRules()->GetGameStarted()) - { - // This player built a structure. Tell all his teammates - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - - if(theEntity->GetIsRelevant() && !theEntity->GetIsBeingDigested()) - { - // Don't send our own messages to ourself unless cheats are enabled - if(GetGameRules()->GetCheatsEnabled() || (this != theEntity)) - { - bool theShowNotification = false; - - // Show to friendlies... - if(theEntity->pev->team == this->pev->team) - { - theShowNotification = true; - } - - if(theShowNotification) - { - NetMsg_PlayHUDNotification( theEntity->pev, 1, inMessageID, inLocation.x, inLocation.y ); - } - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - } -} - -void AvHPlayer::ProcessEvolution() -{ - this->SaveHealthArmorPercentages(); - - UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kGestationSound, 1.0f, .5, SND_STOP, 100); - - // Go back to the role we were before, unless we're morphing into a new lifeform - AvHUser3 theNewUser3 = this->mPreviousUser3; - - bool theChangedLifeforms = false; - switch(this->mEvolution) - { - case ALIEN_LIFEFORM_ONE: - theNewUser3 = AVH_USER3_ALIEN_PLAYER1; - theChangedLifeforms = true; - break; - case ALIEN_LIFEFORM_TWO: - theNewUser3 = AVH_USER3_ALIEN_PLAYER2; - theChangedLifeforms = true; - break; - case ALIEN_LIFEFORM_THREE: - theNewUser3 = AVH_USER3_ALIEN_PLAYER3; - theChangedLifeforms = true; - break; - case ALIEN_LIFEFORM_FOUR: - theNewUser3 = AVH_USER3_ALIEN_PLAYER4; - theChangedLifeforms = true; - break; - case ALIEN_LIFEFORM_FIVE: - theNewUser3 = AVH_USER3_ALIEN_PLAYER5; - theChangedLifeforms = true; - break; - } - - // This shouldn't be needed now that SetUser3 takes into account moving the origin. - // Position player a bit higher off the ground so he doesn't get stuck - //this->pev->origin.z += AvHMUGetOriginOffsetForUser3(theNewUser3); - - if(theChangedLifeforms) - { - if(GetGameRules()->GetIsCombatMode()) - this->SetLifeformCombatNodesAvailable(false); // Only allow one lifeform change - else - { - int iUpgrades = MASK_UPGRADE_1 - | MASK_UPGRADE_2 - | MASK_UPGRADE_3 - | MASK_UPGRADE_4 - | MASK_UPGRADE_5 - | MASK_UPGRADE_6 - | MASK_UPGRADE_7 - | MASK_UPGRADE_8 - | MASK_UPGRADE_9 - | MASK_UPGRADE_10 - | MASK_UPGRADE_11 - | MASK_UPGRADE_12 - | MASK_UPGRADE_13 - | MASK_UPGRADE_14 - | MASK_UPGRADE_15; - - SetUpgradeMask(&this->pev->iuser4, (AvHUpgradeMask)iUpgrades, false); - } - } - - this->SetUser3(theNewUser3, true); - - //this->mPreviousUser3 = User3_UNDEFINED; - - // int theMaxArmor = 0; - // float theArmorPercentage = 0; - // int theNewMaxArmor = 0; - - switch(this->mEvolution) - { - case ALIEN_EVOLUTION_ONE: - case ALIEN_EVOLUTION_TWO: - case ALIEN_EVOLUTION_THREE: - case ALIEN_EVOLUTION_SEVEN: - case ALIEN_EVOLUTION_EIGHT: - case ALIEN_EVOLUTION_NINE: - case ALIEN_EVOLUTION_TEN: - case ALIEN_EVOLUTION_ELEVEN: - case ALIEN_EVOLUTION_TWELVE: - - // If it's the exoskeleton upgrade, upgrade now - // theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, this->mUser3); - // theArmorPercentage = this->pev->armorvalue/theMaxArmor; - - ProcessGenericUpgrade(this->pev->iuser4, this->mEvolution); - - // If player has already decided on the direction to upgrade, spend any extra upgrade levels in that category. - // This has to happen immediately, or else spamming the upgrade button can get another evolution before this is called in InternalAlienUpgradesThink. - AvHTeam* theTeamPointer = this->GetTeamPointer(); - ASSERT(theTeamPointer); - AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); - AvHAddHigherLevelUpgrades(theUpgrades, this->pev->iuser4); - - //this->PlayHUDSound(HUD_SOUND_ALIEN_UPGRADECOMPLETE); - -// if(this->mEvolution == ALIEN_EVOLUTION_TWO) -// { -// theNewMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, this->mUser3); -// this->pev->armorvalue = theArmorPercentage*theNewMaxArmor; -// } - break; - } - - this->PlayRandomRoleSound(kPlayerLevelSpawnSoundList, CHAN_ITEM, this->GetAlienAdjustedEventVolume()); - this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; - - this->RevertHealthArmorPercentages(); - this->mEvolution = MESSAGE_NULL; -} - -void AvHPlayer::RevertHealthArmorPercentages() -{ - // Preserve armor and health percentages - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - this->pev->health = max(this->mHealthPercentBefore*theMaxHealth,1.0f);//voogru: prevent bug with players evolving down from higher lifeform from getting negative health but still "alive" - - int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); - this->pev->armorvalue = max(this->mArmorPercentBefore*theMaxArmor, 0.0f); - - // Assumes a push/pop kind of deal - this->mHealthPercentBefore = this->mArmorPercentBefore = 1.0f; -} - -void AvHPlayer::SaveHealthArmorPercentages() -{ - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - this->mHealthPercentBefore = this->pev->health/(float)theMaxHealth; - this->mHealthPercentBefore = min(max(0.0f, this->mHealthPercentBefore), 1.0f); - - int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); - this->mArmorPercentBefore = this->pev->armorvalue/(float)theMaxArmor; - this->mArmorPercentBefore = min(max(0.0f, this->mArmorPercentBefore), 1.0f); -} - -void AvHPlayer::ProcessResourceAdjustment(AvHMessageID inMessageID) -{ - // TODO: Mark cheater if this isn't the case? - if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) - { - // Make sure we have resource tower selected - if(this->mSelected.size() == 1) - { - int theEntIndex = *this->mSelected.begin(); - AvHFuncResource* theFuncResource; - if(AvHSUGetEntityFromIndex(theEntIndex, theFuncResource)) - { - // Get particle system - int theParticleSystemIndex = theFuncResource->GetParticleSystemIndex(); - AvHParticleSystemEntity* theParticleSystemEntity = NULL; - if(AvHSUGetEntityFromIndex(theParticleSystemIndex, theParticleSystemEntity)) - { - // Adjust particle system for resource tower - - // Get custom data - uint16 theCustomData = theParticleSystemEntity->GetCustomData(); - - // Adjust custom data by inMessageID - ASSERT(false); - //switch(inMessageID) - //{ - //case RESOURCE_ADJUST_ONE_UP: - // theCustomData = min(theCustomData+1, 0xF); - // break; - //case RESOURCE_ADJUST_ONE_DOWN: - // theCustomData = max(theCustomData-1, 0); - // break; - //} - - // Set custom data again - theParticleSystemEntity->SetCustomData(theCustomData); - } - } - } - } -} - -void AvHPlayer::Evolve(AvHMessageID inMessageID, bool inInstantaneous) -{ - // TODO: Put in a waiting time or some other effects? - if(this->GetTeamPointer()->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - this->PlayHUDSound(HUD_SOUND_POINTS_SPENT); - - int theDramaticPriority = 0; - switch(inMessageID) - { - case ALIEN_LIFEFORM_ONE: - theDramaticPriority = kEvolveLevelOnePriority; - break; - case ALIEN_LIFEFORM_TWO: - theDramaticPriority = kEvolveLevelTwoPriority; - break; - case ALIEN_LIFEFORM_THREE: - theDramaticPriority = kEvolveLevelThreePriority; - break; - case ALIEN_LIFEFORM_FOUR: - theDramaticPriority = kEvolveLevelFourPriority; - break; - case ALIEN_LIFEFORM_FIVE: - theDramaticPriority = kEvolveLevelFivePriority; - break; - default: - theDramaticPriority = kEvolveUpgradePriority; - break; - } - - if(!inInstantaneous) - { - GetGameRules()->MarkDramaticEvent(theDramaticPriority, this); - - this->mTimeGestationStarted = gpGlobals->time; - this->mPreviousUser3 = (AvHUser3)this->pev->iuser3; - this->mEvolution = inMessageID; - - this->SetUser3(AVH_USER3_ALIEN_EMBRYO); - this->BecomePod(); - } - else - { - this->mPreviousUser3 = (AvHUser3)this->pev->iuser3; - this->mEvolution = inMessageID; - this->ProcessEvolution(); - } - } -} - -void AvHPlayer::LogEmitRoleChange() -{ - const char* theUser3Name = AvHSHUGetClassNameFromUser3((AvHUser3)this->pev->iuser3); - if(theUser3Name != NULL) - { - UTIL_LogPrintf("%s changed role to \"%s\"\n", - GetLogStringForPlayer( this->edict() ).c_str(), - theUser3Name - ); - } -} - -void AvHPlayer::LogPlayerAction(const char* inActionDescription, const char* inActionData) -{ - if(inActionDescription && inActionData) - { - UTIL_LogPrintf("%s triggered \"%s\" (type \"%s\")\n", - GetLogStringForPlayer( this->edict() ).c_str(), - inActionDescription, - inActionData); - } -} - -void AvHPlayer::LogPlayerActionPlayer(CBasePlayer* inActionPlayer, const char* inAction) -{ - if(inAction) - { - UTIL_LogPrintf("%s triggered \"%s\" against %s\n", - GetLogStringForPlayer( inActionPlayer->edict() ).c_str(), - inAction, - GetLogStringForPlayer( this->edict() ).c_str() - ); - } -} - -void AvHPlayer::LogPlayerAttackedPlayer(CBasePlayer* inAttackingPlayer, const char* inWeaponName, float inDamage) -{ - if(inWeaponName) - { - edict_t* theAttacker = inAttackingPlayer->edict(); - edict_t* theReceiver = this->edict(); - - bool theLogAttack = false; - int theLogDetail = CVAR_GET_FLOAT(kvLogDetail); - - if((theLogDetail > 0) && (theAttacker->v.team != theReceiver->v.team)) - { - theLogAttack = true; - } - else if((theLogDetail > 1) && (theAttacker->v.team == theReceiver->v.team)) - { - theLogAttack = true; - } - else if(theLogDetail > 2) - { - theLogAttack = true; - } - - if(theLogAttack) - { - // Remove "weapon_" prefix - string theKillerName(inWeaponName); - AvHSHUMakeViewFriendlyKillerName(theKillerName); - int theDamage = (int)inDamage; - - UTIL_LogPrintf("%s attacked %s with \"%s\" (damage \"%d\")\n", - GetLogStringForPlayer( theAttacker ).c_str(), - GetLogStringForPlayer( theReceiver ).c_str(), - theKillerName.c_str(), - theDamage - ); - } - } -} - -void AvHPlayer::LogPlayerKilledPlayer(CBasePlayer* inAttackingPlayer, const char* inWeaponName) -{ - if(inWeaponName) - { - edict_t* theAttacker = inAttackingPlayer->edict(); - edict_t* theReceiver = this->edict(); - - bool theLogAttack = false; - int theLogDetail = CVAR_GET_FLOAT(kvLogDetail); - - if((theLogDetail > 0) && (theAttacker->v.team != theReceiver->v.team)) - { - theLogAttack = true; - } - else if((theLogDetail > 1) && (theAttacker->v.team == theReceiver->v.team)) - { - theLogAttack = true; - } - else if(theLogDetail > 2) - { - theLogAttack = true; - } - - if(theLogAttack) - { - // Remove "weapon_" prefix - string theKillerName(inWeaponName); - AvHSHUMakeViewFriendlyKillerName(theKillerName); - - UTIL_LogPrintf("%s killed %s with \"%s\"\n", - GetLogStringForPlayer( theAttacker ).c_str(), - GetLogStringForPlayer( theReceiver ).c_str(), - theKillerName.c_str() - ); - } - } -} - -void AvHPlayer::Research(AvHMessageID inUpgrade, int inEntityIndex) -{ - if(this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) - { - bool theIsResearchable; - int theResearchCost; - float theResearchTime; - AvHTeam* theTeam = this->GetTeamPointer(); - - CBaseEntity* theEntity = AvHSUGetEntityFromIndex(inEntityIndex); - - if(theEntity && theTeam && (theEntity->pev->team == this->pev->team)) - { - AvHResearchManager& theResearchManager = theTeam->GetResearchManager(); - if(inUpgrade == MESSAGE_CANCEL) - { - // Remember research tech and time done - float thePercentageComplete = 0.0f; - AvHMessageID theCancelledTechnology = MESSAGE_NULL; - if(theResearchManager.CancelResearch(inEntityIndex, thePercentageComplete, theCancelledTechnology)) - { - if(theResearchManager.GetResearchInfo(theCancelledTechnology, theIsResearchable, theResearchCost, theResearchTime)) - { - ASSERT(thePercentageComplete >= 0.0f); - ASSERT(thePercentageComplete < 1.0f); - const float kRefundFactor = 1.0f; - float theRefund = kRefundFactor*(1.0f - thePercentageComplete)*theResearchCost; - theRefund = min(theRefund, (float)theResearchCost); - this->SetResources(this->GetResources() + theRefund); - } - - char* theResearchName = NULL; - if(AvHSHUGetResearchTechName(inUpgrade, theResearchName)) - { - this->LogPlayerAction("research_cancel", theResearchName); - } - } - } - else if(theResearchManager.GetResearchInfo(inUpgrade, theIsResearchable, theResearchCost, theResearchTime)) - { - // Look up cost, deduct from player - if(this->GetResources() >= theResearchCost) - { - // Remember research tech and time done - if(theResearchManager.SetResearching(inUpgrade, inEntityIndex)) - { - this->PayPurchaseCost(theResearchCost); - // Tell everyone on this team about research and time research done, so their - // HUD is updated, and so they can't then walk up to station and start researching something else - this->PlayHUDStructureNotification(inUpgrade, theEntity->pev->origin); - - char* theResearchName = NULL; - if(AvHSHUGetResearchTechName(inUpgrade, theResearchName)) - { - this->LogPlayerAction("research_start", theResearchName); - } - } - } - else - { - this->PlayHUDSound(HUD_SOUND_MARINE_MORE); - } - } - } - } -} - -// Digestion functions -int AvHPlayer::GetDigestee() const -{ - return this->mDigestee; -} - -void AvHPlayer::SetDigestee(int inPlayerID) -{ - this->mDigestee = inPlayerID; -} - -void AvHPlayer::StartDigestion(int inDigestee) -{ - AvHPlayer* theDigestee = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inDigestee))); - if(theDigestee) - { - theDigestee->SetBeingDigestedMode(true); - SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, true); - - this->SetDigestee(inDigestee); - - EMIT_SOUND(ENT(this->pev), CHAN_VOICE, kDevourSwallowSound, this->GetAlienAdjustedEventVolume(), ATTN_NORM); - - this->mTimeOfLastDigestDamage = gpGlobals->time; - } -} - -void AvHPlayer::StopDigestion(bool inDigested) -{ - // Play digest complete sound and stop digestion - int theDigesteeIndex = this->GetDigestee(); - this->SetDigestee(0); - - AvHPlayer* theDigestee = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theDigesteeIndex))); - if(theDigestee) - { - if(inDigested) - { - this->SetDesiredRoomType(0, true); - - EMIT_SOUND(ENT(this->pev), CHAN_VOICE, kDevourCompleteSound, this->GetAlienAdjustedEventVolume(), ATTN_NORM); - } - else - { - theDigestee->SetBeingDigestedMode(false); - - EMIT_SOUND(ENT(this->pev), CHAN_VOICE, kDevourCancelSound, this->GetAlienAdjustedEventVolume(), ATTN_NORM); - } - } - - SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, false); - -} - -void AvHPlayer::InternalDigestionThink() -{ - // If we have a digestee - int theDigesteeIndex = this->GetDigestee(); - AvHPlayer* theDigestee = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theDigesteeIndex))); - if(theDigestee) - { - bool thePlayerWasDigested = false; - - // If digestee is alive and still in the game (hasn't disconnected or switched teams) - if(theDigestee->GetIsRelevant()) - { - if(RANDOM_LONG(0, 110) == 0) - { - // Play digesting sound occasionally - EMIT_SOUND(this->edict(), CHAN_AUTO, kDigestingSound, this->GetAlienAdjustedEventVolume(), ATTN_NORM); - } - - // Do damage to digestee - float theTimePassed = gpGlobals->time - this->mTimeOfLastDigestDamage; - if(theTimePassed > 1.0f) - { - // Find devour weapon if we have one, so death message appears properly - entvars_t* theInflictor = this->pev; - CBasePlayerItem* theDevourWeapon = this->HasNamedPlayerItem(kwsDevour); - if(theDevourWeapon) - { - theInflictor = theDevourWeapon->pev; - } - - const float theCombatModeScalar = GetGameRules()->GetIsCombatMode() ? BALANCE_VAR(kCombatModeTimeScalar) : 1.0f; - theDigestee->pev->takedamage = DAMAGE_YES; - float theDamage = theTimePassed*BALANCE_VAR(kDevourDamage)*(1.0f/theCombatModeScalar); - theDigestee->TakeDamage(theInflictor, this->pev, theDamage, DMG_DROWN); - theDigestee->pev->takedamage = DAMAGE_NO; - - // Get health back too - this->Heal(theDamage, false); - - this->mTimeOfLastDigestDamage = gpGlobals->time; - } - - // Set the digestee's position to our own - VectorCopy(this->pev->origin, theDigestee->pev->origin); - VectorCopy(this->pev->angles, theDigestee->pev->angles); - - // Set status bar estimating how long before player will be digested (for both digestee and digester) - theDigestee->TriggerProgressBar(theDigesteeIndex, 3); - this->TriggerProgressBar(theDigesteeIndex, 3); - - // Set fuser3 appropriately - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theDigestee->pev->iuser4, theDigestee->GetUser3(), this->GetExperienceLevel()); - float theDigestingScalar = (((float)theMaxHealth - theDigestee->pev->health)/(float)theMaxHealth); - /*this->pev->fuser3 =*/ theDigestee->pev->fuser3 = theDigestingScalar*kNormalizationNetworkFactor; - - // Set sound effects as player gets more and more digested - int theDesiredRoomType = 26; // strange sounds right before you die - if(theDigestingScalar < .33f) - { - // Water 1 - theDesiredRoomType = 14; - } - else if(theDigestingScalar < .66f) - { - // Water 2 - theDesiredRoomType = 15; - } - else if(theDigestingScalar < .9f) - { - // Water 3 - theDesiredRoomType = 16; - } - theDigestee->SetDesiredRoomType(theDesiredRoomType); - - if(theDigestee->pev->health <= 0) - { - thePlayerWasDigested = true; - } - } - - // If digestee is dead and no longer relevant - if(!theDigestee->IsAlive() || !theDigestee->GetIsRelevant() || (theDigestee->GetTeam() == this->GetTeam())) - { - this->StopDigestion(thePlayerWasDigested); - } - } -} - -bool AvHPlayer::GetDoesCurrentStateStopOverwatch() const -{ - AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); - const AvHTeam* theTeam = this->GetTeamPointer(); - - bool theCurrentStateStopsOverwatch = false; -// bool theCurrentStateStopsOverwatch = ((this->pev->button != 0 && !this->mOverwatchFiredThisThink) || /*(this->pev->velocity.Length() > kOverwatchBreakingVelocity) ||*/ !theWeapon || ((this->pev->iuser3 != AVH_USER3_MARINE_PLAYER) && !GetHasUpgrade(this->pev->iuser4, MASK_MARINE_OVERWATCH))); -// -// // Overwatch not valid for alien players -// if(theTeam && (theTeam->GetTeamType() != AVH_CLASS_TYPE_MARINE)) -// { -// theCurrentStateStopsOverwatch = true; -// } - - return theCurrentStateStopsOverwatch; -} - -bool AvHPlayer::GetIsEntityInSight(CBaseEntity* inEntity) -{ - - bool theSuccess = false; - AvHTeam* theTeam = this->GetTeamPointer(); - // Elven - we don't want marines who are being digested to be able to sight anything. - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) && GetHasUpgrade(this->pev->iuser4, MASK_DIGESTING)) - { - return theSuccess; - } - // Check if the entitiy is in the player's view frustum. - UTIL_MakeVectors ( pev->v_angle ); - - Vector center = inEntity->pev->origin + (inEntity->pev->maxs + inEntity->pev->mins) / 2; - Vector sightLine = center - (pev->origin + pev->view_ofs); - - Vector hSightLine = sightLine - DotProduct(sightLine, gpGlobals->v_up) * gpGlobals->v_up; - Vector vSightLine = sightLine - DotProduct(sightLine, gpGlobals->v_right) * gpGlobals->v_right; - - float hDot = DotProduct(hSightLine.Normalize(), gpGlobals->v_forward); - float vDot = DotProduct(vSightLine.Normalize(), gpGlobals->v_forward); - - float hAngle = acosf(hDot) * 180 / M_PI; - float vAngle = acosf(vDot) * 180 / M_PI; - - if (hAngle > 180) hAngle -= 360; - if (vAngle > 180) vAngle -= 360; - - float aspect = 1.333; // 640/480 - - if (fabs(hAngle) <= pev->fov / 2 && fabs(vAngle) <= pev->fov / (2 * aspect)) - { - if (FVisible(inEntity)) - { - - theSuccess = true; - } - } - -// if(GET_RUN_CODE(4096)) -// { -// if (!theSuccess) -// { -// -// UTIL_MakeVectors ( pev->v_angle ); -// -// const float kMaxDistanceForSighting = 10000; -// -// // Trace a ray in the direction the player is aiming. -// -// Vector theStart = EyePosition(); -// Vector theEnd; -// VectorMA(theStart, kMaxDistanceForSighting, gpGlobals->v_forward, theEnd); -// -// TraceResult tr; -// UTIL_TraceLine(theStart, theEnd, dont_ignore_monsters, ignore_glass, ENT(pev), &tr); -// -// if (tr.flFraction != 1 && tr.pHit == ENT(inEntity->pev)) -// { -// int a = 0; -// theSuccess = true; -// } -// -// } -// } - // TODO: Make this better so we can see edges of things? What about big aliens? - - return theSuccess; - -} - -char* AvHPlayer::GetPlayerModelKeyName() -{ - // Default to marine in ready room - char* theModelKeyName = kSoldierName; - - AvHUser3 theUser3 = this->GetUser3(); - - switch(theUser3) - { - case AVH_USER3_MARINE_PLAYER: - theModelKeyName = kSoldierName; - if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_13)) - { - theModelKeyName = kHeavyName; - } - break; - case AVH_USER3_COMMANDER_PLAYER: - theModelKeyName = kCommanderName; - break; - case AVH_USER3_ALIEN_PLAYER1: - theModelKeyName = kAlien1Name; - break; - case AVH_USER3_ALIEN_PLAYER2: - theModelKeyName = kAlien2Name; - break; - case AVH_USER3_ALIEN_PLAYER3: - theModelKeyName = kAlien3Name; - break; - case AVH_USER3_ALIEN_PLAYER4: - theModelKeyName = kAlien4Name; - break; - case AVH_USER3_ALIEN_PLAYER5: - theModelKeyName = kAlien5Name; - break; - case AVH_USER3_ALIEN_EMBRYO: - theModelKeyName = kAlienGestationName; - break; - } - - return theModelKeyName; -} - -void AvHPlayer::HandleOverwatch(void) -{ - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) - { - this->mOverwatchFiredThisThink = false; - - // If in overwatch - if(this->mInOverwatch) - { - // do we have a target we're firing at? - if(this->mOverwatchTarget != -1) - { - // is target around? - edict_t* theTargetEdict = INDEXENT(this->mOverwatchTarget); - if(!FNullEnt(theTargetEdict)) - { - // if so, move aim toward it but not farther than x degrees from starting pos - CBaseEntity* theTarget = CBaseEntity::Instance(theTargetEdict); - this->TurnOverwatchTowardsTarget(theTarget); - - // is it within our weapon range? - float theTargetDistance = (theTarget->pev->origin - this->pev->origin).Length(); - AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); - if(theWeapon) - { - if(theWeapon->GetRange() >= theTargetDistance) - { - // do we have a clear line of sight to it? - bool theCanSeeTarget = this->GetIsEntityInSight(theTarget); - if(theCanSeeTarget || (gpGlobals->time - this->mTimeLastSeenOverwatchTarget < kOverwatchKeepFiringAfterMissingTargetTime)) - { - // do we have ammo left? - if(!theWeapon->UsesAmmo() || (theWeapon->m_iClip > 0)) - { - // FIRE! - this->pev->button |= IN_ATTACK; - this->mOverwatchFiredThisThink = true; - } - - // Update last time we saw our target - if(theCanSeeTarget) - { - this->mTimeLastSeenOverwatchTarget = gpGlobals->time; - - // Playback event to increase tension, but keep network usage down (once or twice a second?) - if(this->pev->fuser2 != -1) - { - //if(RANDOM_LONG(0, 100) == 0) - //{ - //PLAYBACK_EVENT_FULL(0, this->edict(), gTensionOverwatchEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - //} - } - } - } - - // if not, has it been a long time since we've seen it, or did we see it die? Reset. - if( (gpGlobals->time - this->mTimeLastSeenOverwatchTarget > kOverwatchLostTargetTime) || - (theCanSeeTarget && !theTarget->IsAlive())) - { - this->ResetOverwatch(); - } - } - } - else - { - // This can happen when dropping weapons or switching roles - this->TurnOffOverwatch(); - } - } - else - { - this->ResetOverwatch(); - } - } - // is there a new target in range? - else - { - this->AcquireOverwatchTarget(); - } - - // see if we moved so we're out - if(this->GetDoesCurrentStateStopOverwatch()) - { - this->TurnOffOverwatch(); - } - } - else - { - if(this->GetDoesCurrentStateStopOverwatch()) - { - this->mTimeOfLastOverwatchPreventingAction = gpGlobals->time; - } - - // if overwatch is enabled, see if we've been still long enough to put us into it - if(this->mOverwatchEnabled && !(this->pev->flags & FL_FAKECLIENT)) - { - if(this->mTimeOfLastOverwatchPreventingAction != -1) - { - if(gpGlobals->time - this->mTimeOfLastOverwatchPreventingAction >= kOverwatchAcquireTime) - { - // if so, set overwatch on, make sure to set the current weapon into overwatch - this->TurnOnOverwatch(); - } - } - } - } - } -} - -void AvHPlayer::InternalAlienUpgradesThink() -{ - // If we're an alien player - AvHTeam* theTeam = this->GetTeamPointer(); - - if(this->GetIsAlien()) - { - if(theTeam && !GetGameRules()->GetIsCombatMode()) - { - // Preserve health and armor percentages as we get and remove upgrades - float theHealthPercentage = this->pev->health/AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - float theArmorPercentage = this->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); - - ASSERT(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN); - AvHAlienUpgradeListType theUpgrades = theTeam->GetAlienUpgrades(); - - // If player has already decided on the direction to upgrade, spend any extra upgrade levels in that category - AvHAddHigherLevelUpgrades(theUpgrades, this->pev->iuser4); - - // If we have more upgrades then we should, remove one randomly - int theNumRemoved = AvHRemoveExcessUpgrades(theUpgrades, this->pev->iuser4); - if(theNumRemoved > 0) - { - // Play a sound indicating this has happened - this->PlayHUDSound(HUD_SOUND_ALIEN_UPGRADELOST); - } - - // If we're cloaked, and we no longer have any sensory upgrades, trigger uncloak - //int theNumSensoryUpgrades = AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_SENSORY); - //if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_CLOAKED) && (theNumSensoryUpgrades == 0)) - //{ - // this->TriggerUncloak(); - //} - - this->pev->health = theHealthPercentage*AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - this->pev->armorvalue = theArmorPercentage*AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); - } - - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && this->IsAlive()) - { - // If we have cloaking, update our cloak state - this->InternalAlienUpgradesCloakingThink(); - - // If we have regeneration, heal us - this->InternalAlienUpgradesRegenerationThink(); - - // If we have pheromones, update them - //this->InternalAlienUpgradesPheromonesThink(); - } - } - - // Update ensnare here - if(this->GetIsEnsnared()) - { - if(gpGlobals->time > this->mTimeToBeUnensnared) - { - this->SetEnsnareState(false); - } - } - - // Update stun - if(this->GetIsStunned()) - { - if(gpGlobals->time > this->mTimeToBeFreeToMove) - { - this->SetIsStunned(false); - } - } -} - -bool AvHPlayer::GetIsCloaked() const -{ - bool theIsCloaked = false; - - if( (this->GetOpacity() < 0.1f)) - { - theIsCloaked = true; - } - - return theIsCloaked; -} - -bool AvHPlayer::GetIsPartiallyCloaked() const -{ - bool theIsCloaked = false; - - if( (this->GetOpacity() < 0.6f)) - { - theIsCloaked = true; - } - - return theIsCloaked; -} - -bool AvHPlayer::GetRandomGameStartedTick(float inApproximateFrameRate) -{ - bool theTimeToTick = false; - - if(GetGameRules()->GetGameStarted() && (inApproximateFrameRate > 0)) - { - ASSERT(this->mPreThinkFrameRate > 0); - - int theUpperBound = (int)(this->mPreThinkFrameRate/inApproximateFrameRate); - if(RANDOM_LONG(1, theUpperBound) == 1) - { - theTimeToTick = true; - } - } - - return theTimeToTick; -} - -void AvHPlayer::TriggerUncloak() -{ - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && this->IsAlive()) - { - this->mTriggerUncloak = true; - - SetUpgradeMask(&this->pev->iuser4, MASK_SENSORY_NEARBY, false); - - this->Uncloak(); - } -} - -void AvHPlayer::InternalAlienUpgradesPheromonesThink() -{ - const float kPheromoneUpdateInterval = .4f; - const float kPheromoneBaseRange = 600; - const int kMaxPheromonePuffs = 3; - - if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_8)) - { - int theRange = kPheromoneBaseRange; - - if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_14)) - { - theRange *= 2; - } - else if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_15)) - { - theRange *= 3; - } - - if(this->mTimeOfLastPheromone == -1 || (gpGlobals->time > this->mTimeOfLastPheromone + kPheromoneUpdateInterval)) - { - typedef std::map PlayerDistanceListType; - PlayerDistanceListType thePlayerDistanceList; - - // Look for players in range to draw pheromones of - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - - // Find nearest distance to friendly and relevant player - if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team) && !GetHasUpgrade(theEntity->pev->iuser4, MASK_TOPDOWN) && (theEntity != this) /*&& !this->GetIsEntityInSight(theEntity)*/) - { - double theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); - if(theDistance < theRange) - { - // Choose nearest x players to emit from - thePlayerDistanceList[theEntity->entindex()] = theDistance; - } - } - - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - for(int theNumPheromonePuffs = 0; (theNumPheromonePuffs < kMaxPheromonePuffs) && (thePlayerDistanceList.size() > 0); theNumPheromonePuffs++) - { - // Find the nearest entity - PlayerDistanceListType::iterator theClosestIter = thePlayerDistanceList.begin(); - float theClosestDistance = sqrt(kMaxMapDimension*kMaxMapDimension + kMaxMapDimension*kMaxMapDimension); - - for(PlayerDistanceListType::iterator theIter = thePlayerDistanceList.begin(); theIter != thePlayerDistanceList.end(); theIter++) - { - float theCurrentRange = theIter->second; - if(theCurrentRange < theClosestDistance) - { - theClosestDistance = theCurrentRange; - theClosestIter = theIter; - } - } - - // Play a puff for it - int theCurrentEntityIndex = theClosestIter->first; - CBaseEntity* theCurrentEntity = (CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theCurrentEntityIndex))); - ASSERT(theCurrentEntity); - - AvHSUPlayParticleEvent(kpsPheromoneEffect, this->edict(), theCurrentEntity->pev->origin, FEV_HOSTONLY); - - // Delete that entity - thePlayerDistanceList.erase(theClosestIter); - } - - this->mTimeOfLastPheromone = gpGlobals->time; - } - } -} - -float AvHPlayer::GetCloakTime() const -{ - float theCloakTime = AvHCloakable::GetCloakTime(); - - if(this->GetIsAlien()) - { - // If we have cloaking upgrade, we cloak faster - int theCloakingLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_7); - if(theCloakingLevel > 0) - { - theCloakTime = BALANCE_VAR(kCloakTime)/theCloakingLevel; - } - } - - return theCloakTime; -} - -void AvHPlayer::InternalAlienUpgradesCloakingThink() -{ - // joev: - // 0000342 - Cloaking no longer depends on speed. - - // For some reason the lerk moves faster when turning - //const float kWalkSpeedFactor = (this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER3) ? .95f : .7f; - //const int kMaxSpeed = this->pev->maxspeed; - //elven - needs to update the speed factor here to count for celerity - see bug 342 - //int theBaseSpeed, theUnencumberedSpeed; - //this->GetSpeeds(theBaseSpeed, theUnencumberedSpeed); - //const int kMaxSpeed = this->pev->maxspeed; - //const int kMaxSpeed = (theUnencumberedSpeed > this->pev->maxspeed ) ? theUnencumberedSpeed : this->pev->maxspeed; - - //// If player moving too fast or trigger uncloak is set - //if( ( (this->pev->velocity.Length() > kMaxSpeed*kWalkSpeedFactor) && !GetHasUpgrade(this->pev->iuser4, MASK_SENSORY_NEARBY)) || this->mTriggerUncloak) - if(this->mTriggerUncloak) - { - //Uncloak - this->Uncloak(); - } - // :joev - else - { - // If we have cloaking upgrade - int theCloakingLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_7); - if(theCloakingLevel > 0) - { - // If time needed to cloak has passed - // puzl: 864 - float theMaxWalkSpeed=this->pev->maxspeed * ((this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER3) ? .95f : .7f); - float theSpeed=AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_SENSORY_NEARBY) ? 0.0f : this->pev->velocity.Length(); - this->SetSpeeds(theSpeed, this->pev->maxspeed*1.05, theMaxWalkSpeed); -// if ( this->pev->velocity.Length() < theMaxWalkSpeed ) -// { -// ALERT(at_console, "Cloaking\n"); - this->Cloak(); -// } - } - } - - this->mTriggerUncloak = false; -} - -bool AvHPlayer::Redeem() -{ - bool theSuccess = false; - - // Can only be pulled back if we have at least one active hive - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam && (theTeam->GetNumActiveHives() > 0)) - { - // Bring player back - if(this->GetTeamPointer()->GetNumActiveHives() > 0) - { - // Play redeem effect where it happened so attackers know it happened - PLAYBACK_EVENT_FULL(0, this->edict(), gStartCloakEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - - edict_t* theNewSpawnPoint = GetGameRules()->SelectSpawnPoint(this); - if(!FNullEnt(theNewSpawnPoint)) - { - //// Create building here to test getting stuck - //const int kOffset = 20; - //Vector theBaseOrigin = theNewSpawnPoint->v.origin; - //Vector theRandomOffset(theBaseOrigin.x + RANDOM_LONG(-kOffset, kOffset), theBaseOrigin.y + RANDOM_LONG(-kOffset, kOffset), theBaseOrigin.z + RANDOM_LONG(-kOffset, kOffset)); - //AvHSUBuildTechForPlayer(ALIEN_BUILD_MOVEMENT_CHAMBER, theRandomOffset, this); - - theNewSpawnPoint = GetGameRules()->SelectSpawnPoint(this); - if(!FNullEnt(theNewSpawnPoint)) - { - if(this->GetIsDigesting()) - { - this->StopDigestion(false); - } - - mTimeOfLastRedeem = gpGlobals->time; - - // Respawn player normally - this->InitPlayerFromSpawn(theNewSpawnPoint); - - PLAYBACK_EVENT_FULL(0, this->edict(), gStartCloakEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - } - - theSuccess = true; - } - } - } - - return theSuccess; -} - -void AvHPlayer::InternalAlienUpgradesRegenerationThink() -{ - // puzl - 0000856 - Add innate regeneration for all alien players. - // Add a small and silent innate health and armor regeneration for - // all alien players, similar to the innate regeneration of all alien - // chambers. If a player chooses the regeneration upgrade, it replaces - // the innate regeneration rather than adding to it. - - // If enough time has elapsed to heal - if((this->mTimeOfLastRegeneration == -1) || (gpGlobals->time - this->mTimeOfLastRegeneration > BALANCE_VAR(kAlienRegenerationTime))) - { - int theRegenLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_2); - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - - float theRegenAmount = 0.0f; - float theRegenPercentage = BALANCE_VAR(kAlienInnateRegenerationPercentage); - - // If we have regeneration upgrade, multiply the amount by the regen level - if(theRegenLevel > 0) - { - theRegenPercentage = BALANCE_VAR(kAlienRegenerationPercentage); - theRegenAmount = (theRegenPercentage*theMaxHealth)*theRegenLevel; - } - - // Innate regeneration is at a fixed rate - else { - theRegenAmount = theRegenPercentage*(float)theMaxHealth; - } - // Always do at least 1 health of innate regeneration - theRegenAmount=max(theRegenAmount, 1.0f); - - // Don't play a sound for innate regeneration - this->Regenerate(theRegenAmount, theRegenLevel > 0 ); - } - - // Do we have the redemption? - int theRedemptionLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_3); - if(theRedemptionLevel > 0) - { - // Is the player really hurting? - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - - if((this->pev->health < theMaxHealth*BALANCE_VAR(kRedemptionThreshold)) && this->IsAlive()) - { - const float kPullBackTime = 20.0f; - if((this->mLastTimeRedemptionTriggered == -1) || (gpGlobals->time > (this->mLastTimeRedemptionTriggered + kPullBackTime))) - { - // Chance per second - float theRedemptionChance = theRedemptionLevel*BALANCE_VAR(kRedemptionChance); - - // How many times is this being called per second? - float theThinkInterval = 1.0f; - - if(this->mLastTimeCheckedRedemption > 0) - { - // The longer the time between checks, the higher the chance of being redeemed - theThinkInterval = (gpGlobals->time - this->mLastTimeCheckedRedemption); - } - - // Chance per second times seconds elapsed - if(RANDOM_FLOAT(0, 1.0f) <= theRedemptionChance*theThinkInterval) - { - if(this->Redeem()) - { - this->mLastTimeRedemptionTriggered = gpGlobals->time; - } - } - } - } - } - - this->mLastTimeCheckedRedemption = gpGlobals->time; -} - -void AvHPlayer::ProcessEntityBlip(CBaseEntity* inEntity) -{ - // puzl: 982 - // Make alien hivesight range a balance var - const float kAlienFriendlyBlipRange = BALANCE_VAR(kHiveSightRange); - - // Is player alien? - bool theIsAlien = this->GetIsAlien(true); - - // Is player marine? - bool theIsMarine = this->GetIsMarine(true); - - ASSERT(theIsAlien || theIsMarine); - // Friendly? - bool theIsFriendly = ((inEntity->pev->team == 0) || (inEntity->pev->team == this->GetTeam())) ; - CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); - if(theSpectatingEntity) - { - theIsFriendly = ((theSpectatingEntity->pev->team == 0) || (theSpectatingEntity->pev->team == inEntity->pev->team)); - } - - // If this player in an alien - if(theIsAlien && this->IsAlive(true) && !GetHasUpgrade(inEntity->pev->iuser4, MASK_TOPDOWN)) - { - // elven added - don't let digesting players get rendered on parasite - bool theEntityIsDigesting = GetHasUpgrade(inEntity->pev->iuser4, MASK_DIGESTING); - bool theEntityIsParasited = (theEntityIsDigesting) ? false : GetHasUpgrade(inEntity->pev->iuser4, MASK_PARASITED); - bool theEntityIsNearSensory = (theEntityIsDigesting) ? false : GetHasUpgrade(inEntity->pev->iuser4, MASK_SENSORY_NEARBY); - bool theEntityIsInHiveSight = (theEntityIsNearSensory || theEntityIsParasited || (GetHasUpgrade(inEntity->pev->iuser4, MASK_VIS_SIGHTED) && inEntity->IsPlayer()) || theIsFriendly || (inEntity->pev->iuser3 == AVH_USER3_HIVE)); - - //bool theHasHiveSightUpgrade = true;//GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_UPGRADE_11) || GetGameRules()->GetIsTesting(); - bool theEntityIsInSight = this->GetIsEntityInSight(inEntity); - - // If we're processing a relevant player - AvHPlayer* theOtherPlayer = dynamic_cast(inEntity); - bool theIsSpectatingEntity = this->GetIsSpectatingPlayer(inEntity->entindex()); - if(theOtherPlayer && (theOtherPlayer != this) && !theIsSpectatingEntity && theOtherPlayer->GetIsRelevant()) - { - // Calculate angle and distance to player - Vector theVectorToEntity = inEntity->pev->origin - this->pev->origin; - float theDistanceToEntity = theVectorToEntity.Length(); - - // If friendly - if(theEntityIsInHiveSight && theIsFriendly && !theEntityIsInSight) - { - int8 theBlipStatus = kFriendlyBlipStatus; - if(GetGameRules()->GetIsEntityUnderAttack(inEntity->entindex())) - { - theBlipStatus = kVulnerableFriendlyBlipStatus; - } - - if(theOtherPlayer->GetUser3() == AVH_USER3_ALIEN_PLAYER2) - { - theBlipStatus = kGorgeBlipStatus; - } - - //if(theOtherPlayer->GetEnemySighted()) - //{ - // theBlipStatus = 1; - //} - - if(theBlipStatus || (theDistanceToEntity < kAlienFriendlyBlipRange)) - { - // Info is the player index by default - int theBlipInfo = theOtherPlayer->entindex(); - this->mFriendlyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, theBlipStatus, theBlipInfo); - } - } - // else if enemy - else - { - // If it's in range - //if(theDistanceToEntity < kAlienEnemyBlipRange) - //{ - - int8 theBlipStatus = kEnemyBlipStatus; - - bool theDrawBlip = false; - - if(!theIsFriendly && !theEntityIsInSight) - { - // If they're parasited - if(theEntityIsParasited) - { - theDrawBlip = true; - } - - // If we have scent of fear upgrade - don't render if being eaten. changed by elven. - if( (theEntityIsNearSensory || GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_9)) && !theEntityIsParasited && !GetHasUpgrade(inEntity->pev->iuser4, MASK_DIGESTING)) - { - int theRange = BALANCE_VAR(kScentOfFearRadiusPerLevel); - if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_14)) - { - theRange *= 2; - } - else if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_15)) - { - theRange *= 3; - } - - // ...and blip is within range - if( (theRange > theDistanceToEntity) || theEntityIsNearSensory ) - { -// int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theOtherPlayer->pev->iuser4, theOtherPlayer->GetUser3()); -// -// // ...and blip is under attack or weak -// if(GetGameRules()->GetIsEntityUnderAttack(theOtherPlayer->entindex()) || (theOtherPlayer->pev->health < (theMaxHealth/3))) -// { - theBlipStatus = kVulnerableEnemyBlipStatus; - theDrawBlip = true; -// } - } - } - } - - // Add it if it's in hive sight, or if we have scent of fear and he's marked - if(theDrawBlip) - { - this->mEnemyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, theBlipStatus); - } - //} - } - } - // else if we're processing a generic buildable - else - { - AvHBaseBuildable* theBaseBuildable = dynamic_cast(inEntity); - if(theBaseBuildable) - { - // If friendly hive - if(theIsFriendly) - { - // If not visible - if(!theEntityIsInSight) - { - // If it's being hurt - bool theDrawBlip=false; - AvHTeam* theTeam = this->GetTeamPointer(); - - AvHAlertType theAlertType = ALERT_NONE; - int8 theBlipStatus = kFriendlyBlipStatus; - bool theIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theBaseBuildable->entindex()); - if(theIsUnderAttack) - { - theBlipStatus = kVulnerableFriendlyBlipStatus; - theDrawBlip=true; - } - - // Add it if relevant - AvHHive* theHive = dynamic_cast(inEntity); - if(theHive ) - { - theDrawBlip=true; - if(!theIsUnderAttack) - { - theBlipStatus = kHiveBlipStatus; - } - if ( theHive && !theHive->GetIsBuilt() ) - { - theBlipStatus=kUnbuiltHiveBlipsStatus; - } - } - if ( theDrawBlip == true ) - { - // Info is the player index or the kBaseBlipInfoOffset + the iuser3 - int theBlipInfo = kBaseBlipInfoOffset + inEntity->pev->iuser3; - if(theBlipInfo >= sizeof(char)*128) - { - ASSERT(false); - } - this->mFriendlyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, theBlipStatus, theBlipInfo); - } - } - } - // If enemy - else - { - // If it's in hive sight, but not visible - if(!theEntityIsInSight && theEntityIsInHiveSight) - { - // If within range - //if(theDistanceToEntity < kAlienEnemyBlipRange) - //{ - // Add it - this->mEnemyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, kEnemyStructureBlipsStatus); - //} - } - } - } - } - } - // else if this player is a marine - else if(theIsMarine && this->IsAlive(true)) - { - bool theTeamHasMotionTracking = GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_8); - - // If player is commander (or is any marine with motion tracking?) - if((this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) || (theTeamHasMotionTracking)) - { - - // If enemy, add it to enemy list if "detected" // Elven - we don't want motion blips on aliens visible to us. - // && !(inEntity->pev->iuser4 & MASK_VIS_SIGHTED) <- this won't work as if I see an alien, other marines won't see MT if they're in another room - bool visibleToThis = this->GetIsEntityInSight(inEntity); - if(!theIsFriendly && (inEntity->pev->iuser4 & MASK_VIS_DETECTED) && !visibleToThis && inEntity->IsAlive())//voogru: make sure there alive - { - this->mEnemyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, kMarineEnemyBlipStatus); - } - } - } -} - -void AvHPlayer::ClearBlips() -{ - this->mEnemyBlips.Clear(); - this->mFriendlyBlips.Clear(); -} - -void AvHPlayer::ClientDisconnected() -{ - this->InitBalanceVariables(); - this->ResetGameNewMap(); - this->ResetEntity(); -} - -void AvHPlayer::ResetGameNewMap() -{ - this->mNumParticleTemplatesSent = 0; - this->mTimeOfLastParticleTemplateSending = -1; - this->mClientGamma = kDefaultMapGamma; - this->mNewMap = true; -} - -void AvHPlayer::InternalCommanderThink() -{ - // Finally, if we are commander, set the special PAS origin to use - if(this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) - { - // Only update every so often - const float kUpdatePASInterval = 1.0f; - float theTime = gpGlobals->time; - if((this->mTimeOfLastPASUpdate == -1) || (theTime > (this->mTimeOfLastPASUpdate + kUpdatePASInterval))) - { - // Default to our last known "real-world" origin, in case there are no other players? - VectorCopy(this->mPositionBeforeTopDown, this->mSpecialPASOrigin); - - // Max map size is 8012x8012 - double theShortestDistance = 64192144; - - // Loop through all players - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - - // Find nearest distance to friendly and relevant player - if(theEntity->GetIsRelevant() && (theEntity->pev->team == this->pev->team) && (theEntity != this)) - { - double theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); - if(theDistance < theShortestDistance) - { - VectorCopy(theEntity->pev->origin, this->mSpecialPASOrigin); - theShortestDistance = theDistance; - - //SET_VIEW(ENT(this->pev), ENT(theEntity->pev)); - } - } - - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - this->mTimeOfLastPASUpdate = theTime; - } - } -} - -void AvHPlayer::InternalBoundResources() -{ - // Aliens have a max resource amount, put any that overflows back into the team - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam && this->GetIsAlien()) - { - float theMaxResources = theTeam->GetMaxResources((AvHUser3)this->pev->iuser3); - - float theExtraResources = this->mResources - theMaxResources; - if(theExtraResources > 0) - { - theTeam->SetTeamResources(theTeam->GetTeamResources() + theExtraResources); - } - - this->mResources = min(this->mResources, theMaxResources); - } - - if(GetGameRules()->GetIsCombatMode()) - { - this->mResources = 0; - } -} - -bool AvHPlayer::QueryEnemySighted(CBaseEntity* inEntity) -{ - bool theSuccess = false; - - if((this->pev->team != inEntity->pev->team) && (this->pev->team != TEAM_IND) && (inEntity->pev->team != TEAM_IND)) - { - if(inEntity != this) - { - if(inEntity->IsAlive() && this->GetIsEntityInSight(inEntity)) - { - AvHPlayer* thePlayer = dynamic_cast(inEntity); - //this->CompareToPlayer(inEntity); //voogru: WTF? - if(!thePlayer || (thePlayer->GetIsRelevant() && !thePlayer->GetIsCloaked())) - { - this->mEnemySighted = true; - this->mTimeOfLastEnemySighting = gpGlobals->time; - theSuccess = true; - } - } - } - } - return theSuccess; -} - -void AvHPlayer::InternalAlienThink() -{ - if(this->GetIsAlien() && this->IsAlive()) - { - // Are we gestating? Has enough time passed so we are something new? - if(this->pev->iuser3 == AVH_USER3_ALIEN_EMBRYO) - { - float theCurrentTime = gpGlobals->time; - float theFullTimeToGestate = GetGameRules()->GetBuildTimeForMessageID(this->mEvolution); - if(GetGameRules()->GetIsTesting()) - { - theFullTimeToGestate = 1.0f; - } - - this->TriggerProgressBar(this->entindex(), 3); - - // If changing this, make sure to change spectator behavior in InternalCommonThink - float theTimeElapsed = theCurrentTime - this->mTimeGestationStarted; - this->pev->fuser3 = (theTimeElapsed/theFullTimeToGestate)*kNormalizationNetworkFactor; - - if(theTimeElapsed >= theFullTimeToGestate) - { - this->ProcessEvolution(); - - // Set ourselves crouching so we have a smaller chance of getting stuck - if(AvHMUGetCanDuck(this->pev->iuser3)) - { - SetBits(this->pev->flags, FL_DUCKING); - } - } - } - - // Has enough time passed since we started screaming? - if(this->mIsScreaming) - { - if(gpGlobals->time > (this->mTimeStartedScream + BALANCE_VAR(kPrimalScreamDuration))) - { - this->mIsScreaming = false; - } - } - - // Uncloak if we are charging - if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT)) - { - this->TriggerUncloak(); - } - } -} - -void AvHPlayer::InternalCommonThink() -{ - if(GetGameRules()->GetGameStarted()) - { - this->mPreThinkTicks++; - - float theTimePassed = gpGlobals->time - GetGameRules()->GetTimeGameStarted(); - this->mPreThinkFrameRate = this->mPreThinkTicks/theTimePassed; - -// if(RANDOM_LONG(0, 125) == 0) -// { -// char theMessage[128]; -// sprintf(theMessage, "Num ents: %d\n", GetGameRules()->GetNumEntities()); -// //sprintf(theMessage, "Time passed: %f, ticks: %d, rate: %f, %d\n", theTimePassed, this->mPreThinkTicks, this->mPreThinkFrameRate, gNumFullPackCalls); -// UTIL_SayText(theMessage, this); -// } - } - else - { - this->mPreThinkTicks = 0; - this->mPreThinkFrameRate = 30; - } - - // If we're not in the ready room, and the a victor has just been determined, see if it's time to reset us - // This has to be done at a different time per player, to avoid overflows - if((GetGameRules()->GetVictoryTeam() != TEAM_IND) && (this->GetPlayMode() != PLAYMODE_READYROOM)) - { - // Get victory time, see if it's time to reset us - int thePlayerIndex = this->entindex(); - int theSecondToReset = kVictoryIntermission - 1 - kMaxPlayers/kResetPlayersPerSecond + (thePlayerIndex - 1)/kResetPlayersPerSecond; - //ASSERT(theSecondToReset >= 0); - //ASSERT(theSecondToReset < kVictoryIntermission); - - // NOTE: This depends on gamerules not allong players join a team after victory has been declared - float theVictoryTime = GetGameRules()->GetVictoryTime(); - if(gpGlobals->time > (theVictoryTime + theSecondToReset)) - { - this->SetPlayMode(PLAYMODE_READYROOM, true); - } - } - - // Must be called every frame to prevent exploiting - this->SetModelFromState(); - - - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - // Clear existing upgrades for marines. Aliens have their own individual upgrades. - if(this->GetIsMarine() && !GetGameRules()->GetIsCombatMode()) - { - int theInvertedUpgradeMask = ~kUpgradeBitMask; - this->pev->iuser4 &= theInvertedUpgradeMask; - } - - // Set the current upgrades - this->pev->iuser4 |= theTeam->GetTeamWideUpgrades(); - } - - // Update active and inactive inventory - const float kUpdateInventoryInterval = .5f; - if(gpGlobals->time > (this->mLastInventoryThink + kUpdateInventoryInterval)) - { - this->UpdateInventoryEnabledState(this->mNumHives, true); - this->mLastInventoryThink = gpGlobals->time; - } - - // Remember last time we were playing - if(this->GetPlayMode() == PLAYMODE_PLAYING) - { - this->mTimeLastPlaying = gpGlobals->time; - } - - this->InternalBoundResources(); - - // Players keep their health in fuser2 - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - int theCurrentHealth = max(0.0f, this->pev->health); - int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); - int theCurrentArmor = max(0.0f, this->pev->armorvalue); - - // Draw ring to take into account health and armor for aliens, just health for marines (so gorge and comm know when to heal) - float theScalar = (float)theCurrentHealth/theMaxHealth; - if(this->GetIsAlien()) - { - theScalar = (theCurrentHealth + theCurrentArmor)/(float)(theMaxHealth + theMaxArmor); - } - this->pev->fuser2 = theScalar*kNormalizationNetworkFactor; - - //float theRandomAngle = RANDOM_FLOAT(0, 50); - //this->pev->v_angle.x = theRandomAngle; - //VectorCopy(this->pev->angles, this->mAnglesBeforeTopDown); - //VectorCopy(this->pev->v_angle, this->mViewAnglesBeforeTopDown); - - if(GetGameRules()->GetCountdownStarted() && !GetGameRules()->GetGameStarted() && (GetPlayMode() == PLAYMODE_PLAYING) && !GetGameRules()->GetCheatsEnabled()) - { - this->pev->flags |= FL_FROZEN; - DROP_TO_FLOOR(this->edict()); - } - else - { - this->pev->flags &= ~FL_FROZEN; - } - - // If we have a different desired name, change to it - if(GetGameRules()->GetArePlayersAllowedToJoinImmediately()) - { - // If our desired net name hasn't been set, set it now - if(this->mDesiredNetName != "") - { - char* theInfoBuffer = g_engfuncs.pfnGetInfoKeyBuffer(this->edict()); - - char theBuffer[256]; - strcpy(theBuffer, this->mDesiredNetName.c_str()); - g_engfuncs.pfnSetClientKeyValue(this->entindex(), theInfoBuffer, "name", theBuffer); - - this->mDesiredNetName = ""; - } - } - - // If we're spectating a target, set our health and armorvalue to theirs - // For spectators that are tracking players, move to their location, to prevent problems outside of the PVS - AvHPlayer* theEntity = NULL; - if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) - { - // If entity is no longer spectatable, jump to the next target - if(theEntity->GetIsInTopDownMode() || (theEntity->GetPlayMode() != PLAYMODE_PLAYING)) - { - this->Observer_FindNextPlayer(); - } - else - { - this->pev->health = theEntity->pev->health; - this->pev->armorvalue = theEntity->pev->armorvalue; - this->pev->fuser3 = theEntity->pev->fuser3; - - if((theEntity->GetUser3() == AVH_USER3_ALIEN_EMBRYO) || (theEntity->GetIsBeingDigested())) - { - this->TriggerProgressBar(theEntity->entindex(), 3); - } - - // Hacky way to make sure player is in PVS of target - float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); - if(theDistance > 700) - { - VectorCopy(theEntity->pev->origin, this->pev->origin); - } - } - } - - // If we are digesting a player, process him (process even for marines in case we're testing) - this->InternalDigestionThink(); - - // Update FOV and view height every tick, needed for first-person spectating - this->SetViewForUser3(); - - PropagateServerVariables(); - -} - -void AvHPlayer::PropagateServerVariables() -{ - if ( gpGlobals->time > (mLastUpdateTime + 0.5f) ) - { - for (int i = 0; i < (signed)mServerVariableList.size(); ++i) - { - std::string theValue = CVAR_GET_STRING( mServerVariableList[i].mName.c_str() ); - - if ( mServerVariableList[i].mLastValueSent != theValue) - { - NetMsg_ServerVar( this->pev, mServerVariableList[i].mName, theValue ); - mServerVariableList[i].mLastValueSent = theValue; - break; // Only send one message per tick to avoid overflow. - } - } - mLastUpdateTime = gpGlobals->time; - } -} - -void AvHPlayer::InternalMarineThink() -{ - if(this->GetIsMarine() && (this->pev->iuser3 != AVH_USER3_COMMANDER_PLAYER)) - { - // Slowly heal power armor over time - if(this->GetHasPowerArmor()) - { - if(this->mLastPowerArmorThink != -1) - { - float theTimePassed = gpGlobals->time - this->mLastPowerArmorThink; - if(theTimePassed > 0.0f) - { - // Key regen to velocity to enhance leap-frogging, ala Halo - float theVelocity = this->pev->velocity.Length(); - float theRegenFactor = .5f; - if(theVelocity > 0) - { - //theRegenFactor = .3f - .3f*(theVelocity/kMaxGroundPlayerSpeed); - theRegenFactor = 0.1f; - } - theRegenFactor = max(min(theRegenFactor, 1.0f), 0.0f); - const float kPowerRegenRate = theRegenFactor*2.0f; - - int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); - if(this->pev->armorvalue < theMaxArmor) - { - this->pev->armorvalue = min((float)theMaxArmor, this->pev->armorvalue + kPowerRegenRate*theTimePassed); - } - } - } - this->mLastPowerArmorThink = gpGlobals->time; - } - - // Update buffed - if(this->GetIsCatalysted()) - { - if(gpGlobals->time > this->mTimeToEndCatalyst) - { - this->SetIsCatalysted(false); - } - } - } -} - -void AvHPlayer::InternalPreThink() -{ - PROFILE_START() - this->InternalCommonThink(); - PROFILE_END(kPlayerCommonThink); - - PROFILE_START() - this->InternalAlienThink(); - PROFILE_END(kPlayerAlienThink) - - PROFILE_START() - this->InternalMarineThink(); - PROFILE_END(kPlayerMarineThink) - - PROFILE_START() - this->InternalCommanderThink(); - PROFILE_END(kPlayerCommanderThink) - - PROFILE_START() - this->InternalAlienUpgradesThink(); - PROFILE_END(kPlayerAlienUpgradesThink) - - PROFILE_START() - this->InternalCombatThink(); - PROFILE_END(kPlayerCombatThink) - //this->InternalEnemySightedPreThink(); - - PROFILE_START() - this->InternalSpeakingThink(); - PROFILE_END(kPlayerSpeakingThink) - - PROFILE_START() - this->InternalProgressBarThink(); - PROFILE_END(kPlayerProgressBarThink) - - PROFILE_START() - this->InternalFogThink(); - PROFILE_END(kPlayerFogThink) - - PROFILE_START() - this->InternalHUDThink(); - PROFILE_END(kPlayerHUDThink) -} - -void AvHPlayer::InternalFogThink() -{ - if((this->mTimeOfLastFogTrigger != -1) && (gpGlobals->time > this->mTimeOfLastFogTrigger + this->mFogExpireTime)) - { - this->mCurrentFogEntity = -1; - } -} - -void AvHPlayer::InternalHUDThink() -{ - // Pull weapon out if we're done using something - if(this->mTimeOfLastUse != -1) - { - const float kUseTime = .5f; - if(gpGlobals->time > this->mTimeOfLastUse + kUseTime) - { - // Don't deploy while you're a commander (happens if you finish building something next to command station and immediately jump in) - if(!this->GetIsInTopDownMode() && !this->GetIsBeingDigested()) - { - this->DeployCurrent(); - this->mTimeOfLastUse = -1; - } - } - } - - // Don't hide chat by default - int theHideHUD = HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT | HIDEHUD_HEALTH; - - // Use local player or player we're spectating - AvHPlayer* theVisiblePlayer = this; - if(this->pev->iuser1 == OBS_IN_EYE) - { - AvHPlayer* theEntity = NULL; - if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) - { - theVisiblePlayer = theEntity; - } - } - - AvHPlayMode thePlayMode = theVisiblePlayer->GetPlayMode(); - AvHUser3 theUser3 = theVisiblePlayer->GetUser3(); - - if((thePlayMode == PLAYMODE_READYROOM) || (thePlayMode == PLAYMODE_REINFORCING) || (thePlayMode == PLAYMODE_AWAITINGREINFORCEMENT)) - { - theHideHUD = HIDEHUD_FLASHLIGHT | HIDEHUD_WEAPONS | HIDEHUD_HEALTH; - - // Only hide health when not following a target - if((thePlayMode == PLAYMODE_AWAITINGREINFORCEMENT) && (this->pev->iuser1 == OBS_IN_EYE)) - { - //theHideHUD &= ~HIDEHUD_WEAPONS; - theHideHUD &= ~HIDEHUD_HEALTH; - } - } - else if(thePlayMode == PLAYMODE_PLAYING) - { - theHideHUD = 0; - - if(!GetGameRules()->FAllowFlashlight()) - { - theHideHUD |= HIDEHUD_FLASHLIGHT; - } - - if(!theVisiblePlayer->pev->viewmodel) - { - //theHideHUD |= HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT; - } - - if(theUser3 == AVH_USER3_ALIEN_EMBRYO) - { - //theHideHUD |= HIDEHUD_HEALTH; - } - - if(GetHasUpgrade(theVisiblePlayer->pev->iuser4, MASK_TOPDOWN) || theVisiblePlayer->GetIsBeingDigested()) - { - theHideHUD |= HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT | HIDEHUD_HEALTH; - } - else - { - - // If we have no other weapons, hide ammo - - if(!HasWeapons()) - { - theHideHUD |= HIDEHUD_WEAPONS; - theHideHUD |= HIDEHUD_FLASHLIGHT; - } - - if(theUser3 == AVH_USER3_ALIEN_EMBRYO) - { - theHideHUD |= HIDEHUD_WEAPONS; - theHideHUD |= HIDEHUD_FLASHLIGHT; - } - } - } - else if(thePlayMode == PLAYMODE_OBSERVER) - { - theHideHUD = HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT | HIDEHUD_HEALTH; - - // Only hide health when not following a target - if((this->pev->iuser1 == OBS_IN_EYE)) - { - //theHideHUD &= ~HIDEHUD_WEAPONS; - theHideHUD &= ~HIDEHUD_HEALTH; - } - } - else - { - int a = 0; - } - - this->m_iHideHUD = theHideHUD; -} - - -void AvHPlayer::InternalProgressBarThink() -{ - // If some time has passed since the progress bar was triggered, send down a message to kill it - const float kProgressBarTimeOut = .2f; - if(this->mTimeProgressBarTriggered != -1) - { - if(gpGlobals->time > this->mTimeProgressBarTriggered + kProgressBarTimeOut) - { - this->mTimeProgressBarTriggered = -1; - this->mProgressBarEntityIndex = -1; - this->mProgressBarParam = -1; - } - } -} - -void AvHPlayer::InternalSpeakingThink() -{ - if((this->mIsSpeaking || this->mOrderAcknowledged || this->mOrdersRequested) && (gpGlobals->time - this->mTimeOfLastSaying >= kSpeakingTime)) - { - this->mIsSpeaking = false; - this->mOrderAcknowledged = false; - this->mOrdersRequested = false; - } -} - -void AvHPlayer::PreThink( void ) -{ - // Get play mode - AvHPlayMode thePlayMode = this->GetPlayMode(); - bool theRunThink = ((thePlayMode == PLAYMODE_READYROOM) && GET_RUN_CODE(8)) || - ((thePlayMode == PLAYMODE_OBSERVER) && (GET_RUN_CODE(16))) || - (this->GetIsAlien() && GET_RUN_CODE(32)) || - (this->GetIsMarine() && GET_RUN_CODE(64)); - - if(theRunThink) - { - PROFILE_START() - CBasePlayer::PreThink(); - PROFILE_END(kCBasePlayerPreThink) - - PROFILE_START() - this->InternalPreThink(); - PROFILE_END(kPlayerInternalPreThink) - - PROFILE_START() - this->ValidateClientMoveEvents(); - PROFILE_END(kValidateClientMoveEvents) - - PROFILE_START() - this->HandleTopDownInput(); - PROFILE_END(kHandleTopDownInput) - - PROFILE_START() - this->RecalculateSpeed(); - PROFILE_END(kRecalculateSpeed) - - PROFILE_START() - if(this->mQueuedThinkMessage != "") - { - this->SendMessage(this->mQueuedThinkMessage.c_str(), TOOLTIP); - this->mQueuedThinkMessage = ""; - } - if(this->mPendingCommand) - { - // Is this bad? - GetGameRules()->ClientCommand(this, this->mPendingCommand); - this->mPendingCommand = NULL; - } - PROFILE_END(kPlayerPreThinkMisc) - } -} - -bool AvHPlayer::PayPurchaseCost(int inCost) -{ - bool theSuccess = false; - - if(GetGameRules()->GetIsCombatMode()) - { - if(inCost <= (this->GetExperienceLevel() - this->GetExperienceLevelsSpent() - 1)) - { - this->SetExperienceLevelsSpent(this->GetExperienceLevelsSpent() + inCost); - theSuccess = true; - } - } - else - { - if(inCost <= this->GetResources()) - { - this->SetResources(this->GetResources() - inCost); - theSuccess = true; - } - } - - return theSuccess; -} - -void AvHPlayer::RecalculateSpeed(void) -{ - // Look at inventory and set speed from weight - int theRelevantWeight = this->GetRelevantWeight(); - - int theMaxWeight = GetGameRules()->GetMaxWeight(); - - int theBaseSpeed, theUnencumberedSpeed; - this->GetSpeeds(theBaseSpeed, theUnencumberedSpeed); - this->mMaxWalkSpeed = theUnencumberedSpeed*.75f; - - // Calculate the max speed - int theMaxSpeed = theUnencumberedSpeed - (theRelevantWeight/(float)theMaxWeight)*(theUnencumberedSpeed - theBaseSpeed); - theMaxSpeed = max(theMaxSpeed, theBaseSpeed); - theMaxSpeed = min(theMaxSpeed, theUnencumberedSpeed); - - // Set it but only if it changed (just in case there's a hidden performance or network cost) - if(this->pev->maxspeed != theMaxSpeed) - { - //char theMessage[256]; - //sprintf(theMessage, "New weight is %d, setting speed to %d.\n", theRelevantWeight, theMaxSpeed); - //ClientPrint(this->pev, HUD_PRINTTALK, theMessage); - - this->pev->maxspeed = theMaxSpeed; - g_engfuncs.pfnSetClientMaxspeed( ENT(this->pev), theMaxSpeed); - } -} - -void AvHPlayer::ReloadWeapon(void) -{ - if(this->m_pActiveItem) - { - CBasePlayerWeapon* theGun = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); - if(theGun) - { - //SetAnimation(PLAYER_RELOAD); - theGun->Reload(); - } - } -} - -// Reset stuff -void AvHPlayer::ResetEntity(void) -{ - CBasePlayer::ResetEntity(); - - this->mHasSeenTeamA = false; - this->mHasSeenTeamB = false; - - this->ResetBehavior(true); - - this->UpdateTopDownMode(); - - // Preserve items we want to survive init - bool theSavedNewMap = this->mNewMap; - string theSavedDesiredNetName = this->mDesiredNetName; - AvHBaseInfoLocationListType theSavedClientInfoLocations = this->mClientInfoLocations; - - this->Init(); - - this->mNewMap = theSavedNewMap; - this->mDesiredNetName = theSavedDesiredNetName; - this->mClientInfoLocations = theSavedClientInfoLocations; -} - -void AvHPlayer::ResetOverwatch() -{ - // clear target - this->mOverwatchTarget = -1; - this->pev->fuser1 = -1; - this->pev->fuser2 = -1; - - // Set facing back to original facing - VectorCopy(this->mOverwatchFacing, this->pev->angles); - this->pev->fixangle = TRUE; -} - - -#include "engine/studio.h" - -void AvHPlayer::SetModelFromState() -{ - // Default to marine in ready room - char* theModelName = NULL; - - AvHUser3 theUser3 = this->GetUser3(); - - switch(theUser3) - { - case AVH_USER3_MARINE_PLAYER: - theModelName = kMarineSoldierModel; - if(this->GetHasHeavyArmor()) - { - theModelName = kHeavySoldierModel; - } - break; - case AVH_USER3_COMMANDER_PLAYER: - theModelName = kMarineCommanderModel; - break; - case AVH_USER3_ALIEN_PLAYER1: - theModelName = kAlienLevelOneModel; - break; - case AVH_USER3_ALIEN_PLAYER2: - theModelName = kAlienLevelTwoModel; - break; - case AVH_USER3_ALIEN_PLAYER3: - theModelName = kAlienLevelThreeModel; - break; - case AVH_USER3_ALIEN_PLAYER4: - theModelName = kAlienLevelFourModel; - break; - case AVH_USER3_ALIEN_PLAYER5: - theModelName = kAlienLevelFiveModel; - break; - case AVH_USER3_ALIEN_EMBRYO: - theModelName = kAlienGestateModel; - break; - default: - if(this->GetPlayMode() == PLAYMODE_READYROOM) - { - theModelName = kReadyRoomModel; - } - break; - } - - //SET_MODEL(ENT(pev), theModelName); - - // Alternative method of setting the model on the server: - if(theModelName) - { - pev->model = MAKE_STRING(theModelName); - this->mLastModelIndex = MODEL_INDEX(theModelName); - } - - if(this->mLastModelIndex != 1) - { - pev->modelindex = mLastModelIndex; - } - - // Set body group for marine armor - this->pev->body = 0; - if(this->GetHasJetpack()) - { - this->pev->body = 1; - } -} - -void AvHPlayer::SetMoveTypeForUser3() -{ - switch(this->pev->iuser3) - { - case AVH_USER3_ALIEN_EMBRYO: -// this->pev->movetype = MOVETYPE_PUSH; -// break; - - case AVH_USER3_NONE: - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: - this->pev->movetype = MOVETYPE_WALK; - break; - } -} - -void AvHPlayer::GetSize(Vector& outMinSize, Vector& outMaxSize) const -{ - bool theIsDucking = FBitSet(this->pev->flags, FL_DUCKING); - - AvHSHUGetSizeForPlayerUser3((AvHUser3)this->pev->iuser3, outMinSize, outMaxSize, theIsDucking); -} - -void AvHPlayer::SetWeaponsForUser3() -{ - AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; - - bool theTeamHasGrenades = false; - - if(!GetGameRules()->GetIsCombatMode()) - { - AvHTeam* theTeamPointer = this->GetTeamPointer(false); - if(theTeamPointer) - { - theTeamHasGrenades = theTeamPointer->GetResearchManager().GetTechNodes().GetIsTechResearched(TECH_RESEARCH_GRENADES); - } - } - - switch(theUser3) - { - case AVH_USER3_NONE: - break; - - case AVH_USER3_MARINE_PLAYER: - if(this->mPreviousUser3 != AVH_USER3_COMMANDER_PLAYER) - { - this->GiveNamedItem(kwsMachineGun); - this->GiveNamedItem(kwsPistol); - this->GiveNamedItem(kwsKnife); - - if(theTeamHasGrenades) - { - this->GiveNamedItem(kwsGrenade); - } - } - break; - case AVH_USER3_COMMANDER_PLAYER: - break; - - // NOTE: When moving weapons/abilities around, be sure to change AvHBasePlayerWeapon::GetAnimationExtension(), and AvHSHUGetIsWeaponFocusable(). - case AVH_USER3_ALIEN_PLAYER1: - this->DestroyAllItems(FALSE); - this->GiveNamedItem(kwsBiteGun); - this->GiveNamedItem(kwsParasiteGun); - this->GiveNamedItem(kwsLeap); - this->GiveNamedItem(kwsDivineWind); - this->SwitchWeapon(kwsBiteGun); - break; - - case AVH_USER3_ALIEN_PLAYER2: - this->DestroyAllItems(FALSE); - this->GiveNamedItem(kwsHealingSpray); - this->GiveNamedItem(kwsSpitGun); - this->GiveNamedItem(kwsBileBombGun); - this->GiveNamedItem(kwsWebSpinner); - this->SwitchWeapon(kwsSpitGun); - break; - - case AVH_USER3_ALIEN_PLAYER3: - this->DestroyAllItems(FALSE); - this->GiveNamedItem(kwsBite2Gun); - //this->GiveNamedItem(kwsSpikeGun); - this->GiveNamedItem(kwsSporeGun); - this->GiveNamedItem(kwsUmbraGun); - this->GiveNamedItem(kwsPrimalScream); - this->SwitchWeapon(kwsBite2Gun); - break; - - case AVH_USER3_ALIEN_PLAYER4: - this->DestroyAllItems(FALSE); - this->GiveNamedItem(kwsSwipe); - this->GiveNamedItem(kwsBlinkGun); - this->GiveNamedItem(kwsAcidRocketGun); - this->GiveNamedItem(kwsMetabolize); - this->SwitchWeapon(kwsSwipe); - break; - - case AVH_USER3_ALIEN_PLAYER5: - this->DestroyAllItems(FALSE); - this->GiveNamedItem(kwsClaws); - this->GiveNamedItem(kwsDevour); - this->GiveNamedItem(kwsStomp); - this->GiveNamedItem(kwsCharge); - this->SwitchWeapon(kwsClaws); - break; - - case AVH_USER3_ALIEN_EMBRYO: - this->DestroyAllItems(FALSE); - break; - } -} - - -void AvHPlayer::SetSizeForUser3() -{ - AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; - Vector theMinSize; - Vector theMaxSize; - - // Use our previous User3 if we're back in the ready room after a game - if(this->GetPlayMode() == PLAYMODE_READYROOM) - { - if(this->mPreviousUser3 != AVH_USER3_NONE) - { - theUser3 = this->mPreviousUser3; - } - } - - this->GetSize(theMinSize, theMaxSize); - - UTIL_SetSize(this->pev, theMinSize, theMaxSize); - UTIL_SetOrigin(this->pev, this->pev->origin ); -} - -void AvHPlayer::GetViewForUser3(AvHUser3 inUser3, bool inIsDucking, float& outFOV, float& outOffset) const -{ - - switch(inUser3) - { - case AVH_USER3_NONE: - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_COMMANDER_PLAYER: - case AVH_USER3_ALIEN_PLAYER4: - default: - outFOV = 90; - outOffset = inIsDucking ? kDuckingViewHeightPercentage*HULL1_MAXZ: kStandingViewHeightPercentage*HULL0_MAXZ; - break; - - case AVH_USER3_ALIEN_PLAYER1: - outFOV = 105; - outOffset = 0; - break; - - case AVH_USER3_ALIEN_EMBRYO: - case AVH_USER3_ALIEN_PLAYER2: - outFOV = 100; - outOffset = 10; - break; - - case AVH_USER3_ALIEN_PLAYER3: - outFOV = 90; - outOffset = 10; - break; - - case AVH_USER3_ALIEN_PLAYER5: - outFOV = 90; - outOffset = inIsDucking ? kDuckingViewHeightPercentage*HULL0_MAXZ: kStandingViewHeightPercentage*HULL3_MAXZ; - break; - - } - -} - -void AvHPlayer::SetViewForUser3() -{ - - AvHUser3 theEndUser3 = this->GetUser3(true); - bool theIsDucking = FBitSet(this->pev->flags, FL_DUCKING); - - if (theEndUser3 == AVH_USER3_ALIEN_EMBRYO) - { - - bool theEndIsDucking = true; - - switch(GetEvolution(true)) - { - case ALIEN_LIFEFORM_ONE: - theEndUser3 = AVH_USER3_ALIEN_PLAYER1; - break; - case ALIEN_LIFEFORM_TWO: - theEndUser3 = AVH_USER3_ALIEN_PLAYER2; - break; - case ALIEN_LIFEFORM_THREE: - theEndUser3 = AVH_USER3_ALIEN_PLAYER3; - break; - case ALIEN_LIFEFORM_FOUR: - theEndUser3 = AVH_USER3_ALIEN_PLAYER4; - break; - case ALIEN_LIFEFORM_FIVE: - theEndUser3 = AVH_USER3_ALIEN_PLAYER5; - break; - default: - // For upgrades. - theEndUser3 = GetPreviousUser3(true); - break; - } - - // Linearly interpolate between the previous lifeform and the new lifeform. - - float theStartFOV; - float theStartOffset; - - float theEndFOV; - float theEndOffset; - - float amount = pev->fuser3 / kNormalizationNetworkFactor; - - AvHUser3 theStartUser3 = GetPreviousUser3(true); - - GetViewForUser3(theStartUser3, theIsDucking, theStartFOV, theStartOffset); - GetViewForUser3(theEndUser3, true, theEndFOV, theEndOffset); - - // Take into account that the origin will change for the offset. - - Vector theStartMinSize; - Vector theStartMaxSize; - - AvHSHUGetSizeForPlayerUser3(this->GetUser3(true), theStartMinSize, theStartMaxSize, theIsDucking); - - Vector theEndMinSize; - Vector theEndMaxSize; - - AvHSHUGetSizeForPlayerUser3(theEndUser3, theEndMinSize, theEndMaxSize, true); - theEndOffset += theStartMinSize.z - theEndMinSize.z; - - pev->fov = theStartFOV + amount * (theEndFOV - theStartFOV); - pev->view_ofs[2] = theStartOffset + amount * (theEndOffset - theStartOffset); - - } - else - { - GetViewForUser3(theEndUser3, theIsDucking, pev->fov, pev->view_ofs[2]); - } - - - -} - -bool AvHPlayer::SendMessage(const char *pMessage, SHOWMESSAGE_TYPE type) -{ - bool theSuccess = false; - - int theNumChars = strlen(pMessage); - if((theNumChars > 0) && (theNumChars < kMaxPlayerSendMessageLength)) - { - string theMessage(pMessage); - if(theMessage != this->mLastMessageSent) - { - UTIL_ShowMessage2(pMessage, this, type); - - this->mLastMessageSent = theMessage; - - theSuccess = true; - } - else - { - int a = 0; - } - } -// else -// { -// // Log error to console -// char theErrorMessage[10000]; -// sprintf(theErrorMessage, "Can't send message \"%s\" of length %d, max size is %d", pMessage, theNumChars, kMaxPlayerSendMessageLength); -// ALERT(at_logged, theErrorMessage); -// } - - return theSuccess; -} - -bool AvHPlayer::SendMessageOnce(const char *pMessage, SHOWMESSAGE_TYPE type) -{ - bool theSentMessage = false; - - string theMessage(pMessage); - - // Check if message is in sent list - StringList::iterator theIter = std::find(this->mSentMessageList.begin(), this->mSentMessageList.end(), theMessage); - if(theIter == this->mSentMessageList.end()) - { - // If not - // Call SendMessage - theSentMessage = this->SendMessage(pMessage, type); - - this->mLastMessageSent = theMessage; - - // Add message to list - this->mSentMessageList.push_back(theMessage); - } - - return theSentMessage; -} - -bool AvHPlayer::SendMessageNextThink(const char* pMessage) -{ - this->mQueuedThinkMessage = string(pMessage); - return true; -} - -void AvHPlayer::SetCurrentCommand(const struct usercmd_s* inCommand) -{ - memcpy(&this->mCurrentCommand, inCommand, sizeof(*inCommand)); -} - -void AvHPlayer::SetDebugCSP(weapon_data_t* inWeaponData) -{ - CBasePlayerWeapon* theCurrentWeapon = dynamic_cast(this->m_pActiveItem); - if(theCurrentWeapon && (theCurrentWeapon->m_iId == inWeaponData->m_iId)) - { - memcpy(&this->mDebugCSPInfo, inWeaponData, sizeof(weapon_data_t)); - } -} - -void AvHPlayer::StartObserver( Vector vecPosition, Vector vecViewAngle ) -{ - Vector theFadeColor; - theFadeColor.x = 0; - theFadeColor.y = 0; - theFadeColor.z = 0; - - UTIL_ScreenFade(this, theFadeColor, kTransitionFadeTime, 0.0f, 255, FFADE_IN); - - CBasePlayer::StartObserver(vecPosition, vecViewAngle); -} - -void AvHPlayer::ResetBehavior(bool inRemoveFromTeam) -{ - // remove observer mode if enabled - this->StopObserver(); - - // Leave top down mode if in it - this->StopTopDownMode(); - - // Stop digesting if you are - this->StopDigestion(false); - - // Stop being digested - this->SetBeingDigestedMode(false); - - // Reset room sounds - this->SetDesiredRoomType(0, true); - - // remove all equipment, but don't drop it (how to do this?) - this->DestroyAllItems(FALSE); - - if(inRemoveFromTeam) - { - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - theTeam->RemovePlayer(this->entindex()); - } - - // Clear experience - this->mExperience = 0.0f; - this->mExperienceLevelsSpent = 0; - this->mCombatNodes.Clear(); - this->mPurchasedCombatUpgrades.clear(); - this->mGiveCombatUpgrades.clear(); - } -} - -void AvHPlayer::SetPlayMode(AvHPlayMode inPlayMode, bool inForceSpawn) -{ - if(this->pev->playerclass != inPlayMode || inForceSpawn) - { - bool theGoingToReadyRoom = (inPlayMode == PLAYMODE_READYROOM); - this->ResetBehavior(theGoingToReadyRoom); - - if(!theGoingToReadyRoom) - { - // Clear player - //this->Init(); - this->ClearUserVariables(); - this->pev->rendermode = kRenderNormal; - this->pev->renderfx = kRenderFxNone; - this->pev->renderamt = 0; - } - - // Clear anim - this->m_szAnimExtention[0] = '\0'; - -// this->mUpgrades.clear(); - - AvHTeamNumber theTeamNumber = AvHTeamNumber(this->pev->team); - AvHUser3 theUser3 = AVH_USER3_NONE; - bool theSetUser3 = false; - - string theMessage; - - AvHTeam* theTeam = this->GetTeamPointer(false); - string theTeamName = kUndefinedTeam; - - switch(inPlayMode) - { - // Initialize stuff - case PLAYMODE_UNDEFINED: - this->pev->iuser3 = AVH_USER3_NONE; - this->pev->playerclass = PLAYMODE_UNDEFINED; - this->pev->team = TEAM_IND; - respawn(this->pev, FALSE); - break; - - case PLAYMODE_READYROOM: - this->pev->playerclass = PLAYMODE_READYROOM; - - if(theTeam) - { - theTeam->RemovePlayer(this->entindex()); - } - - this->pev->frags = 0; - this->mScore = 0; - this->mSavedCombatFrags = 0; - this->m_iDeaths = 0; - this->pev->team = TEAM_IND; - - // So we don't have the sideways camera because we're dead - this->pev->health = 100; - this->pev->max_health = pev->health; - this->pev->armorvalue = 0; - - respawn(this->pev, FALSE); - - if(this->pev->iuser3 == AVH_USER3_ALIEN_EMBRYO) - { - // Stop sound and allow movement, else we're stuck in the ready room - UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kGestationSound, 1.0f, .5, SND_STOP, 100); - SetUpgradeMask(&this->pev->iuser4, MASK_ALIEN_EMBRYO, false); - } - // Else "commander" draws on the scoreboard and it doesn't make sense to have a commander on the ground - else if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) - { - this->pev->iuser3 = AVH_USER3_MARINE_PLAYER; - } - //else if(this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER5) - //{ - // // Set player ducking to improve chances of them not getting stuck - // SetBits( m_afPhysicsFlags, PFLAG_DUCKING ); - // SetBits( this->pev->flags, FL_DUCKING ); - // this->pev->origin.z += 25; - //} - - theMessage = kReadyRoomMessage; - theTeamName = kUndefinedTeam; - break; - - case PLAYMODE_PLAYING: - // Don't set playmode if we couldn't respawn - this->pev->playerclass = PLAYMODE_PLAYING; - - //respawn(this->pev, FALSE); - - // Account for both sides, or to let player choose it somehow - if(this->GetClassType() == AVH_CLASS_TYPE_MARINE) - { - theUser3 = AVH_USER3_MARINE_PLAYER; - } - else - { - theUser3 = AVH_USER3_ALIEN_PLAYER1; // TEMPORARY - - // In combat mode, spawn player in as most advanced lifeform so player doesn't get stuck. - if(GetGameRules()->GetIsCombatMode() && !GetGameRules()->GetIsHamboneMode()) - { - // Find the most advanced lifeform - for(MessageIDListType::const_iterator theIter = this->mGiveCombatUpgrades.begin(); theIter != this->mGiveCombatUpgrades.end(); theIter++) - { - AvHMessageID theCurrentCombatUpgrade = *theIter; - switch(theCurrentCombatUpgrade) - { - case ALIEN_LIFEFORM_TWO: - theUser3 = AVH_USER3_ALIEN_PLAYER2; - break; - case ALIEN_LIFEFORM_THREE: - theUser3 = AVH_USER3_ALIEN_PLAYER3; - break; - case ALIEN_LIFEFORM_FOUR: - theUser3 = AVH_USER3_ALIEN_PLAYER4; - break; - case ALIEN_LIFEFORM_FIVE: - theUser3 = AVH_USER3_ALIEN_PLAYER5; - break; - } - } - } - } - - this->SetUser3(theUser3, true, false); - - respawn(this->pev, FALSE); - - this->SetWeaponsForUser3(); - - // In combat mode, add all player upgrades - if(GetGameRules()->GetIsCombatMode()) - { - if(GetGameRules()->GetIsHamboneMode()) - { - // Reset tech nodes - this->mGiveCombatUpgrades.clear(); - this->mCombatNodes = this->GetTeamPointer()->GetTechNodes(); - this->mExperienceLevelsSpent = 0; - } - else if(!GetGameRules()->GetIsIronMan()) - { - // Respend upgrades - this->GiveCombatUpgradesOnSpawn(); - } - } - - theTeam = this->GetTeamPointer(); - theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); - - this->mLastTimeStartedPlaying = gpGlobals->time; - this->mHasLeftReadyRoom = true; - break; - - case PLAYMODE_AWAITINGREINFORCEMENT: - //this->mRole = AVH_USER3_NONE; - // Preserve team, we could be brought back in - this->pev->team = theTeamNumber; - this->pev->playerclass = PLAYMODE_AWAITINGREINFORCEMENT; - - theTeam = this->GetTeamPointer(); - theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); - - this->StartObservingIfNotAlready(); - - theTeam = this->GetTeamPointer(); - theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); - theMessage = kReinforcementMessage; - this->mHasLeftReadyRoom = true; - break; - - case PLAYMODE_REINFORCING: - this->pev->team = theTeamNumber; - this->pev->playerclass = PLAYMODE_REINFORCING; - - theTeam = this->GetTeamPointer(); - theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); - - this->StartObservingIfNotAlready(); - - this->SendMessage(kReinforcingMessage, TOOLTIP); - this->mHasLeftReadyRoom = true; - break; - - case PLAYMODE_OBSERVER: - - if(theTeam) - { - theTeam->RemovePlayer(this->entindex()); - } - - // Set observer mode - this->StartObservingIfNotAlready(); - - // Set current team to indeterminate; we aren't allowed to join from spectating - this->pev->team = TEAM_IND; - this->pev->playerclass = PLAYMODE_OBSERVER; - - // Remember that we have spectated - this->mHasBeenSpectator = true; - theMessage = kObserverMessage; - theTeamName = kSpectatorTeam; - this->mHasLeftReadyRoom = true; - - this->SetHasSeenTeam(GetGameRules()->GetTeamA()->GetTeamNumber()); - this->SetHasSeenTeam(GetGameRules()->GetTeamB()->GetTeamNumber()); - break; - - case PLAYMODE_REINFORCINGCOMPLETE: - this->pev->playerclass = PLAYMODE_REINFORCINGCOMPLETE; - this->SendMessage(kReinforcementComplete, NORMAL); - break; - } - - // Force reset of entities because we just respawned - //this->ResetPlayerPVS(); - - // Inform gamerules of the change - GetGameRules()->ChangePlayerTeam(this, theTeamName.c_str(), false, false); - if(theTeam) - { - this->SetHasSeenTeam(theTeam->GetTeamNumber()); - } - - // Inform scoreboard - this->EffectivePlayerClassChanged(); - - if(theMessage != "") - { - // Send instructions to player - this->SendMessageNextThink(theMessage.c_str()); - } - } -} - -void AvHPlayer::GetNewOrigin(AvHUser3 inNewUser3, bool inCheckDucking, vec3_t& outOrigin) const -{ - - vec3_t theOldMinSize; - vec3_t theOldMaxSize; - - GetSize(theOldMinSize, theOldMaxSize); - - vec3_t theNewMinSize; - vec3_t theNewMaxSize; - - AvHSHUGetSizeForPlayerUser3(inNewUser3, theNewMinSize, theNewMaxSize, inCheckDucking); - - VectorCopy(pev->origin, outOrigin); - outOrigin[2] += theOldMinSize.z - theNewMinSize.z; - -} - -void AvHPlayer::SetUser3(AvHUser3 inUser3, bool inForceChange, bool inGiveWeapons) -{ - if((inUser3 != this->pev->iuser3) || inForceChange) - { - - // Make us duck so that it's easier to gestate in small areas. - - if(AvHMUGetCanDuck(this->pev->iuser3)) - { - - SetBits(this->pev->flags, FL_DUCKING); - SetBits( m_afPhysicsFlags, PFLAG_DUCKING ); - - // Important or the animations will get screwed up! - - m_Activity = ACT_RESET; - SetAnimation( PLAYER_IDLE ); - - } - - bool theIsDucking = FBitSet(this->pev->flags, FL_DUCKING); - - // Save off the current size of the player so that we can adjust the - // origin later. - - Vector theOldMinSize; - Vector theOldMaxSize; - - this->GetSize(theOldMinSize, theOldMaxSize); - - vec3_t theNewOrigin; - GetNewOrigin(inUser3, true, theNewOrigin); - - this->mPreviousUser3 = (AvHUser3)this->pev->iuser3; - - // Leave old User3 - switch(this->mPreviousUser3) - { - case AVH_USER3_MARINE_PLAYER: - break; - case AVH_USER3_ALIEN_PLAYER5: - this->StopDigestion(false); - break; - case AVH_USER3_COMMANDER_PLAYER: - this->StopTopDownMode(); - break; - } - - string theMessage; - - this->pev->iuser3 = inUser3; - - // Drop inventory, clear abilities - //this->DestroyAllItems(FALSE); - - bool theSavedAlienSightActive = this->mAlienSightActive; - - this->ClearRoleAbilities(); - - int theSavedUser4 = this->pev->iuser4; - bool theSavedIsSpectator = this->GetIsSpectator(); - - float theJetpackEnergy = mSavedJetpackEnergy; - mSavedJetpackEnergy = this->pev->fuser3; - - this->ClearUserVariables(); - - switch(inUser3) - { - case AVH_USER3_NONE: - this->pev->team = TEAM_IND; - break; - - case AVH_USER3_MARINE_PLAYER: - this->pev->iuser3 = inUser3; - theMessage = kSoldierMessage; - this->pev->movetype = MOVETYPE_WALK; - break; - case AVH_USER3_COMMANDER_PLAYER: - this->pev->iuser3 = inUser3; - this->StartTopDownMode(); - theMessage = kCommanderMessage; - break; - - // NOTE: When moving weapons/abilities around, be sure to change AvHBasePlayerWeapon::GetAnimationExtension() also - case AVH_USER3_ALIEN_PLAYER1: - this->pev->iuser3 = inUser3; - this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; - break; - - case AVH_USER3_ALIEN_PLAYER2: - this->pev->iuser3 = inUser3; - this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; - break; - - case AVH_USER3_ALIEN_PLAYER3: - this->pev->iuser3 = inUser3; - this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; - break; - - case AVH_USER3_ALIEN_PLAYER4: - this->pev->iuser3 = inUser3; - this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; - break; - - case AVH_USER3_ALIEN_PLAYER5: - this->pev->iuser3 = inUser3; - this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; - this->mLastGallopViewDirection = gpGlobals->v_forward; - break; - - case AVH_USER3_ALIEN_EMBRYO: - this->pev->iuser3 = inUser3; - this->pev->iuser4 |= MASK_ALIEN_EMBRYO; - theMessage = kGestationMessage; - break; - } - - // Preserve upgrades on a role change - this->pev->iuser4 |= theSavedUser4; - - // All players are selectable - this->pev->iuser4 |= MASK_SELECTABLE; - - // Get team-wide upgrades - AvHTeam* theTeamPointer = this->GetTeamPointer(); - if(theTeamPointer) - { - this->pev->iuser4 |= theTeamPointer->GetTeamWideUpgrades(); - } - - if(inGiveWeapons) - { - this->SetWeaponsForUser3(); - } - - // Adjust the size for the new user3. - - this->SetSizeForUser3(); - - // Adjust the origin of the player so that they are still on the ground. - - //Vector theNewMinSize; - //Vector theNewMaxSize; - //this->GetSize(theNewMinSize, theNewMaxSize); - //this->pev->origin[2] += theOldMinSize.z - theNewMinSize.z; - - if(this->mPreviousUser3 != AVH_USER3_COMMANDER_PLAYER) - { - UTIL_SetOrigin(this->pev, theNewOrigin); - } - - this->SetViewForUser3(); - this->SetMoveTypeForUser3(); - - if(theSavedIsSpectator) - { - this->SetIsSpectator(); - } - - if(this->pev->playerclass == PLAYMODE_AWAITINGREINFORCEMENT) - { - this->pev->playerclass = PLAYMODE_PLAYING; - } - - //this->SetModelFromState(); - float theHealthPercentage = 1.0f; - float theArmorPercentage = 1.0f; - if((this->mPreviousUser3 != AVH_USER3_NONE) /*&& (inUser3 != AVH_USER3_ALIEN_EMBRYO)*/) - { - theHealthPercentage = (float)this->pev->health/AvHPlayerUpgrade::GetMaxHealth(theSavedUser4, this->mPreviousUser3, this->GetExperienceLevel()); - theArmorPercentage = (float)this->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(theSavedUser4, this->mPreviousUser3); - } - - if (mPreviousUser3 == AVH_USER3_COMMANDER_PLAYER) - { - // Restore the jetpack energy from when the player went into the CC. - this->pev->fuser3 = theJetpackEnergy; - } - - //char theInitializeMessage[128]; - //sprintf(theInitializeMessage, "Initializing to user3: %d with health/armor percentages: %f/%f\n", inUser3, theHealthPercentage, theArmorPercentage); - //ClientPrint(this->pev, HUD_PRINTTALK, theInitializeMessage); - - this->InitializeFromTeam(theHealthPercentage, theArmorPercentage); - - this->SetModelFromState(); - - if(this->GetIsAlien()) - { - this->mAlienSightActive = theSavedAlienSightActive; - } - else - { - this->mAlienSightActive = false; - } - - // Update team - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - theTeam->ProcessRankChange(this, this->mPreviousUser3, this->GetUser3()); - } - - // Update scoreboard - this->EffectivePlayerClassChanged(); - - if(theMessage != "") - { - // Send instructions to player - this->SendMessageOnce(theMessage.c_str(), TOOLTIP); - } - this->LogEmitRoleChange(); - } -} - -void AvHPlayer::SetResources(float inResources, bool inPlaySound) -{ - if(!GetGameRules()->GetIsCombatMode()) - { - if(inResources < 0) - { - inResources = 0; - } - - AvHTeam* theTeam = this->GetTeamPointer(); - ASSERT(theTeam != NULL); - - if(this->GetIsMarine()) - { - if(inPlaySound) - { - this->PlayHUDSound(HUD_SOUND_MARINE_POINTS_RECEIVED); - - AvHPlayer* theCommander = this->GetCommander(); - if(theCommander) - { - theCommander->PlayHUDSound(HUD_SOUND_MARINE_POINTS_RECEIVED); - } - } - - theTeam->SetTeamResources(inResources); - } - else if(this->GetIsAlien()) - { - if(inPlaySound) - { - this->PlayHUDSound(HUD_SOUND_ALIEN_POINTS_RECEIVED); - } - - this->mResources = inResources; - } - - this->InternalBoundResources(); - - if(this->GetIsAlien()) - { - AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); - if(theServerPlayerData) - { - theServerPlayerData->SetResources(this->mResources); - } - } - } -} - -void AvHPlayer::Spawn( void ) -{ - CBasePlayer::Spawn(); - //this->PrecacheAndSetPlayerModel(); - - pev->classname = MAKE_STRING(kAvHPlayerClassName); - - this->mSendSpawnScreenFade = true; - - if(this->pev->playerclass != PLAYMODE_READYROOM) - { - this->PlayRandomRoleSound(kPlayerLevelSpawnSoundList, CHAN_ITEM, this->GetAlienAdjustedEventVolume()); - } - - //SET_MODEL(ENT(pev), "models/headcrab.mdl"); - //UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); - - SetTouch(&AvHPlayer::PlayerTouch); - - // Stop spectating - this->pev->iuser1 = 0; -} - -void AvHPlayer::StartObservingIfNotAlready(void) -{ - // Prevent this is the cvar is set - if ( allow_spectators.value ) - { - //CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pev); - if(!this->IsObserver()) - //if(!pPlayer->IsObserver()) - { - //if(this->mPlayMode == PLAYMODE_AWAITINGREINFORCEMENT) - //{ - // TODO: Start observer mode in chase cam on friendlies - //} - //else - //{ - edict_t *pentSpawnSpot = GetGameRules()->SelectSpawnPoint( this /*pPlayer*/ ); - if(!FNullEnt(pentSpawnSpot)) - { - this->StartObserver( VARS(pentSpawnSpot)->origin, VARS(pentSpawnSpot)->angles); - } - else - { - this->StartObserver( this->pev->origin, this->pev->angles); - } - } - } - else - { - this->ObserverModeIllegal(); - } -} - -bool AvHPlayer::SetBeingDigestedMode(bool inBeingDigested) -{ - bool theSuccess = false; - bool theIsDigesting = this->GetIsBeingDigested(); - - SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, false); - - if(inBeingDigested && !theIsDigesting) - { - // Fade player to black -// Vector theFadeColor; -// theFadeColor.x = 0; -// theFadeColor.y = 0; -// theFadeColor.z = 0; -// UTIL_ScreenFade(this, theFadeColor, .7f, 0.0f, 255, FFADE_OUT | FFADE_STAYOUT); - - this->HolsterCurrent(); - - this->pev->solid = SOLID_NOT; - this->pev->effects |= EF_NODRAW; - this->pev->takedamage = DAMAGE_NO; - - this->pev->movetype = MOVETYPE_FLY; - this->m_afPhysicsFlags |= PFLAG_OBSERVER; - - ClearBits( m_afPhysicsFlags, PFLAG_DUCKING ); - ClearBits( this->pev->flags, FL_DUCKING ); - VectorCopy(g_vecZero, this->pev->velocity); - - theSuccess = true; - } - else if(!inBeingDigested && theIsDigesting) - { - // Fade player up from black - Vector theFadeColor; - theFadeColor.x = 0; - theFadeColor.y = 0; - theFadeColor.z = 0; - UTIL_ScreenFade(this, theFadeColor, 1.0f, 0.0f, 255, FFADE_IN); - this->SetDesiredRoomType(0); - - this->DeployCurrent(); - - // Set physics - this->pev->solid = SOLID_SLIDEBOX; - this->pev->effects &= ~EF_NODRAW; - this->pev->takedamage = DAMAGE_YES; - - this->pev->movetype = MOVETYPE_WALK; - ClearBits(this->m_afPhysicsFlags, PFLAG_OBSERVER); - - // Set player ducking to improve chances of them not getting stuck - SetBits( m_afPhysicsFlags, PFLAG_DUCKING ); - SetBits( this->pev->flags, FL_DUCKING ); - - VectorCopy(g_vecZero, this->pev->velocity); - this->pev->fixangle = TRUE; - - theSuccess = true; - } - - if(theSuccess) - { - // Set digesting flag - SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, inBeingDigested); - this->EffectivePlayerClassChanged(); - } - - return theSuccess; -} - -void AvHPlayer::StartTopDownMode() -{ - if(!this->mInTopDownMode && !this->GetCurrentWeaponCannotHolster()) - { - Vector theFadeColor; - theFadeColor.x = 0; - theFadeColor.y = 0; - theFadeColor.z = 0; - UTIL_ScreenFade(this, theFadeColor, kTransitionFadeTime, 0.0f, 255, FFADE_IN); - - VectorCopy(this->pev->origin, this->mPositionBeforeTopDown); - VectorCopy(this->pev->angles, this->mAnglesBeforeTopDown); - VectorCopy(this->pev->v_angle, this->mViewAnglesBeforeTopDown); - VectorCopy(this->pev->view_ofs, this->mViewOfsBeforeTopDown); - this->mAnimExtensionBeforeTopDown = this->m_szAnimExtention; - - this->HolsterCurrent(); - - this->mTimeStartedTopDown = gpGlobals->time; - - this->mOverwatchEnabled = false; - SetUpgradeMask(&this->pev->iuser4, MASK_TOPDOWN); - this->m_afPhysicsFlags |= PFLAG_OBSERVER; - this->pev->effects |= EF_NODRAW; - this->pev->view_ofs = g_vecZero; - this->pev->gravity = 0; - - this->pev->solid = SOLID_NOT; - this->pev->takedamage = DAMAGE_NO; - - //this->pev->movetype = MOVETYPE_NOCLIP; - //this->pev->movetype = MOVETYPE_WALK; - this->pev->movetype = MOVETYPE_FLY; - this->m_afPhysicsFlags |= PFLAG_OBSERVER; - - ClearBits( m_afPhysicsFlags, PFLAG_DUCKING ); - ClearBits( this->pev->flags, FL_DUCKING ); - //this->pev->deadflag = DEAD_RESPAWNABLE; - //this->pev->deadflag = DEAD_RESPAWNABLE; - //this->pev->velocity[0] = 0.0f; - //this->pev->velocity[1] = 0.0f; - //this->pev->velocity[2] = -1.0f; - - -// float theMinViewHeight, theMaxViewHeight; -// float theMinX, theMaxX; -// float theMinY, theMaxY; -// bool theDrawMapBG; -// GetGameRules()->GetMapExtents(theMinViewHeight, theMaxViewHeight, theMinX, theMinY, theMaxX, theMaxY, theDrawMapBG); - - this->pev->origin.z = GetGameRules()->GetMapExtents().GetMaxViewHeight(); - - this->mInTopDownMode = true; - - // Cheesy way to make sure player class change is sent to everyone - this->EffectivePlayerClassChanged(); - } -} - -bool AvHPlayer::GetHasLeftReadyRoom() const -{ - return this->mHasLeftReadyRoom; -} - -bool AvHPlayer::GetHasJetpack() const -{ - return this->GetIsMarine() && GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_7); -} - -bool AvHPlayer::GetHasHeavyArmor() const -{ - return this->GetIsMarine() && GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_13); -} - -bool AvHPlayer::GetHasGivenOrder() const -{ - return this->mHasGivenOrder; -} - -void AvHPlayer::SetHasGivenOrder(bool inState) -{ - this->mHasGivenOrder = inState; -} - -float AvHPlayer::GetTimeLastF4() const -{ - return this->mTimeOfLastF4; -} - -void AvHPlayer::SetTimeLastF4(float inTime) -{ - this->mTimeOfLastF4=inTime; -} - -float AvHPlayer::GetTimeStartedTopDown() const -{ - return this->mTimeStartedTopDown; -} - -float AvHPlayer::GetTimeOfLastSignificantCommanderAction() const -{ - return this->mTimeOfLastSignificantCommanderAction; -} - -bool AvHPlayer::GetHasAvailableUpgrades() const -{ - bool theHasPendingUpgrades = false; - - AvHTeam* theTeamPointer = this->GetTeamPointer(); - if(theTeamPointer) - { - AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); - - for(int i = ALIEN_UPGRADE_CATEGORY_INVALID + 1; i < ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE; i++) - { - AvHAlienUpgradeCategory theCurrentCategory = AvHAlienUpgradeCategory(i); - - // Now make sure we have an unspent upgrade available - if(AvHGetHasFreeUpgradeCategory(theCurrentCategory, theUpgrades, this->pev->iuser4)) - { - theHasPendingUpgrades = true; - break; - } - } - } - - return theHasPendingUpgrades; -} - - -bool AvHPlayer::GetHasPowerArmor() const -{ - return this->GetIsMarine() && GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_10); -} - -int AvHPlayer::GetHull() const -{ - int theHull = AvHMUGetHull(true, this->pev->iuser3); - - return theHull; -} - -bool AvHPlayer::GetIsTemporarilyInvulnerable() const -{ - bool theIsInvulnerable = false; - - if(GetGameRules()->GetIsCombatMode() && (this->GetPlayMode() == PLAYMODE_PLAYING)) - { - if(this->mLastTimeStartedPlaying != -1) - { - float theInvulnerableTimeSeconds = 0.0f; - - #ifdef DEBUG - theInvulnerableTimeSeconds = avh_spawninvulnerabletime.value; - #endif - - if(gpGlobals->time < (this->mLastTimeStartedPlaying + theInvulnerableTimeSeconds)) - { - theIsInvulnerable = true; - } - } - } - - if (mTimeOfLastRedeem != -1 && gpGlobals->time < mTimeOfLastRedeem + kRedeemInvulnerableTime) - { - theIsInvulnerable = true; - } - - return theIsInvulnerable; -} - -bool AvHPlayer::GetIsEnsnared() const -{ - return GetHasUpgrade(this->pev->iuser4, MASK_ENSNARED); -} - -bool AvHPlayer::GetIsAbleToAct() const -{ - return !GetIsInTopDownMode() && !GetIsBeingDigested() && !GetIsEnsnared() && !GetIsStunned(); -} - -bool AvHPlayer::SetEnsnareState(bool inState) -{ - bool theSuccess = true; - - if(inState) - { - // If too ensnared already, don't ensnare further - if(!this->GetIsEnsnared()) - { - this->mTimeToBeUnensnared = gpGlobals->time; - } - - if(!this->GetIsEnsnared() || ((this->mTimeToBeUnensnared + BALANCE_VAR(kEnsnareTime) - gpGlobals->time) < BALANCE_VAR(kMaxEnsnareTime))) - { - this->mLastTimeEnsnared = gpGlobals->time; - this->mTimeToBeUnensnared += BALANCE_VAR(kEnsnareTime); - - // Player is defenseless - this->HolsterCurrent(); - - SetUpgradeMask(&this->pev->iuser4, MASK_ENSNARED); - } - else - { - theSuccess = false; - } - } - else - { - SetUpgradeMask(&this->pev->iuser4, MASK_ENSNARED, false); - this->mTimeToBeUnensnared = -1; - this->mLastTimeEnsnared = -1; - - this->DeployCurrent(); - } - - return theSuccess; -} - -bool AvHPlayer::GetIsStunned() const -{ - return GetHasUpgrade(this->pev->iuser4, MASK_PLAYER_STUNNED); -} - -bool AvHPlayer::SetIsStunned(bool inState, float inTime) -{ - bool theSuccess = false; - - // Only able to stun walking players (prevents weird problems with players on ladders, who are treated as flying - if(inState && !this->GetIsStunned() && (this->pev->movetype == MOVETYPE_WALK)) - { - SetUpgradeMask(&this->pev->iuser4, MASK_PLAYER_STUNNED); - this->mTimeToBeFreeToMove = gpGlobals->time + inTime; - - Vector theFadeColor; - theFadeColor.x = 255; - theFadeColor.y = 255; - theFadeColor.z = 255; - float theFadeTime = .25f; - UTIL_ScreenFade(this, theFadeColor, theFadeTime, 0.0f, 128, FFADE_IN/* | FFADE_MODULATE*/); - - theSuccess = true; - - // Clear keys so they aren't held down - //this->ClearKeys(); - } - else if(!inState && this->GetIsStunned()) - { - SetUpgradeMask(&this->pev->iuser4, MASK_PLAYER_STUNNED, false); - this->mTimeToBeFreeToMove = -1; - } - - return theSuccess; -} - -bool AvHPlayer::GetIsCatalysted() const -{ - return this->GetIsMarine() && GetHasUpgrade(this->pev->iuser4, MASK_BUFFED); -} - -void AvHPlayer::SetIsCatalysted(bool inState, float inTime) -{ - if(this->GetIsMarine()) - { - if(inState && !this->GetIsCatalysted()) - { - SetUpgradeMask(&this->pev->iuser4, MASK_BUFFED); - this->mTimeToEndCatalyst = gpGlobals->time + inTime; - - // Trigger screen effect? - } - else - { - SetUpgradeMask(&this->pev->iuser4, MASK_BUFFED, false); - this->mTimeToEndCatalyst = -1; - } - } -} - -bool AvHPlayer::Energize(float inEnergyAmount) -{ - bool theSuccess = false; - - if(AvHMUGiveAlienEnergy(this->pev->fuser3, inEnergyAmount)) - { - theSuccess = true; - } - - return theSuccess; -} - -bool AvHPlayer::Heal(float inAmount, bool inPlaySound) -{ - int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); - bool theDidHeal = false; - float theAmount = inAmount; - - // If we aren't at full health, heal health - if(this->pev->health < theMaxHealth) - { - int theAmountToGive = theAmount; - theAmount -= (theMaxHealth - this->pev->health); //store relative amount compared to that necessary for complete heal - this->pev->health = min((float)theMaxHealth, this->pev->health + theAmountToGive); - theDidHeal = true; - } - else if(this->pev->armorvalue < theMaxArmor) - { - this->pev->armorvalue = min((float)theMaxArmor, this->pev->armorvalue + theAmount); - theDidHeal = true; - } - - // Play regen event - if(theDidHeal) - { - if(inPlaySound) - { - // Play regeneration event - PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - } - } - - return theDidHeal; -} - -bool AvHPlayer::Regenerate(float inRegenerationAmount, bool inPlaySound) -{ - bool theDidRegenerate = this->Heal(inRegenerationAmount, inPlaySound); - - if(theDidRegenerate) - { - this->mTimeOfLastRegeneration = gpGlobals->time; - } - - return theDidRegenerate; -} - -bool AvHPlayer::GetCanBeResupplied() const -{ - bool theCanBeResupplied = false; - - const float theResupplyTime = BALANCE_VAR(kResupplyTime); - - if((this->mTimeOfLastResupply == 0) || (gpGlobals->time > (this->mTimeOfLastResupply + theResupplyTime))) - { - if(this->m_pActiveItem) - { - AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem->GetWeaponPtr()); - if(theBaseWeapon && theBaseWeapon->CanHolster() && theBaseWeapon->GetCanBeResupplied()) - { - theCanBeResupplied = true; - } - } - - // If we don't have max health, or we need ammo - if(this->pev->health < this->pev->max_health) - { - theCanBeResupplied = true; - } - } - return theCanBeResupplied; -} - -bool AvHPlayer::Resupply(bool inGiveHealth) -{ - bool theSuccess = false; - - if(this->m_pActiveItem) - { - AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem->GetWeaponPtr()); - if(theBaseWeapon && theBaseWeapon->Resupply()) - { - theSuccess = true; - } - - if(inGiveHealth) - { - // puzl: 1017 armoury gives 10 health per use - if(AvHHealth::GiveHealth(this, BALANCE_VAR(kPointsPerArmouryHealth))) - { - // Play event for each person helped - //PLAYBACK_EVENT_FULL(0, this->edict(), gPhaseInEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); - theSuccess = true; - } - } - - this->mTimeOfLastResupply = gpGlobals->time; - } - - return theSuccess; -} - -bool AvHPlayer::GetIsScreaming() -{ - return this->mIsScreaming; -} - -void AvHPlayer::StartScreaming() -{ - this->mIsScreaming = true; - this->mTimeStartedScream = gpGlobals->time; -} - -bool AvHPlayer::StopTopDownMode() -{ - bool theSuccess = false; - - if(this->mInTopDownMode) - { - Vector theFadeColor; - theFadeColor.x = 0; - theFadeColor.y = 0; - theFadeColor.z = 0; - UTIL_ScreenFade(this, theFadeColor, kTransitionFadeTime, 0.0f, 255, FFADE_IN); - - this->DeployCurrent(); - - this->mOverwatchEnabled = true; - this->pev->effects &= ~EF_NODRAW; - this->pev->view_ofs = g_vecZero; - SetUpgradeMask(&this->pev->iuser4, MASK_TOPDOWN, false); - - // TODO: Make sure original gravity is 1? - this->pev->gravity = 1; - - this->pev->solid = SOLID_SLIDEBOX; - this->pev->takedamage = DAMAGE_YES; - - //this->pev->movetype = MOVETYPE_NONE; - this->pev->movetype = MOVETYPE_WALK; - ClearBits(this->m_afPhysicsFlags, PFLAG_OBSERVER); - this->pev->deadflag = DEAD_NO; - - VectorCopy(this->mPositionBeforeTopDown, this->pev->origin); - VectorCopy(this->mAnglesBeforeTopDown, this->pev->angles); - VectorCopy(this->mViewAnglesBeforeTopDown, this->pev->v_angle); - VectorCopy(this->mViewOfsBeforeTopDown, this->pev->view_ofs); - strcpy(this->m_szAnimExtention, this->mAnimExtensionBeforeTopDown.c_str()); - - VectorCopy(g_vecZero, this->pev->velocity); - this->pev->fixangle = TRUE; - this->mInTopDownMode = false; - - AvHTeamNumber theStationTeamNumber = (AvHTeamNumber)this->pev->team; - const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationLogoutTeamOne : kTargetCommandStationLogoutTeamTwo; - FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f); - - this->mSelected.clear(); - - // Need to reset groups when logging out, so they are re-propagated when logging in after another potential commander (see AvHHud::ResetTopDownUI) - for(int i = 0; i < kNumHotkeyGroups; i++) - { - this->mClientGroupAlerts[i] = ALERT_NONE; - - this->mClientGroups[i].clear(); - } - - this->mClientSelectAllGroup.clear(); - - theSuccess = true; - } - - return theSuccess; -} - -void AvHPlayer::SetPendingCommand(char* inCommand) -{ - this->mPendingCommand = inCommand; -} - -void AvHPlayer::TriggerFog(int inFogEntity, float inFogExpireTime) -{ - this->mCurrentFogEntity = inFogEntity; - this->mFogExpireTime = inFogExpireTime; - - // Allows resetting of fog entity - if(inFogEntity > -1) - { - this->mTimeOfLastFogTrigger = gpGlobals->time; - } -} - -void AvHPlayer::TriggerProgressBar(int inEntityID, int inParam) -{ - ASSERT(inEntityID >= 0); - - this->mProgressBarEntityIndex = inEntityID; - this->mProgressBarParam = inParam; - this->mTimeProgressBarTriggered = gpGlobals->time; -} - -float AvHPlayer::GetTimeOfLastTeleport() const -{ - return this->mTimeOfLastTeleport; -} - -float AvHPlayer::GetTimeLastPlaying() const -{ - return this->mTimeLastPlaying; -} - - -bool AvHPlayer::HolsterWeaponToUse() -{ - bool theSuccess = false; - - if(!this->GetCurrentWeaponCannotHolster()) - { - this->HolsterCurrent(); - - this->mTimeOfLastUse = gpGlobals->time; - - theSuccess = true; - } - return theSuccess; -} - -void AvHPlayer::SetTimeOfLastTeleport(float inTime) -{ - this->mTimeOfLastTeleport = inTime; -} - -void AvHPlayer::BecomePod() -{ - //ASSERT(this->mRole != AVH_USER3_ALIEN_EMBRYO); - - this->HolsterCurrent(); - - ClearBits(this->m_afPhysicsFlags, PFLAG_DUCKING); - ClearBits(this->pev->flags, FL_DUCKING); - - //EMIT_SOUND_DYN(ENT(this->pev), CHAN_VOICE, kGestationSound, 1, ATTN_NORM, 0, 100); - - float flSilenceLevel = this->GetAlienAdjustedEventVolume(); - - if(flSilenceLevel > 0.0) - UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kGestationSound, flSilenceLevel, 2.0, 0, 100); -} - -bool AvHPlayer::SwitchWeapon(const char* inString) -{ - bool theSuccess = false; - - if(!this->GetIsEnsnared()) - { - CBasePlayerWeapon* theCurrentWeapon; - - for (int i = 0 ; i < MAX_ITEM_TYPES ; i++ ) - { - theCurrentWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); - while( theCurrentWeapon ) - { - if(FClassnameIs(theCurrentWeapon->pev, inString)) - { - // this weapon is from the same category. - if ( theCurrentWeapon->CanDeploy() ) - { - theSuccess = CBasePlayer::SwitchWeapon( theCurrentWeapon ); - } - break; - } - theCurrentWeapon = dynamic_cast(theCurrentWeapon->m_pNext); - } - } - } - - return theSuccess; -} - -//BOOL AvHPlayer::SwitchWeapon( CBasePlayerItem* inWeapon ) -//{ -// CBasePlayerWeapon* theCurrentWeapon = dynamic_cast(this->m_pActiveItem); -// CBasePlayerItem* theWeapon = inWeapon; -// BOOL theSuccess = TRUE; -// -// if(!inWeapon) -// { -// if(theCurrentWeapon) -// { -// theCurrentWeapon->RetireWeapon(); -// } -// else -// { -// theSuccess = FALSE; -// } -// } -// else -// { -// CBasePlayer::SwitchWeapon(theWeapon); -// } -// -// return theSuccess; -//} - - -void AvHPlayer:: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if ( pev->takedamage && GetCanBeAffectedByEnemies()) - { - m_LastHitGroup = ptr->iHitgroup; - - // No locational damage in NS. - - /* - switch ( ptr->iHitgroup ) - { - case HITGROUP_GENERIC: - break; - case HITGROUP_HEAD: - flDamage *= gSkillData.plrHead; - break; - case HITGROUP_CHEST: - flDamage *= gSkillData.plrChest; - break; - case HITGROUP_STOMACH: - flDamage *= gSkillData.plrStomach; - break; - case HITGROUP_LEFTARM: - case HITGROUP_RIGHTARM: - flDamage *= gSkillData.plrArm; - break; - case HITGROUP_LEFTLEG: - case HITGROUP_RIGHTLEG: - flDamage *= gSkillData.plrLeg; - break; - default: - break; - } - */ - - // Player's aren't affected by structural damage, so don't create blood - // if that's the damage type. - - if (!(bitsDamageType & NS_DMG_STRUCTURAL)) - { - SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage); - TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); - } - - AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); - - } -} - - -int AvHPlayer::TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) -{ - //Even out the damage - //flDamage = ceil(flDamage); - - int theReturnValue = 0; - - if(GetGameRules()->GetGameStarted() && !this->GetIsTemporarilyInvulnerable()) - { - // Take into account handicap - if(!pevAttacker) - { - pevAttacker = pevInflictor; - } - - if(!pevInflictor) - { - pevInflictor = pevAttacker; - } - - AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(pevAttacker->team)); - if(theTeam) - { - float theHandicap = theTeam->GetHandicap(); - flDamage *= theHandicap; - } - - if(GetGameRules()->GetIsCheatEnabled(kcHighDamage)) - { - flDamage *= 10; - } - - if(bitsDamageType & NS_DMG_STRUCTURAL) - { - flDamage = 0.0f; - } - - // Do half damage to the heavy armor of HA and Onos - if(bitsDamageType & NS_DMG_LIGHT) - { - if(this->GetHasHeavyArmor() || (this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER5)) - { - flDamage *= .5f; - } - } - - // If we're metabolizing, convert the damage to energy -// if(this->GetIsMetabolizing()) -// { -// const float theFactor = BALANCE_VAR(kMetabolizeDamageEnergyFactor); -// float theEnergy = (flDamage/100.f)*theFactor; -// AvHMUGiveAlienEnergy(this->pev->fuser3, theEnergy); -// -// if((this->mTimeOfLastMetabolizeEvent == -1) || (gpGlobals->time > (this->mTimeOfLastMetabolizeEvent + 1.0f))) -// { -// // Playback metabolize success event -// PLAYBACK_EVENT_FULL(0, this->edict(), gMetabolizeSuccessEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); -// -// this->mTimeOfLastMetabolizeEvent = gpGlobals->time;; -// } -// -// theReturnValue = 0; -// } -// else -// { - - theReturnValue = CBasePlayer::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); - if(theReturnValue > 0) - { - float theSlowDownFactor = .8f; - float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); - - if(flDamage > theMaxHealth/2.0f) - { - this->PlayRandomRoleSound(kPlayerLevelWoundSoundList, CHAN_BODY, 1.0); - theSlowDownFactor = .3f; - } - else if(flDamage >= theMaxHealth/5.0f) - { - this->PlayRandomRoleSound(kPlayerLevelPainSoundList, CHAN_BODY, .8f); - theSlowDownFactor = .5f; - } - - // Slow down when hit - //VectorScale(this->pev->velocity, theSlowDownFactor, this->pev->velocity); - - if(!pevAttacker || (this->pev->team != pevAttacker->team) && (pevAttacker->team != 0)) - { - GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_PLAYER_ENGAGE, this->entindex()); - } - - if(pevAttacker) - { - CBasePlayer* inAttackingPlayer = dynamic_cast(CBaseEntity::Instance(ENT(pevAttacker))); - const char* inWeaponName = STRING(pevInflictor->classname); - if(inAttackingPlayer && inWeaponName) - { - this->LogPlayerAttackedPlayer(inAttackingPlayer, inWeaponName, flDamage); - } - } - - bool theDrawDamage = (CVAR_GET_FLOAT(kvDrawDamage) > 0); - - if(theDrawDamage) - { - this->PlaybackNumericalEvent(kNumericalInfoHealthEvent, (int)(-flDamage)); - } - - this->Uncloak(); - } -// } - } - - return theReturnValue; -} - -void AvHPlayer::PlaybackNumericalEvent(int inEventID, int inNumber) -{ - Vector theMinSize; - Vector theMaxSize; - this->GetSize(theMinSize, theMaxSize); - - Vector theStartPos = this->pev->origin; - theStartPos.z += theMaxSize.z; - - // Draw for everyone (team = 0 after flDamage parameter) - AvHSUPlayNumericEvent(inNumber, this->edict(), theStartPos, 0, inEventID, this->pev->team); -} - - -//const char* AvHPlayer::TeamID( void ) -//{ -// AvHTeam* theTeam = this->GetTeamPointer(); -// const char* theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); -// //const char* theTeamName = this->GetPlayerModelKeyName(); -// return theTeamName; -//} - -void AvHPlayer::TurnOffOverwatch() -{ - this->mInOverwatch = false; - AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); - if(theWeapon) - { - theWeapon->SetOverwatchState(false); - } - - //VectorCopy(this->mOverwatchFacing, this->pev->angles); - //this->pev->fixangle = TRUE; - - //ASSERT(this->pev->iuser4 == AVH_USER4_OVERWATCH); - //this->pev->iuser4 &= ~MASK_MARINE_OVERWATCH; - this->pev->fuser1 = -1; - this->pev->fuser2 = -1; -} - -void AvHPlayer::TurnOnOverwatch() -{ - this->mInOverwatch = true; - - // Remember facing when we entered overwatch - VectorCopy(this->pev->angles, this->mOverwatchFacing); - - // if so, set overwatch on, make sure to set the current weapon into overwatch - AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); - ASSERT(theWeapon); - theWeapon->SetOverwatchState(true); - - // Flip on overwatch, clear target, it will be acquired in think - this->mOverwatchTarget = -1; - this->pev->fuser1 = -1; - this->pev->fuser2 = -1; - //this->pev->iuser4 |= MASK_MARINE_OVERWATCH; -} - -void AvHPlayer::TurnOverwatchTowardsTarget(CBaseEntity* theTarget) -{ - // TODO: Take gun offset into account with vecMid? - Vector vecMid = pev->origin + pev->view_ofs; - Vector vecMidEnemy = theTarget->BodyTarget( vecMid ); - - // Right now just point at enemy - Vector vecDirToEnemy = vecMidEnemy - vecMid; - Vector vec = UTIL_VecToAngles(vecDirToEnemy); - - vec.x = -vec.x; - -// if (vec.y > 360) -// vec.y -= 360; -// -// if (vec.y < 0) -// vec.y += 360; - - VectorCopy(vec, this->pev->angles); - VectorCopy(vec, this->pev->v_angle); - this->pev->fixangle = TRUE; -} - -bool AvHPlayer::RunClientScript(const string& inScriptName) -{ - // Returns false if client scripts aren't enabled - bool theSuccess = false; - - this->mPendingClientScripts.push_back(inScriptName); - theSuccess = true; - - return theSuccess; -} - -void AvHPlayer::PrintWeaponListToClient(CBaseEntity *theAvHPlayer) { - char msg[1024]; - sprintf(msg, "Weapons for %s:\n", this->GetPlayerName()); - ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, msg); - - for(int i = 0; i < MAX_ITEM_TYPES; i++) - { - AvHBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); - while(theActiveWeapon) - { - theActiveWeapon->PrintWeaponToClient(theAvHPlayer); - - // Next weapon - theActiveWeapon = dynamic_cast(theActiveWeapon->m_pNext); - } - } -} -void AvHPlayer::UpdateInventoryEnabledState(int inNumActiveHives, bool inForceUpdate) -{ - // Have we not yet updated our weapons with this # of hives? - if((inNumActiveHives != this->mNumHives) || (inForceUpdate)) - { - for(int i = 0; i < MAX_ITEM_TYPES; i++) - { - AvHBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); - while(theActiveWeapon) - { - theActiveWeapon->UpdateInventoryEnabledState(inNumActiveHives); - - // Next weapon - theActiveWeapon = dynamic_cast(theActiveWeapon->m_pNext); - } - } - } - - // Save # of hives we've last updated with - this->mNumHives = inNumActiveHives; -} - -bool AvHPlayer::GetIsBeingDigested() const -{ - bool theIsBeingDigested = false; - - if(GetHasUpgrade(this->pev->iuser4, MASK_DIGESTING)) - { - if(this->pev->effects & EF_NODRAW) - { - theIsBeingDigested = true; - } - } - - return theIsBeingDigested; -} - -bool AvHPlayer::GetIsDigesting() const -{ - bool theIsDigesting = false; - - if(GetHasUpgrade(this->pev->iuser4, MASK_DIGESTING)) - { - if(! (this->pev->effects & EF_NODRAW)) - { - theIsDigesting = true; - } - } - - return theIsDigesting; -} - -void AvHPlayer::UpdateAmbientSounds() -{ - AvHClassType theClassType = this->GetClassType(); - if(theClassType == AVH_CLASS_TYPE_MARINE) - { - } - else if(theClassType == AVH_CLASS_TYPE_ALIEN) - { - // Get role - AvHUser3 theUser3 = this->GetUser3(); - int theAlienLevel = theUser3 - AVH_USER3_COMMANDER_PLAYER; - int theVelocity = this->pev->velocity.Length(); - bool theIsMoving = theVelocity > 100; - int theSilenceLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_6); - -// if(RANDOM_LONG(0, 40) == 0) -// { -// char theVelocityMsg[128]; -// sprintf(theVelocityMsg, "alien moving at velocity: %d", theVelocity); -// ClientPrint(this->pev, HUD_PRINTTALK, theVelocityMsg); -// } - - // if moving, check chance for playing moving sound - if(!this->GetIsCloaked() && !this->mIsScreaming /*&& !this->GetIsBlinking()*/) - { - float theSilenceVolumeFactor = this->GetAlienAdjustedEventVolume(); - - if(theIsMoving) - { - int theBaseSpeed, theMaxSpeed; - this->GetSpeeds(theBaseSpeed, theMaxSpeed); - - float theAlienSoundFreq = 0.003f; - float theChanceOfPlayingSound = theAlienSoundFreq*(theVelocity/((float)theMaxSpeed)); - if(RANDOM_FLOAT(0, 1) < theChanceOfPlayingSound) - { - float theVolume = RANDOM_FLOAT(.5, 1.0)*theSilenceVolumeFactor; - if(theVolume > 0.01f) - { - this->PlayRandomRoleSound(kPlayerLevelMoveSoundList, CHAN_VOICE, theVolume); - } - } - } - else - { - if(GetHasUpgrade(this->pev->iuser4, MASK_BUFFED)) - { - float theSilenceVolumeFactor = this->GetAlienAdjustedEventVolume(); - - // If player is part of primal scream, scream defiance! - if(RANDOM_FLOAT(0, 1) < .02f && theSilenceVolumeFactor > 0.0) - { - EMIT_SOUND(this->edict(), CHAN_VOICE, kPrimalScreamResponseSound, theSilenceVolumeFactor, ATTN_NORM); - } - } - else - { - bool theIsGestating = (this->GetUser3() == AVH_USER3_ALIEN_EMBRYO); - - // if idle, check chance for playing idle sound - float theBaseChance = 0.0005f; - if(theIsGestating) - { - theBaseChance *= 10; - } - - if(RANDOM_FLOAT(0, 1) < theBaseChance) - { - float theVolume = RANDOM_FLOAT(.2, .4)*theSilenceVolumeFactor; - if(theVolume > 0.01f) - { - if(theIsGestating) - { - EMIT_SOUND(this->edict(), CHAN_AUTO, kEggIdleSound, theVolume, ATTN_NORM); - } - else - { - this->PlayRandomRoleSound(kPlayerLevelIdleSoundList, CHAN_VOICE, theVolume); - } - } - } - } - } - } - } - - if(this->mDesiredRoomType != this->mClientDesiredRoomType) - { - MESSAGE_BEGIN( MSG_ONE, SVC_ROOMTYPE, NULL, this->pev); - WRITE_SHORT( (short)this->mDesiredRoomType); - MESSAGE_END(); - - this->mClientDesiredRoomType = this->mDesiredRoomType; - } -} - -void AvHPlayer::UpdateAlienUI() -{ - AvHTeam* theTeamPointer = this->GetTeamPointer(); - bool theIsMarine = false; - bool theIsAlien = false; - - if(this->GetIsAlien()) - { - theIsAlien = true; - } - else if(this->GetIsMarine()) - { - theIsMarine = true; - } - - // Update when going back to ready room, so check if not-marine instead of is-alien - if(!theIsMarine) - { - bool theCanRespawn = GetGameRules()->FPlayerCanRespawn(this); - - AvHTeam* theTeamPointer = this->GetTeamPointer(); - if(theTeamPointer) - { - AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); - - int thePreSize = theUpgrades.size(); - - // Trim max upgrades in each category - AvHRemoveIrrelevantUpgrades(theUpgrades); - - if(theUpgrades != this->mClientUpgrades) - { - NetMsg_AlienInfo_Upgrades( this->pev, theUpgrades ); - this->mClientUpgrades = theUpgrades; - } - - HiveInfoListType theTeamHiveInfo = theTeamPointer->GetHiveInfoList(); - if(this->mClientHiveInfo != theTeamHiveInfo) - { - NetMsg_AlienInfo_Hives( this->pev, theTeamHiveInfo, this->mClientHiveInfo ); - this->mClientHiveInfo = theTeamHiveInfo; - } - } - } -} - -// TODO: Send only changed blips, send only the changes for each blip. -void AvHPlayer::UpdateBlips() -{ - if(this->mEnemyBlips != this->mClientEnemyBlips) - { - NetMsg_BlipList( this->pev, false, this->mEnemyBlips ); - this->mClientEnemyBlips = this->mEnemyBlips; - } - - if(this->mFriendlyBlips != this->mClientFriendlyBlips) - { - NetMsg_BlipList( this->pev, true, this->mFriendlyBlips ); - this->mClientFriendlyBlips = this->mFriendlyBlips; - } -} - -void AvHPlayer::UpdateClientData( void ) -{ - if(GET_RUN_CODE(128)) - { - //UTIL_LogPrintf("UpdateClientData starting.\n"); - CBasePlayer::UpdateClientData(); - - // Update one-shot stuff - this->UpdateFirst(); - this->UpdateParticleTemplates(); - this->UpdateInfoLocations(); - this->UpdateOrders(); - this->UpdateVUser4(); - this->UpdateProgressBar(); - this->UpdateSetSelect(); - this->UpdateTechNodes(); - this->UpdateBalanceVariables(); - this->UpdateSoundNames(); - this->UpdateTopDownMode(); - this->UpdateEntityHierarchy(); - this->UpdateExperienceLevelsSpent(); - this->UpdateSpawnScreenFade(); - this->UpdateEffectiveClassAndTeam(); - //this->UpdateArmor(); - //this->UpdateOverwatch(); - this->UpdatePendingClientScripts(); - this->UpdateGamma(); - this->UpdateBlips(); - this->UpdateAlienUI(); - this->UpdateFog(); - //this->UpdateDebugCSP(); - } - //UTIL_LogPrintf("UpdateClientData done.\n"); -} - -void AvHPlayer::UpdateEffectiveClassAndTeam() -{ - // Don't send too many messages when these get updated really quickly. Too many messages are being sent on a game reset, and it's not needed. We only need the most recent message. - const float kClassAndTeamUpdateRate = .4f; - if((this->mTimeOfLastClassAndTeamUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastClassAndTeamUpdate + kClassAndTeamUpdateRate))) - { - if(this->mEffectivePlayerClassChanged) - { - int theAuthMask = this->GetAuthenticationMask(); - int theTotalScore = this->mScore + this->pev->frags /*- this->m_iDeaths*/; - if(GetGameRules()->GetIsCombatMode()) - { - int theCurrentLevel = AvHPlayerUpgrade::GetPlayerLevel(this->GetExperience()); - theTotalScore += max((theCurrentLevel - 1), 0); - } - - ScoreInfo info; - info.player_index = ENTINDEX(this->edict()); - info.score = theTotalScore; - info.frags = this->pev->frags; - info.deaths = this->m_iDeaths; - info.player_class = this->GetEffectivePlayerClass(); - info.auth = this->GetAuthenticationMask(); - info.team = GetGameRules()->GetTeamIndex(this->TeamID()); - NetMsg_ScoreInfo( info ); - this->mEffectivePlayerClassChanged = false; - } - - if(this->mNeedsTeamUpdate) - { - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( i ); - if ( plr && GetGameRules()->IsValidTeam( plr->TeamID() ) ) - { - NetMsg_TeamInfo( this->pev, plr->entindex(), plr->TeamID() ); - } - } - this->mNeedsTeamUpdate = false; - } - - if(this->mSendTeamUpdate) - { - // notify everyone's HUD of the team change - NetMsg_TeamInfo( this->entindex(), this->TeamID() ); - this->mSendTeamUpdate = false; - } - - this->mTimeOfLastClassAndTeamUpdate = gpGlobals->time; - } -} - -void AvHPlayer::UpdateFirst() -{ - if(this->mFirstUpdate) - { - // Tell this player to reset - int theState = (this->mNewMap ? kGameStatusResetNewMap : kGameStatusReset); - NetMsg_GameStatus_State( this->pev, theState, GetGameRules()->GetMapMode() ); - - if(this->mNewMap) - { - NetMsg_SetSoundNames( this->pev, true, string() ); - this->mClientSoundNames.clear(); - - // Send down map extents so players can start computing it - // Cache this so it isn't computed every round, only the when a player connects or a new map starts? - const char* theCStrLevelName = STRING(gpGlobals->mapname); - const AvHMapExtents& theMapExtents = GetGameRules()->GetMapExtents(); - ASSERT(theCStrLevelName); - ASSERT(!FStrEq(theCStrLevelName, "")); - - float mins[3] = { theMapExtents.GetMinMapX(), theMapExtents.GetMinMapY(), theMapExtents.GetMinViewHeight() }; - float maxs[3] = { theMapExtents.GetMaxMapX(), theMapExtents.GetMaxMapY(), theMapExtents.GetMaxViewHeight() }; - - NetMsg_SetupMap_Extents( this->pev, string( theCStrLevelName ), mins, maxs, theMapExtents.GetDrawMapBG() ); - } - - this->mFirstUpdate = false; - this->mNewMap = false; - } -} - -void AvHPlayer::UpdateFog() -{ - if(this->mClientCurrentFogEntity != this->mCurrentFogEntity) - { - bool theFogEnabled = this->mCurrentFogEntity > -1; - int theR, theG, theB; - float theStart, theEnd; - - if(theFogEnabled) - { - AvHFog* theFogEntity = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCurrentFogEntity))); - ASSERT(theFogEntity); - - theFogEntity->GetFogColor(theR, theG, theB); - theStart = theFogEntity->GetFogStart(); - theEnd = theFogEntity->GetFogEnd(); - } - - NetMsg_Fog( this->pev, theFogEnabled, theR, theG, theB, theStart, theEnd ); - this->mClientCurrentFogEntity = this->mCurrentFogEntity; - } -} - -void AvHPlayer::UpdateGamma() -{ - float theMapGamma = GetGameRules()->GetMapGamma(); - if(this->mClientGamma != theMapGamma) - { - if(!GetGameRules()->GetIsTesting()) - { - NetMsg_SetGammaRamp( this->pev, theMapGamma ); - this->mClientGamma = theMapGamma; - } - } -} - -void AvHPlayer::UpdateOrders() -{ - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - OrderListType theTeamOrders; - theTeam->GetOrders(theTeamOrders); - - for(OrderListType::iterator theIter = theTeamOrders.begin(); theIter != theTeamOrders.end(); theIter++) - { - bool theClientHasOrder = false; - AvHOrder theClientOrder; - - // Does client already have this order? - for(OrderListType::iterator theClientIter = this->mClientOrders.begin(); theClientIter != this->mClientOrders.end(); theClientIter++) - { - if(theIter->GetOrderID() == theClientIter->GetOrderID()) - { - theClientHasOrder = true; - theClientOrder = *theClientIter; - break; - } - } - - if(!theClientHasOrder || theClientOrder != *theIter) - { - NetMsg_SetOrder( this->pev, *theIter ); - } - } - - this->mClientOrders = theTeamOrders; - } -} - -void AvHPlayer::UpdateParticleTemplates() -{ - const float kParticleTemplateRate = 1.0f; - if(gParticleTemplateList.GetCreatedTemplates()) - { - // Make sure client clears out all particle systems first - int theNumberTemplates = gParticleTemplateList.GetNumberTemplates(); - if(theNumberTemplates > this->mNumParticleTemplatesSent) - { - if((this->mTimeOfLastParticleTemplateSending == -1) || (gpGlobals->time > this->mTimeOfLastParticleTemplateSending + kParticleTemplateRate)) - { - AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(this->mNumParticleTemplatesSent); - ASSERT(theTemplate); - NetMsg_SetParticleTemplate( this->pev, *theTemplate ); - this->mNumParticleTemplatesSent++; - this->mTimeOfLastParticleTemplateSending = gpGlobals->time; - } - } - } -} - -void AvHPlayer::UpdateInfoLocations() -{ - // Get map location list - const AvHBaseInfoLocationListType& theInfoLocations = GetGameRules()->GetInfoLocations(); - - // Compare with ours, send one down each tick (assumes that previous ones sent don't change) - int theNumClientInfoLocations = this->mClientInfoLocations.size(); - if((signed)theInfoLocations.size() > theNumClientInfoLocations) - { - // Only send one at a time - AvHBaseInfoLocation theInfoLocation = theInfoLocations[theNumClientInfoLocations]; - - vec3_t theMaxExtents = theInfoLocation.GetMaxExtent(); - vec3_t theMinExtents = theInfoLocation.GetMinExtent(); - float mins[3] = { theMinExtents.x, theMinExtents.y, theMinExtents.z }; - float maxs[3] = { theMaxExtents.x, theMaxExtents.y, theMinExtents.z }; - - NetMsg_SetupMap_Location( this->pev, theInfoLocation.GetLocationName(), mins, maxs ); - this->mClientInfoLocations.push_back(theInfoLocation); - } - -} - -void AvHPlayer::UpdatePendingClientScripts() -{ - if(this->mPendingClientScripts.size() > 0) - { - NetMsg_ClientScripts( this->pev, this->mPendingClientScripts ); - this->mPendingClientScripts.clear(); - } -} - -void AvHPlayer::UpdateProgressBar() -{ - // TODO: If this is the commander, send him all the progress bars of all his teammates so he can see them! - - // Assumes that progress is normalized and stored in one of the fuser variables of the entity index sent down - if(this->mClientProgressBarEntityIndex != this->mProgressBarEntityIndex) - { - NetMsg_ProgressBar( this->pev, this->mProgressBarEntityIndex, this->mProgressBarParam ); - this->mClientProgressBarEntityIndex = this->mProgressBarEntityIndex; - } -} - -void AvHPlayer::UpdateVUser4() -{ - // Update client with resources (as int) - int theResources = (short)(this->GetResources(true)); - - if(CVAR_GET_FLOAT(kvTesting)) - { - theResources = g_engfuncs.pfnNumberOfEntities(); - } - - if(this->pev) - { - if(GetGameRules()->GetIsCombatMode()) - { - this->pev->vuser4.z = this->GetExperience()*kNumericNetworkConstant; - } - else - { - this->pev->vuser4.z = theResources*kNumericNetworkConstant; - } - } -} - -void AvHPlayer::UpdateSetSelect() -{ - if(this->GetIsInTopDownMode()) - { - if((this->mSelected != this->mClientSelected) || (this->mTrackingEntity != this->mClientTrackingEntity)) - { - Selection selection; - selection.group_number = 0; - selection.selected_entities = this->mSelected; - selection.tracking_entity = max( this->mTrackingEntity, 0 ); - - NetMsg_SetSelect( this->pev, selection ); - - // Synch up - this->mClientSelected = this->mSelected; - this->mClientTrackingEntity = this->mTrackingEntity; - } - - AvHTeam* theTeam = this->GetTeamPointer(); - ASSERT(theTeam); - - for(int j=0; j < kNumHotkeyGroups; j++) - { - EntityListType theGroup = theTeam->GetGroup(j); - EntityListType& theClientGroup = this->mClientGroups[j]; - AvHUser3 theGroupType = theTeam->GetGroupType(j); - AvHAlertType theGroupAlert = ALERT_NONE; - AvHAlertType& theClientGroupAlert = this->mClientGroupAlerts[j]; - - // Is group under attack or no longer under attack? - for(EntityListType::iterator theIter = theGroup.begin(); theIter != theGroup.end(); theIter++) - { - if(GetGameRules()->GetIsEntityUnderAttack(*theIter)) - { - theGroupAlert = ALERT_UNDER_ATTACK; - } - } - - if((theClientGroup != theGroup) || (theClientGroupAlert != theGroupAlert)) - { - Selection selection; - selection.group_number = j+1; - selection.selected_entities = theGroup; - selection.group_type = theGroupType; - selection.group_alert = theGroupAlert; - - NetMsg_SetSelect( this->pev, selection ); - - theClientGroup = theGroup; - theClientGroupAlert = theGroupAlert; - } - } - - // See if "selectall" hotgroup has changed and send it if needed - EntityListType theSelectAllGroup = theTeam->GetSelectAllGroup(); - if(theSelectAllGroup != this->mClientSelectAllGroup) - { - Selection selection; - selection.group_number = kSelectAllHotGroup; - selection.selected_entities = theSelectAllGroup; - - NetMsg_SetSelect( this->pev, selection ); - - this->mClientSelectAllGroup = theSelectAllGroup; - } - - // Check idle soldiers, ammo requests and health requests - AvHMessageID theRequestList[kNumRequestTypes] = {COMMANDER_NEXTIDLE, COMMANDER_NEXTAMMO, COMMANDER_NEXTHEALTH}; - for(int i = 0; i < kNumRequestTypes; i++) - { - AvHMessageID theCurrentRequestType = theRequestList[i]; - int theNumClientRequests = this->mClientRequests[i]; - int theNumTeamRequests = theTeam->GetAlerts(theCurrentRequestType).size(); - if(theNumClientRequests != theNumTeamRequests) - { - NetMsg_SetRequest( this->pev, theCurrentRequestType, theNumTeamRequests ); - this->mClientRequests[i] = theNumTeamRequests; - } - } - } -} - -void AvHPlayer::UpdateSoundNames() -{ - if(this->pev != NULL ) // Not fully connected yet - { - // Send list of special sounds - const StringList& theSoundNameList = AvHMP3Audio::GetSoundNameList(); - int theNumberOfSounds = theSoundNameList.size(); - int theNumberOfSoundsOnClient = this->mClientSoundNames.size(); - - ASSERT(theNumberOfSoundsOnClient <= theNumberOfSounds); - - // Only send one new sound name every tick, to avoid sending too much data and overflowing too quickly - if(theNumberOfSounds > theNumberOfSoundsOnClient) - { - const char* theSoundNameToSend = theSoundNameList[theNumberOfSoundsOnClient].c_str(); - if(GetGameRules()->GetIsTesting()) - { - this->SendMessage(theSoundNameToSend); - } - ASSERT( strlen(theSoundNameToSend) < 50); - NetMsg_SetSoundNames( this->pev, false, theSoundNameToSend); - - this->mClientSoundNames.push_back(theSoundNameToSend); - } - } -} - -//TODO: (KGP) there are a lot of expensive per-frame operations here that can be eliminated through careful refactoring. -// 1) make AvHTechTree an abstract interface -// 2) create base case using current AvHTechTree code -// 3) create filter around AvHTechTree that uses override of IsResearchable by MessageID and returns AvHTechNode objects -// that reflect the filter. -// 4) create AvHTechChangeListener class and use it as basis for decision to send tech nodes -// 5) create NetMsg_SetTechNodeDelta function that bundles state changes for multiple nodes into a single call -// 6) always use a personal copy of AvHTechNodes interface for each player to eliminate the per-frame copy of the team nodes -// 7) use filter class for NS mode aliens and update state of the filter when alien lifeform changes instead of using per-frame update -// Combined, these changes should reduce CPU overhead for tech node update by at least 90%. -void AvHPlayer::UpdateTechNodes() -{ - bool theIsCombatMode = GetGameRules()->GetIsCombatMode(); - bool theIsNSMode = GetGameRules()->GetIsNSMode(); - if((this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) || theIsCombatMode || this->GetIsAlien()) - { - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam) - { - // Propagate and use local tech nodes in combat mode, else use team nodes in NS mode - AvHTechTree& theTechNodes = theIsCombatMode ? this->mCombatNodes : theTeam->GetTechNodes(); - - // Now customize nodes for aliens in NS - if(theIsNSMode && this->GetIsAlien()) - { - // Set current lifeform to be unavailable - AvHMessageID theLifeform = MESSAGE_NULL; - switch(this->GetUser3()) - { - case AVH_USER3_ALIEN_PLAYER1: - theLifeform = ALIEN_LIFEFORM_ONE; - break; - case AVH_USER3_ALIEN_PLAYER2: - theLifeform = ALIEN_LIFEFORM_TWO; - break; - case AVH_USER3_ALIEN_PLAYER3: - theLifeform = ALIEN_LIFEFORM_THREE; - break; - case AVH_USER3_ALIEN_PLAYER4: - theLifeform = ALIEN_LIFEFORM_FOUR; - break; - case AVH_USER3_ALIEN_PLAYER5: - theLifeform = ALIEN_LIFEFORM_FIVE; - break; - } - // tankefugl 0001075 : Reset techs to researchable before flagging them otherwise - theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_ONE, true); - theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_TWO, true); - theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_THREE, true); - theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_FOUR, true); - theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_FIVE, true); - theTechNodes.SetIsResearchable(theLifeform, false); - - // tankefugl 0001075 : Reset techs to researchable before flagging them otherwise - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_ONE, true); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TWO, true); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_THREE, true); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_SEVEN, true); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_EIGHT, true); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_NINE, true); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TEN, true); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_ELEVEN, true); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TWELVE, true); - - // If not Gorge, set buildables to be unavailable - if(theLifeform != ALIEN_LIFEFORM_TWO) - { - theTechNodes.SetIsResearchable(ALIEN_BUILD_HIVE, false); - theTechNodes.SetIsResearchable(ALIEN_BUILD_RESOURCES, false); - theTechNodes.SetIsResearchable(ALIEN_BUILD_OFFENSE_CHAMBER, false); - theTechNodes.SetIsResearchable(ALIEN_BUILD_DEFENSE_CHAMBER, false); - theTechNodes.SetIsResearchable(ALIEN_BUILD_MOVEMENT_CHAMBER, false); - theTechNodes.SetIsResearchable(ALIEN_BUILD_SENSORY_CHAMBER, false); - } - else - { - // tankefugl 0001075 : Reset techs to researchable before flagging them otherwise - theTechNodes.SetIsResearchable(ALIEN_BUILD_HIVE, true); - theTechNodes.SetIsResearchable(ALIEN_BUILD_RESOURCES, true); - theTechNodes.SetIsResearchable(ALIEN_BUILD_OFFENSE_CHAMBER, true); - theTechNodes.SetIsResearchable(ALIEN_BUILD_DEFENSE_CHAMBER, true); - theTechNodes.SetIsResearchable(ALIEN_BUILD_MOVEMENT_CHAMBER, true); - theTechNodes.SetIsResearchable(ALIEN_BUILD_SENSORY_CHAMBER, true); - - // If we have the max hives, disable hives - - bool theHasFreeUpgradeCategory = false; - MessageIDListType theUnsupportedUpgrades; - theUnsupportedUpgrades.push_back(ALIEN_BUILD_DEFENSE_CHAMBER); - theUnsupportedUpgrades.push_back(ALIEN_BUILD_MOVEMENT_CHAMBER); - theUnsupportedUpgrades.push_back(ALIEN_BUILD_SENSORY_CHAMBER); - - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - if(theEntity && theEntity->GetIsActive() && (theEntity->pev->team == this->pev->team)) - { - AvHMessageID theTechnology = theEntity->GetTechnology(); - if(theTechnology == MESSAGE_NULL) - { - theHasFreeUpgradeCategory = true; - break; - } - else - { - // It's supported, so remove from unsupported list - theUnsupportedUpgrades.erase(std::find(theUnsupportedUpgrades.begin(), theUnsupportedUpgrades.end(), theTechnology)); - } - } - END_FOR_ALL_ENTITIES(kesTeamHive); - - // If we don't have a free upgrade category - if(!theHasFreeUpgradeCategory) - { - // Remove every unsupported structure - for(MessageIDListType::iterator theIter = theUnsupportedUpgrades.begin(); theIter != theUnsupportedUpgrades.end(); theIter++) - { - theTechNodes.SetIsResearchable(*theIter, false); - } - } - } - - // If there are no free upgrades available, disable upgrades - AvHTeam* theTeamPointer = this->GetTeamPointer(); - if(theTeamPointer) - { - AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); - if(!AvHGetHasFreeUpgradeCategory(ALIEN_UPGRADE_CATEGORY_DEFENSE, theUpgrades, this->pev->iuser4)) - { - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_ONE, false); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TWO, false); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_THREE, false); - } - if(!AvHGetHasFreeUpgradeCategory(ALIEN_UPGRADE_CATEGORY_MOVEMENT, theUpgrades, this->pev->iuser4)) - { - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_SEVEN, false); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_EIGHT, false); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_NINE, false); - } - if(!AvHGetHasFreeUpgradeCategory(ALIEN_UPGRADE_CATEGORY_SENSORY, theUpgrades, this->pev->iuser4)) - { - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TEN, false); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_ELEVEN, false); - theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TWELVE, false); - } - } - } - - theTechNodes.GetDelta( this->mClientTechNodes,this->mClientTechDelta ); - if( !mClientTechDelta.empty() ) - { - const AvHTechNode* Node = NULL; - MessageIDListType::iterator current, end = mClientTechDelta.end(); - for( current = mClientTechDelta.begin(); current != end; ++current ) - { - Node = theTechNodes.GetNode(*current); - if( Node != NULL ) - { - NetMsg_SetTechNode( this->pev, Node ); - this->mClientTechNodes.InsertNode( Node ); - } - else - { - //TODO: send signal to remove the tech node from the client here... - this->mClientTechNodes.RemoveNode(*current); - } - } - mClientTechDelta.clear(); - mClientTechNodes = theTechNodes; - } - - // Propagate any tech slots that have changed - const AvHTechSlotListType& theTeamTechSlotList = theTeam->GetTechSlotManager().GetTechSlotList(); - if(this->mClientTechSlotList != theTeamTechSlotList) - { - // Send any nodes that have changed - int theCurrentSlot = 0; - for(AvHTechSlotListType::const_iterator theIter = theTeamTechSlotList.begin(); theIter != theTeamTechSlotList.end(); theIter++, theCurrentSlot++) - { - bool theHasClientTechSlot = false; - AvHTechSlots theClientTechSlot; - if((signed)this->mClientTechSlotList.size() > theCurrentSlot) - { - theClientTechSlot = this->mClientTechSlotList[theCurrentSlot]; - theHasClientTechSlot = true; - } - - AvHTechSlots theServerTechSlot = theTeamTechSlotList[theCurrentSlot]; - - if(!theHasClientTechSlot || (theClientTechSlot != theServerTechSlot)) - { - NetMsg_SetTechSlots( this->pev, theServerTechSlot ); - } - } - - this->mClientTechSlotList = theTeamTechSlotList; - } - } - } -} - -void AvHPlayer::UpdateTopDownMode() -{ - if((this->mClientInTopDownMode != this->mInTopDownMode) || (this->mSpecialPASOrigin != this->mClientSpecialPASOrigin)) - { - vec3_t& angles = this->mInTopDownMode ? this->mSpecialPASOrigin : this->mAnglesBeforeTopDown; - float position[3] = { angles.x, angles.y, angles.z }; - NetMsg_SetTopDown_Position( this->pev, this->mInTopDownMode, position ); - - this->mClientInTopDownMode = this->mInTopDownMode; - this->mClientSpecialPASOrigin = this->mSpecialPASOrigin; - } - - // Send menu tech slots to commander - AvHTeam* theTeam = this->GetTeamPointer(); - if(theTeam && this->mInTopDownMode) - { - int theMenuTechSlots = theTeam->GetMenuTechSlots(); - if(theMenuTechSlots != this->mClientMenuTechSlots) - { - NetMsg_SetTopDown_TechSlots( this->pev, theMenuTechSlots ); - this->mClientMenuTechSlots = theMenuTechSlots; - } - } -} - -void AvHPlayer::UpdateExperienceLevelsSpent() -{ - // If our spent level is different then our client's - if(this->mClientExperienceLevelsSpent != this->mExperienceLevelsSpent) - { - NetMsg_GameStatus_UnspentLevels( this->pev, kGameStatusUnspentLevels, GetGameRules()->GetMapMode(), this->mExperienceLevelsSpent ); - this->mClientExperienceLevelsSpent = this->mExperienceLevelsSpent; - } -} - -void AvHPlayer::UpdateEntityHierarchy() -{ - - AvHPlayer* player = this; - - // If we're spectating, then use the minimap data for the player we're spectating. - - AvHPlayer* spectatingPlayer = dynamic_cast(GetSpectatingEntity()); - - if (spectatingPlayer) - { - player = spectatingPlayer; - } - - AvHTeam* theTeam = player->GetTeamPointer(); - - if(theTeam && GetGameRules()->GetGameStarted()) - { - if (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE || - theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - // Pass in previous version so it can optimize and only send diff - AvHEntityHierarchy& theEntityList = GetGameRules()->GetEntityHierarchy(player->GetTeam()); - - if(theEntityList.SendToNetworkStream(this->mClientEntityHierarchy, this->pev)) - { - this->mClientEntityHierarchy = theEntityList; - } - } - } - -} - -void AvHPlayer::UpdateSpawnScreenFade() -{ - if(this->mSendSpawnScreenFade) - { - Vector theFadeColor; - theFadeColor.x = 0; - theFadeColor.y = 0; - theFadeColor.z = 0; - UTIL_ScreenFade(this, theFadeColor, kSpawnInFadeTime, 0.0f, 255, FFADE_IN); - this->mSendSpawnScreenFade = false; - } -} - - -void AvHPlayer::UpdateDebugCSP() -{ - bool theCSPChanged = memcmp(&this->mClientDebugCSPInfo, &this->mDebugCSPInfo, sizeof(weapon_data_t)); - if(theCSPChanged || (this->mClientNextAttack != this->m_flNextAttack)) - { - NetMsg_DebugCSP( this->pev, this->mDebugCSPInfo, this->m_flNextAttack ); - memcpy(&this->mClientDebugCSPInfo, &this->mDebugCSPInfo, sizeof(weapon_data_t)); - this->mClientNextAttack = this->m_flNextAttack; - } -} - -void AvHPlayer::UpdateOverwatch() -{ - // Update overwatch indicator - if(this->mClientInOverwatch != this->mInOverwatch) - { - // We are entering overwatch - if(this->mInOverwatch) - { - PLAYBACK_EVENT_FULL(0, this->edict(), gStartOverwatchEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - } - // We are leaving overwatch - else - { - PLAYBACK_EVENT_FULL(0, this->edict(), gEndOverwatchEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - } - this->mClientInOverwatch = this->mInOverwatch; - } -} - -bool AvHPlayer::GetCanUseWeapon() const -{ - return GetIsAbleToAct() && pev->viewmodel; -} - -// tankefugl: 0000953 -// allows a player to join team only once each inCoolDownTime seconds -bool AvHPlayer::JoinTeamCooledDown(float inCoolDownTime) { -// UTIL_ClientPrintAll(HUD_PRINTTALK, UTIL_VarArgs("Enter: JoinTeamCooledDown(%f), gpGlobals->time = %f, this->mTimeLastJoinTeam = %f", inCoolDownTime, gpGlobals->time, this->mTimeLastJoinTeam)); - if ((this->mTimeLastJoinTeam == -1) || (gpGlobals->time > this->mTimeLastJoinTeam + inCoolDownTime)) - { - this->mTimeLastJoinTeam = gpGlobals->time; - return true; - } - else - return false; -} -// :tankefugl - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Nexus interface -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -//TODO: flesh this out with admin privileges, etc. once the UPP authorization interface has been expanded -bool AvHPlayer::GetIsAuthorized(AvHAuthAction inAction, int inParameter) const -{ - switch( inAction ) - { - case AUTH_ACTION_JOIN_TEAM: - { - AvHTeamNumber theTeam = (AvHTeamNumber)inParameter; - switch( theTeam ) - { - case TEAM_IND: // ready room & spectator - game allows in all cases - case TEAM_SPECT: - return true; - default: - // check it's an active team - if( theTeam == GetGameRules()->GetTeamA()->GetTeamNumber() || theTeam == GetGameRules()->GetTeamB()->GetTeamNumber() ) - { - // tankefugl: 0001042 -- allow switching of teams -- placeholder before Nexus - // if( GetGameRules()->GetCheatsEnabled() ) { return true; } // cheaters can switch - // if( !GetGameRules()->GetGameStarted() ) { return true; } // can switch teams before start - // if( this->GetHasBeenSpectator() ) { return false; } // spectators have seen everybody - // for(int counter = TEAM_ACTIVE_BEGIN; counter < TEAM_ACTIVE_END; counter++) - // { - // if( theTeam != counter && this->GetHasSeenTeam( (AvHTeamNumber)counter ) ) - // { return false; } // we've seen another active team - // } - return true; // haven't seen another team, authorized to join - } - return false; // unknown/inactive team - never grant an unknown permission! - } - } - case AUTH_ACTION_ADJUST_BALANCE: - { -#ifndef BALANCE_ENABLED - return false; -#else - return this->GetIsMember(PLAYERAUTH_DEVELOPER); -#endif - } - default: - return false; // never grant an unknown permission! - } -} - -#ifdef USE_OLDAUTH -bool AvHPlayer::GetIsMember(const AvHPlayerAuthentication inAuthGroup) -{ - return (this->GetAuthenticationMask() & inAuthGroup); -} -#else -bool AvHPlayer::GetIsMember(const string& inAuthGroup) const -{ - return false; -} -#endif - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// BalanceChangeListener implementation and balance network code -// -// Balance is checked for changes at a set rate determined by the -// BALANCE_UPDATE_MAX_FREQUENCY const below. This prevents the -// balance system logic from bogging down the server if there are 32 -// players and the entire system is reloaded. A maximum of one -// message will be sent with each check. Note that this system is -// much, much more efficient than the old check of the entire balance -// state every frame! -// -// Due to the setup of the balance system, the BalanceChanageListener -// functions will never be called for non-playtest compiles, so -// there is no need to gaurd with a playtest build #define. The -// call to UpdateBalanceVariables may benefit from an being #define'd -// out, but that function has very low overhead anyway. -// -// TODO: move this block (variables and logic) into a discrete class -// and associate that class with the player using an auto_ptr instead -// of embedding the information into the player class -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -const float BALANCE_UPDATE_MAX_FREQUENCY = 0.05; //maximum frequency at which checks occur - -bool AvHPlayer::shouldNotify(const string& name, const BalanceValueType type) const -{ - return true; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHPlayer::balanceCleared(void) const -{ - this->mBalanceRemovalList.clear(); - this->mBalanceMapInts.clear(); - this->mBalanceMapFloats.clear(); - this->mBalanceMapStrings.clear(); - NetMsg_BalanceVarClear( this->pev ); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// INTEGER -void AvHPlayer::balanceValueInserted(const string& name, const int value) const -{ - this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion - this->mBalanceMapInts.insert(BalanceIntCollection::value_type(name,value)); -} - -void AvHPlayer::balanceValueChanged(const string& name, const int old_value, const int new_value) const -{ - this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion - this->mBalanceMapInts.insert(BalanceIntCollection::value_type(name,new_value)); -} - -void AvHPlayer::balanceValueRemoved(const string& name, const int old_value) const -{ - this->mBalanceMapInts.erase(name); //in case we didn't send it yet - this->mBalanceRemovalList.insert(name); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// FLOAT -void AvHPlayer::balanceValueInserted(const string& name, const float value) const -{ - this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion - this->mBalanceMapFloats.insert(BalanceFloatCollection::value_type(name,value)); -} - -void AvHPlayer::balanceValueChanged(const string& name, const float old_value, const float new_value) const -{ - this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion - this->mBalanceMapFloats.insert(BalanceFloatCollection::value_type(name,new_value)); -} - -void AvHPlayer::balanceValueRemoved(const string& name, const float old_value) const -{ - this->mBalanceMapFloats.erase(name); //in case we didn't send it yet - this->mBalanceRemovalList.insert(name); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// STRING -void AvHPlayer::balanceValueInserted(const string& name, const string& value) const -{ - this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion - this->mBalanceMapStrings.insert(BalanceStringCollection::value_type(name,value)); -} - -void AvHPlayer::balanceValueChanged(const string& name, const string& old_value, const string& new_value) const -{ - this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion - this->mBalanceMapStrings.insert(BalanceStringCollection::value_type(name,new_value)); -} - -void AvHPlayer::balanceValueRemoved(const string& name, const string& old_value) const -{ - this->mBalanceMapStrings.erase(name); //in case we didn't send it yet - this->mBalanceRemovalList.insert(name); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHPlayer::InitBalanceVariables(void) -{ - //grab the entire current balance - BalanceValueContainer* container = BalanceValueContainerFactory::get(BalanceValueContainerFactory::getDefaultFilename()); - this->mBalanceMapStrings = *container->getStringMap(); - this->mBalanceMapInts = *container->getIntMap(); - this->mBalanceMapFloats = *container->getFloatMap(); - - //clear the client in preparation to send everything again - //pev will be null if this is called during construction - if( this->pev ) { NetMsg_BalanceVarClear( this->pev ); } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHPlayer::UpdateBalanceVariables(void) -{ - if(mNextBalanceVarUpdate < gpGlobals->time) - { - //only send it if it can be used... - //CONSIDER: second security setting for read-only transfer - if(this->GetIsAuthorized(AUTH_ACTION_ADJUST_BALANCE,0)) - { - //check number of changes we have to send - int total_changes = this->mBalanceRemovalList.size(); - total_changes += this->mBalanceMapInts.size(); - total_changes += this->mBalanceMapFloats.size(); - total_changes += this->mBalanceMapStrings.size(); - - //if we have multiple changes and need to tell client they are a set - if( total_changes > 1 && !this->mBalanceMessagePending ) //flag multiple changes incoming - { - NetMsg_BalanceVarChangesPending( this->pev, true ); - this->mBalanceMessagePending = true; - } - //else if we have no more changes -- check to see if we need to send end set signal - else if( total_changes == 0 ) - { - if( this->mBalanceMessagePending ) - { - NetMsg_BalanceVarChangesPending( this->pev, false ); - this->mBalanceMessagePending = false; - } - } - // we have at least one change to make, possibly in a set - else if(!this->mBalanceRemovalList.empty()) - { - set::iterator item = this->mBalanceRemovalList.begin(); - NetMsg_BalanceVarRemove( this->pev, *item ); - this->mBalanceRemovalList.erase(item); - } - else if(!this->mBalanceMapInts.empty()) - { - BalanceIntCollection::iterator item = this->mBalanceMapInts.begin(); - NetMsg_BalanceVarInsertInt( this->pev, item->first, item->second ); - this->mBalanceMapInts.erase(item); - } - else if(!this->mBalanceMapFloats.empty()) - { - BalanceFloatCollection::iterator item = this->mBalanceMapFloats.begin(); - NetMsg_BalanceVarInsertFloat( this->pev, item->first, item->second ); - this->mBalanceMapFloats.erase(item); - } - else if(!this->mBalanceMapStrings.empty()) - { - BalanceStringCollection::iterator item = this->mBalanceMapStrings.begin(); - NetMsg_BalanceVarInsertString( this->pev, item->first, item->second ); - this->mBalanceMapStrings.erase(item); - } - } - //update next check of balance message queue - mNextBalanceVarUpdate = gpGlobals->time + BALANCE_UPDATE_MAX_FREQUENCY; - } -} +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHPlayer.cpp $ +// $Date: 2002/11/22 21:18:24 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHPlayer.cpp,v $ +// Revision 1.86 2002/11/22 21:18:24 Flayra +// - Potentially fixed strange Onos collision crash +// - Don't allow player to join team after he's seen another team +// - "lastinv" support +// - Fixed perma-cloak bug after losing last sensory chamber whilst cloaked +// - Started fixing commander PAS problem +// - Fixed readyroom "ghost player" exploit when F4 during REIN +// - Draw damage in debug, never otherwise +// +// Revision 1.85 2002/11/15 04:42:50 Flayra +// - Regenerate now returns true if healing was successful +// - Logging changes and fixes +// +// Revision 1.84 2002/11/13 01:49:08 Flayra +// - Proper death message logging for Psychostats +// +// Revision 1.83 2002/11/12 22:39:25 Flayra +// - Logging changes for Psychostats compatibility +// +// Revision 1.82 2002/11/12 18:44:54 Flayra +// - Added mp_logdetail support for damage messages +// - Changed the alien ability anti-exploit code to try to co-exist with scripters +// +// Revision 1.81 2002/11/12 02:28:39 Flayra +// - Fixed problems with armor not being updated when armor upgrades completed +// - Aliens now keep same percentage of health and armor when morphing +// - Much better logging, up to standard +// - Don't add enemy buildings to hive sight unless parasited (solves blip spam) +// - Removed draw damage from public build +// - Changes to minimap to less overflows at end of big games +// +// Revision 1.80 2002/11/06 01:38:54 Flayra +// - Damage refactoring (TakeDamage assumes caller has already adjusted for friendly fire, etc.) +// - Regeneration update +// +// Revision 1.79 2002/11/05 06:17:26 Flayra +// - Balance changes +// +// Revision 1.78 2002/10/28 20:36:18 Flayra +// - Updated auth mask +// +// Revision 1.77 2002/10/25 21:48:01 Flayra +// - Added more auth masks +// - Moved mSkin to pev->skin so playerclass could hold what used to be mPlayMode could be used to quickly perform culling, to try to prevent overflowage at the end of large games +// +// Revision 1.76 2002/10/24 21:40:02 Flayra +// - Reworked jetpack effect +// - Authicons update +// - Set builder for alien buildings, so turret kills can credit builder +// - Alien easter eggs +// - Network optimizations after game reset on huge (20-32) player games +// - Allow server ops to disable auth icons +// - Alien energy tweaks +// - Show unbuilt hives in hive sight +// - Move alien energy updating fully into shared code +// - Tried to fix full health ring showing for dead selected players +// - Moved help text client-side +// - Cache info_locations and gamma until map change +// - Skin fixes +// +// Revision 1.75 2002/10/20 21:10:57 Flayra +// - Optimizations +// +// Revision 1.74 2002/10/20 16:36:37 Flayra +// - Code optimization! Took forever to find. +// +// Revision 1.73 2002/10/20 02:36:14 Flayra +// - Regular update +// +// Revision 1.72 2002/10/19 22:33:44 Flayra +// - Various server optimizations +// +// Revision 1.71 2002/10/18 22:21:54 Flayra +// - Limit alien buildings in sphere +// - Fix various spawning problems (morphing as level 5, redemption for level 5) +// - Max motd length fix +// +// Revision 1.70 2002/10/17 17:34:09 Flayra +// - Authmask update +// - Part 1 of persistent weapons fix (found with Grendel) +// +// Revision 1.69 2002/10/16 20:55:40 Flayra +// - Debug code for tracking down death animation problems +// - Updated auth masks +// +// Revision 1.68 2002/10/16 01:05:38 Flayra +// - Sent health as short for big aliens +// - Fixed sayings not triggering commander alerts +// - Now name changes are queued until the next match +// - Added authmask support +// - Egg idle sounds play more frequently, refactored too +// - Fixed preserving model in ready room after game end +// - Profiling of AddToFullPack +// - Fix for falling through lifts when morphing on them (untested) +// +// Revision 1.67 2002/10/04 18:04:07 Flayra +// - Fixed floating gestation sacs +// - Aliens now fall all the way to ground during countdown (instead of floating and shaking) +// +// Revision 1.66 2002/10/03 20:24:39 Flayra +// - Changes for "more resources required" +// +// Revision 1.65 2002/10/03 19:32:06 Flayra +// - Reworked orders completely +// - Send max resources to players +// - Heavy armor sped up slightly +// - Kill players who illegally try to use alien abilities +// - Moved energy to a new variable, send health for aliens +// - Only freeze players during countdown +// - Removed slowdown when taking damage +// - Send blips in two messages, one friendly and one enemy (old bad hack) +// +// Revision 1.64 2002/09/25 20:50:03 Flayra +// - Added 3 new sayings +// - Frame-rate independent updating +// - Don't allow player to kill self while commanding +// +// Revision 1.63 2002/09/23 22:27:52 Flayra +// - Added skin support +// - Added client connected/disconnected hooks for particle system propagation optimizations +// - Removed power armor, added heavy armor +// - Fixed death animations +// - Added hook to see if commander has given an order and to see if he's idle +// - Bound resources for aliens +// - Soldiers asking for ammo and health trigger commander alert +// - Added gestation anims +// - Slowed down Onos movement +// - When cheats are enabled, purchases are free +// +// Revision 1.62 2002/09/09 20:04:53 Flayra +// - Added commander voting +// - Commander score is now the average of the rest of his players (reverted back when he leaves CC) +// - Fixed bug where upgrades were getting removed and then add repeatedly +// - Added multiple skins for marines +// - Play sound when aliens lose an upgrade +// - Changed fov to 90 for all aliens for software compatibility +// - Added hiveinfo drawing +// - Tweaked marine and alien speeds (to compensate for loss of drastic alien fov) +// +// Revision 1.61 2002/08/31 18:01:02 Flayra +// - Work at VALVe +// +// Revision 1.60 2002/08/16 02:42:57 Flayra +// - New damage types +// - Much more efficient blip calculation (at least it should be, I haven't measured it so who really knows) +// - New way of representing ensnare state for shared code (disabling jumping, jetpacking) +// - Removed old overwatch code +// - Store health in fuser2 for drawing health for commander +// - Swap bile bomb and umbra +// +// Revision 1.59 2002/08/09 01:10:30 Flayra +// - Keep previous model when a game is over and going back to ready room +// - Refactoring for scoreboard +// - Support for "jump" animation +// - Freeze player before game starts +// - Reset score when leaving a team +// +// Revision 1.58 2002/08/02 21:52:18 Flayra +// - Cleaned up jetpacks, made webbing useful again, help messages, added "orderself" cheat, changed gestation messages for new tooltip system, added GetHasAvailableUpgrades() for help system, removed particle template messages, slowed down particle template update rate to reduce overflows (woo!) +// +// Revision 1.57 2002/07/28 19:21:28 Flayra +// - Balance changes after/during RC4a +// +// Revision 1.56 2002/07/26 23:07:53 Flayra +// - Numerical feedback +// - New artwork for marine, with jetpack as body group (this code doesn't work) +// - Misc. fixes (alien vision mode not being preserved when it should, and staying when it shouldn't) +// +// Revision 1.55 2002/07/24 18:45:42 Flayra +// - Linux and scripting changes +// +// Revision 1.54 2002/07/23 17:17:57 Flayra +// - Added power armor, added "talking" state for hive sight, changes for new resource model, removed max buildings, create buildings with random orientation, added stimpack, tweaked redemption, added new hive sight info +// +// Revision 1.53 2002/07/10 14:44:39 Flayra +// - Draw chat text while dead (bug #280) +// +// Revision 1.52 2002/07/08 17:15:39 Flayra +// - Added validation of all impulses (assumes invalid by default, instead of valid), reset players like all other entities, ensnaring fixes, players are temporarily invulnerable when they spawn, preserve health and armor when using command station, fixed up redemption, add hooks for commander voting and going back to the ready room, models can't be changed via the console anymore +// +// Revision 1.51 2002/07/01 22:41:40 Flayra +// - Removed outdated overwatch target and tension events +// +// Revision 1.50 2002/07/01 21:43:16 Flayra +// - Removed outdated adrenaline concept, added new alien sight mode, primal scream, removed flashlight battery message, fixed morphing problems for level 5 (and others), tweaked marine and alien speeds, fixed triple speed upgrade problem, disabled overwatch, moved player assets out of precache() (wasn't being called reliably), get full energy after a lifeform change, hive sight only draws out of sight now, added scent of fear +// +// Revision 1.49 2002/06/25 18:13:57 Flayra +// - Added death animations, added more general animation support, leaps do damage, general construct effects, new alien inventory system, added charging, info_locations, galloping, new alien building code, better morphing system (prevents getting stuck), more builder error messages, clear deaths on game end, hide health while gestating +// +// Revision 1.48 2002/06/10 20:03:13 Flayra +// - Updated for new minimap (remember killed position). Updated blood so marines aren't bloody, and aliens emit yellow blood. Removed slowing when hit (now players fly back when hit though), UpdateBlips() hack (fix when time) +// +// Revision 1.47 2002/06/03 16:54:59 Flayra +// - Added more effective player classes for scoreboard, send player class every time role changes (more network usage, always updated), changed hive sight to always be visible when under attack, all entities added to hive sight, not just players +// +// Revision 1.46 2002/05/28 18:03:40 Flayra +// - Refactored size for role code for movement chambers, deal with inventory properly (entity leak), increased web ensnaring effects (put weapon away!), reinforcement refactoring, tweaked speeds so marines are a little slower, and speed upgrade works properly again (now level 1 can generally outrun marines), added recycling support, play destroy egg sound when killed when morphing, new hive sight, EffectivePlayerClassChanged() refactoring +// +// Revision 1.45 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "util/nowarnings.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHMessage.h" +#include "mod/AvHParticleTemplateServer.h" +#include "mod/AvHEntities.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHConstants.h" +#include "mod/AvHMarineWeapons.h" +#include "dlls/client.h" +#include "dlls/util.h" +#include "mod/AvHSoundListManager.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHTitles.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHParticleTemplate.h" +#include "common/vector_util.h" +#include "dlls/roach.h" +#include "mod/AvHSelectionHelper.h" +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHDramaticPriority.h" +#include "mod/AvHHulls.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHParticleSystemEntity.h" +#include "mod/AvHAlienAbilities.h" +#include "mod/AvHAlienAbilityConstants.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "mod/AvHMarineTurret.h" +#include "mod/AvHSiegeTurret.h" +#include "mod/AvHBlipConstants.h" +#include "mod/AvHParticleConstants.h" +#include "util/MathUtil.h" +#include "types.h" + +#include "mod/AvHNetworkMessages.h" +#include "mod/AvHNexusServer.h" + +std::string GetLogStringForPlayer( edict_t *pEntity ); + +extern int gJetpackEventID; +extern int gAlienSightOnEventID; +extern int gAlienSightOffEventID; +extern int gStartOverwatchEventID; +extern int gEndOverwatchEventID; +extern int gRegenerationEventID; +extern int gStartCloakEventID; +extern int gEndCloakEventID; +extern int gNumFullPackCalls; +extern int gWeaponAnimationEventID; +extern int gMetabolizeSuccessEventID; +extern int gPhaseInEventID; + +// Yucky globals +extern AvHParticleTemplateListServer gParticleTemplateList; +extern AvHSoundListManager gSoundListManager; +extern cvar_t allow_spectators; +extern cvar_t avh_marinereinforcementcost; +#ifdef USE_OLDAUTH +extern cvar_t avh_uplink; +#endif +#ifdef DEBUG +extern cvar_t avh_spawninvulnerabletime; +#endif + +AvHSelectionHelper gSelectionHelper; +extern vec3_t gPMDebugPoint; +extern void ResetPlayerPVS( edict_t *client, int clientnum ); + +LINK_ENTITY_TO_CLASS( player, AvHPlayer ); + +const float kTransitionFadeTime = .6f; +const float kSpawnInFadeTime = .9f; + +AvHPlayer::AvHPlayer() +{ + this->Init(); + //TODO: find out lifecycle of entity vs. lifecycle of client and reset only when we have a new client. + this->InitBalanceVariables(); +} + +void AvHPlayer::AddDebugEnemyBlip(float inX, float inY, float inZ) +{ + this->mEnemyBlips.AddBlip(inX, inY, inZ); +} + +void AvHPlayer::AddPoints( int score, BOOL bAllowNegativeScore ) +{ + // Positive score always adds + if ( score < 0 ) + { + if ( !bAllowNegativeScore ) + { + if ( this->mScore < 0 ) // Can't go more negative + return; + + if ( -score > this->mScore ) // Will this go negative? + { + score = -this->mScore; // Sum will be 0 + } + } + } + + this->mScore += score; + + this->EffectivePlayerClassChanged(); +} + +void AvHPlayer::AwardKill( entvars_t* inTargetPEV) +{ + // Don't award points in new resource model + //CBaseEntity* inTarget = CBaseEntity::Instance(inTargetPEV); + //GetGameRules()->AwardPointsToPlayer(this, inTarget); +} + +int AvHPlayer::BloodColor(void) +{ + int theBloodColor = DONT_BLEED; + + if(this->GetIsMarine() && !this->GetHasHeavyArmor()) + { + theBloodColor = BLOOD_COLOR_RED; + } + else if(this->GetIsAlien()) + { + theBloodColor = BLOOD_COLOR_YELLOW; + } + + return theBloodColor; +} + +void AvHPlayer::AcquireOverwatchTarget() +{ + ASSERT(this->mInOverwatch); + + // Find closest enemy in FOV + + // Find cockroaches for now + CBaseEntity* theCurrentTarget = NULL; + float theCurrentRange = 1000000; + + //FOR_ALL_ENTITIES(kRoachClassName, CRoach*) + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(this->pev->team != theEntity->pev->team) + { + float theDistance = (theEntity->pev->origin - this->pev->origin).Length(); + if(this->GetIsEntityInSight(theEntity) && (theDistance <= theCurrentRange) && theEntity->GetIsRelevant()) + { + theCurrentTarget = theEntity; + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + //END_FOR_ALL_ENTITIES(kRoachClassName) + + if(theCurrentTarget) + { + this->mOverwatchTarget = ENTINDEX(theCurrentTarget->edict()); + this->pev->fuser1 = this->mOverwatchTarget; + + // Playback target event + //PLAYBACK_EVENT_FULL(0, this->edict(), gTargetOverwatchEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + } +} + +bool AvHPlayer::AttemptToBuildAlienStructure(AvHMessageID inMessageID) +{ + bool theSuccess = false; + + string theErrorMessage; + int theBuildingPointCost = 0; + bool thePurchaseAllowed = this->GetPurchaseAllowed(inMessageID, theBuildingPointCost, &theErrorMessage); + + if(thePurchaseAllowed) + { + if(inMessageID == ALIEN_BUILD_HIVE) + { + // See if there is an inactive hive within range + UTIL_MakeVectors(this->pev->v_angle); + + Vector theStart = this->GetGunPosition(); + Vector theEnd = theStart + gpGlobals->v_forward*50; + + // Collide with world to find potential build site + TraceResult theTR; + UTIL_TraceLine(theStart, theEnd, dont_ignore_monsters, this->edict(), &theTR); + + Vector theLocation = theTR.vecEndPos; + + // Do we have enough points? + if(AvHSHUGetIsSiteValidForBuild(ALIEN_BUILD_HIVE, &theLocation, this->entindex())) + { + // Get the hive at this location + CBaseEntity* theBaseEntity = NULL; + AvHHive* theNearestHive = NULL; + + // Find the nearest hive + while((theBaseEntity = UTIL_FindEntityByClassname(theBaseEntity, kesTeamHive)) != NULL) + { + if(theBaseEntity) + { + AvHHive* theCurrentHive = dynamic_cast(theBaseEntity); + if(theCurrentHive) + { + float theCurrentDistance = VectorDistance(theLocation, theCurrentHive->pev->origin); + if(!theNearestHive || (theCurrentDistance < VectorDistance(theLocation, theNearestHive->pev->origin))) + { + theNearestHive = theCurrentHive; + } + } + } + } + + if(theNearestHive) + { + // Make sure another hive isn't already building for this team + bool theAnotherHiveBuilding = false; + + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if((theEntity->pev->team == this->pev->team) && theEntity->GetIsSpawning()) + { + theAnotherHiveBuilding = true; + break; + } + END_FOR_ALL_ENTITIES(kesTeamHive); + + if(!theAnotherHiveBuilding) + { + // If so, set it as growing + if(theNearestHive->StartSpawningForTeam(this->GetTeam())) + { + AvHSUBuildingJustCreated(inMessageID, theNearestHive, this); + theSuccess = true; + } + else + { + this->SendMessage(kHelpTextHiveBlocked); + } + } + else + { + this->SendMessage(kHelpTextOtherHiveBuilding); + } + } + } + else + { + this->SendMessage(kHelpTextEmptyHiveNotNearby); + } + } + else + { + char* theClassName = NULL; + if(AvHSHUGetBuildTechClassName(inMessageID, theClassName)) + { + // Make sure we haven't exceeded the structure limit + int theNumBuildings = AvHSUGetStructureCount(inMessageID); + + // Now check to make sure the space is big enough to hold the building + UTIL_MakeVectors(this->pev->v_angle); + + const int kAimRange = 48; + Vector theStart = this->GetGunPosition(); + Vector theEnd = theStart + gpGlobals->v_forward*kAimRange; + + // Collide with world to find potential build site + TraceResult theTR; + UTIL_TraceLine(theStart, theEnd, dont_ignore_monsters, this->edict(), &theTR); + + Vector theLocation = theTR.vecEndPos; + + // Check if collision point is valid for building + if(AvHSHUGetIsSiteValidForBuild(inMessageID, &theLocation)) + { + if (theNumBuildings < GetGameRules()->GetStructureLimit()) + { + // Make sure there aren't too many buildings in this area already + int theNumBuildingsNearby = UTIL_CountEntitiesInSphere(theLocation, BALANCE_VAR(kBuildingVisibilityRadius), theClassName); + if(theNumBuildingsNearby < BALANCE_VAR(kNumSameAlienStructuresAllowedInRadius) || FStrEq(theClassName, kwsAlienResourceTower))//: allow the building of rt's regardless of how many may be close by (for maps that have a lot of nodes close to each other) + { + // Create the new building + CBaseEntity* theEntity = CBaseEntity::Create(theClassName, theLocation, AvHSUGetRandomBuildingAngles()); + + // Set building's team + theEntity->pev->team = this->pev->team; + + AvHSUBuildingJustCreated(inMessageID, theEntity, this); + + // Set owner (this prevents collisions between the entity and it's owner though) + //theEntity->pev->owner = ENT(this->pev); + + //: I've moved this here because whats the point of playing the sound if the building didnt get placed? (it was after " Vector theLocation = theTR.vecEndPos;") + // Play sound + char* theSoundEffect = kAlienBuildingSound1; + + if(RANDOM_LONG(0, 1) == 1) + theSoundEffect = kAlienBuildingSound2; + + EMIT_SOUND(this->edict(), CHAN_AUTO, theSoundEffect, this->GetAlienAdjustedEventVolume(), ATTN_NORM); + + theSuccess = true; + } + else + { + this->SendMessage(kTooManyStructuresOfThisTypeNearby); + } + } + else + { + this->SendMessage(kTooManyStructuresOnServer); + } + } + else + { + // Play hive easter egg sometimes when trying to build a hive where one already exists + if(inMessageID == ALIEN_BUILD_HIVE) + { + //// If we are close to a hive we already own + //AvHHive* theHive = AvHSUGetRandomActiveHive((AvHTeamNumber)this->pev->team); + //if(theHive) + //{ + // float theDistance = VectorDistance(theHive->pev->origin, this->pev->origin); + // if(theDistance <= 300) + // { + // // Randomly play easter egg + // if(RANDOM_LONG(0, 10) == 0) + // { + // EMIT_SOUND(this->edict(), CHAN_AUTO, kMyHiveEasterEgg, 1.0f, ATTN_NORM); + // } + // } + //} + } + } + } + else + { + this->PlayHUDSound(HUD_SOUND_ALIEN_MORE); + } + } + } + else + { + this->SendMessage(theErrorMessage.c_str()); + } + + return theSuccess; +} + +bool AvHPlayer::BuildTech(AvHMessageID inBuildID, const Vector& inPickRay) +{ + bool theSuccess = false; + + //AvHSUSetIsDebugging(true); + + // If valid + if(AvHSHUGetIsBuildTech(inBuildID)) + { + // Make sure this is a valid place to build + Vector theLocation; + if(AvHSHUTraceAndGetIsSiteValidForBuild(inBuildID, this->GetVisualOrigin(), inPickRay, &theLocation)) + { + // : 1097 Commander created entities now created 4 units above the scan location and they drop to the floor. + if ( this->GetIsInTopDownMode() ) { + theLocation[2]+=4; + } + // Decrement resources + string theErrorMessage; + int theCost = 0; + bool thePurchaseAllowed = this->GetPurchaseAllowed(inBuildID, theCost, &theErrorMessage); + if(thePurchaseAllowed) + { + // Count how many entities on our team we have in area + int theNumFriendlyEntitiesInArea = 0; + CBaseEntity* theEntity = NULL; + while((theEntity = UTIL_FindEntityInSphere(theEntity, theLocation, BALANCE_VAR(kBuildingVisibilityRadius))) != NULL) + { + // Don't count players + if(!theEntity->IsPlayer() && (theEntity->pev->team == this->pev->team)) + { + theNumFriendlyEntitiesInArea++; + } + } + + // Make sure we haven't exceeded the structure limit + int theNumBuildings = AvHSUGetStructureCount(inBuildID); + + if(theNumBuildings < GetGameRules()->GetStructureLimit()) + { + if(theNumFriendlyEntitiesInArea < BALANCE_VAR(kMaxMarineEntitiesAllowedInRadius)) + { + // Build it! + theSuccess = (AvHSUBuildTechForPlayer(inBuildID, theLocation, this) != NULL); + + // Inform structure about build if possible + if(theSuccess) + { + if(this->mSelected.size() > 0) + { + // Get selected structure and inform + int theFirstEntitySelected = *this->mSelected.begin(); + AvHBaseBuildable* theBaseBuildable = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theFirstEntitySelected))); + if(theBaseBuildable) + { + theBaseBuildable->TechnologyBuilt(inBuildID); + } + } + + this->PayPurchaseCost(theCost); + } + } + else + { + this->SendMessage(kTooManyStructuresInArea); + } + } + else + { + this->SendMessage(kTooManyStructuresOnServer); + } + } + else + { + this->PlayHUDSound(HUD_SOUND_MARINE_MORE); + } + } + } + + //AvHSUSetIsDebugging(false); + + // If successful + // Send confirmation if needed + // else + // Send failure so client is updated + + return theSuccess; +} + +bool AvHPlayer::GroupMessage(AvHMessageID inGroupMessage) +{ + bool theSuccess = false; + + if((inGroupMessage >= GROUP_CREATE_1) && (inGroupMessage < (GROUP_CREATE_1 + kNumHotkeyGroups))) + { + int theOffset = (int)(inGroupMessage - GROUP_CREATE_1); + ASSERT(theOffset >= 0); + ASSERT(theOffset < kNumHotkeyGroups); + if(this->mSelected.size() > 0) + { + this->GetTeamPointer()->SetGroup(theOffset, this->mSelected); + AvHUser3 theUser3 = this->GetTeamPointer()->GetGroupType(theOffset); + + AvHHUDSound theHudSound = HUD_SOUND_SELECT; + if(theUser3 == AVH_USER3_MARINE_PLAYER) + { + theHudSound = AvHHUDSound(HUD_SOUND_SQUAD1 + theOffset); + } + + this->PlayHUDSound(theHudSound); + + // If this is a squad, tell all the players in the squad also. This also tells them they are in the squad. + if(theUser3 == AVH_USER3_MARINE_PLAYER) + { + // Loop through them + for(EntityListType::iterator theIter = this->mSelected.begin(); theIter != this->mSelected.end(); theIter++) + { + AvHPlayer* thePlayer = NULL; + AvHSUGetEntityFromIndex(*theIter, thePlayer); + if(thePlayer) + { + thePlayer->PlayHUDSound(theHudSound); + } + } + } + + // Now run through all the hotgroups and remove these entities from them (entities can only be part of one group) + for(int theCurrentIndex = 0; theCurrentIndex < kNumHotkeyGroups; theCurrentIndex++) + { + if(theCurrentIndex != theOffset) + { + //this->mGroups[theCurrentIndex]; + EntityListType theCurrentGroup = this->GetTeamPointer()->GetGroup(theCurrentIndex); + + // Remove all members of mSelected from this group + for(EntityListType::iterator theIterator = theCurrentGroup.begin(); theIterator != theCurrentGroup.end(); /* nothing */) + { + // If the current entity is in selection + EntityListType::iterator theFindIter = std::find(this->mSelected.begin(), this->mSelected.end(), *theIterator); + bool theEntityInSelection = (theFindIter != this->mSelected.end()); + if(theEntityInSelection) + { + // Remove it from this list + theIterator = theCurrentGroup.erase(theIterator); + } + else + { + // Else, increment + theIterator++; + } + } + + // Set the group again + this->GetTeamPointer()->SetGroup(theCurrentIndex, theCurrentGroup); + } + } + } + theSuccess = true; + } + else if((inGroupMessage >= GROUP_SELECT_1) && (inGroupMessage < (GROUP_SELECT_1 + kNumHotkeyGroups))) + { + int theOffset = (int)(inGroupMessage - GROUP_SELECT_1); + ASSERT(theOffset >= 0); + ASSERT(theOffset < kNumHotkeyGroups); + + //const EntityListType& theGroup = this->mGroups[theOffset]; + EntityListType theGroup = this->GetTeamPointer()->GetGroup(theOffset); + if(theGroup.size() > 0) + { + if(this->mSelected != theGroup) + { + this->mSelected = theGroup; + this->mTrackingEntity = 0; + } + else + { + // If we received the same select message twice in a row, go instead to the last place we went to + if(inGroupMessage == this->mLastSelectEvent) + { + // Go to last saved position + VectorCopy(this->mPositionBeforeLastGotoGroup, this->pev->origin); + + // Clear last select so hitting it again will go to group + this->mLastSelectEvent = MESSAGE_NULL; + } + else + { + // Find nearest entity in group and track it + int theNearestGroupEntity = 0; + float theClosestDistance = sqrt(2*kMaxMapDimension*kMaxMapDimension); + for(EntityListType::const_iterator theIter = theGroup.begin(); theIter != theGroup.end(); theIter++) + { + CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter)); + ASSERT(theBaseEntity); + float theCurrentDistance = VectorDistance(theBaseEntity->pev->origin, this->pev->origin); + if(theCurrentDistance < theClosestDistance) + { + theNearestGroupEntity = *theIter; + theClosestDistance = theCurrentDistance; + } + } + + this->mTrackingEntity = theNearestGroupEntity; + + // Move player in vicinity of player so entity is in PVS + if(this->mTrackingEntity > 0) + { + CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mTrackingEntity)); + if(theBaseEntity) + { + // Save last position so we can jump back to it easily + VectorCopy(this->pev->origin, this->mPositionBeforeLastGotoGroup); + this->mLastSelectEvent = inGroupMessage; + + // Goto group + VectorCopy(theBaseEntity->pev->origin, this->pev->origin); + } + } + } + } + } + + theSuccess = true; + } + + return theSuccess; +} + +bool AvHPlayer::GetCenterPositionForGroup(int inGroupNumber, float& outX, float& outY) +{ + bool theSuccess = false; + + if((inGroupNumber >= 1) && (inGroupNumber <= kNumHotkeyGroups)) + { + int theNumFound = 0; + Vector theLocation; + theLocation.x = theLocation.y = theLocation.z = 0.0f; + + //EntityListType& theGroup = this->mGroups[inGroupNumber-1]; + EntityListType theGroup = this->GetTeamPointer()->GetGroup(inGroupNumber-1); + for(EntityListType::iterator theIter = theGroup.begin(); theIter != theGroup.end(); theIter++) + { + Vector theEntityLocation; + if(AvHSHUGetEntityLocation(*theIter, theEntityLocation)) + { + theLocation.x += theEntityLocation.x; + theLocation.y += theEntityLocation.y; + + theNumFound++; + } + } + + if(theNumFound > 0) + { + outX = theLocation.x/theNumFound; + outY = theLocation.y/theNumFound; + + theSuccess = true; + } + } + + return theSuccess; +} + +string AvHPlayer::GetNetworkID() const +{ + return this->mNetworkID; +} + +void AvHPlayer::SetNetworkID(string& inNetworkID) +{ + this->mNetworkID = inNetworkID; +} + +string AvHPlayer::GetNetworkAddress() const +{ + return this->mNetworkAddress; +} + +void AvHPlayer::SetNetworkAddress(string& inNetworkAddress) +{ + this->mNetworkAddress = inNetworkAddress; +} + +void AvHPlayer::ClearRoleAbilities() +{ + // Clear all abilities that don't stick around between changing role, like all alien purchaseable upgrades + //SetUpgradeMask(&this->pev->iuser4, ALIEN_ABILITY_2, false); + this->pev->iuser3 = AVH_USER3_NONE; + this->pev->iuser4 &= ~MASK_ALIEN_EMBRYO; + this->pev->iuser4 &= ~MASK_ALIEN_MOVEMENT; + this->mIsScreaming = false; + //this->mAlienSightActive = false; + this->mDesiredRoomType = 0; +} + +void AvHPlayer::ClearUserVariables() +{ + this->pev->iuser1 = 0; + this->pev->iuser2 = 0; + this->pev->iuser3 = 0; + this->pev->iuser4 = 0; + this->pev->fuser1 = 0; + this->pev->fuser2 = 0; + this->pev->fuser3 = 0; + this->pev->fuser4 = 0; +} + +void AvHPlayer::SetScore(int inScore) +{ + this->mScore = inScore; +} + +int AvHPlayer::GetScore() const +{ + return this->mScore; +} + +CBaseEntity* AvHPlayer::CreateAndDrop(const char* inItemName) +{ + UTIL_MakeVectors(pev->v_angle); + + CBaseEntity* theEntity = CBaseEntity::Create(inItemName, pev->origin + gpGlobals->v_forward * 10, pev->angles, edict() ); + + theEntity->pev->angles.x = 0; + theEntity->pev->angles.z = 0; + //theEntity->PackWeapon( pWeapon ); + theEntity->pev->velocity = gpGlobals->v_forward * 300 + gpGlobals->v_forward * 100; + + return theEntity; +} + +void AvHPlayer::DeployCurrent() +{ + if(this->m_pActiveItem /*&& !this->GetIsBeingDigested()*/) + { + CBasePlayerWeapon* theCurrentWeapon = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); + if(theCurrentWeapon && theCurrentWeapon->CanDeploy()) + { + theCurrentWeapon->Deploy(); + } + } + else + { + // No current weapon, so get the best weapon we have. + g_pGameRules->GetNextBestWeapon( this, NULL ); + } +} + + +// Drop the current item, not weapon, if any. +bool AvHPlayer::DropItem(const char* inItemName) +{ + bool theSuccess = false; + + if(!GetGameRules()->GetIsCombatMode()) + { + CBasePlayerItem* theItem = this->m_pActiveItem; + + if(inItemName) + { + bool theIsDone = false; + theItem = NULL; //we're looking for an item by name, don't bias with active item + + // Find item with this name + for(int i = 0; (i < MAX_ITEM_TYPES) && !theIsDone; i++) + { + CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; + while(theCurrentItem && !theIsDone) + { + if(FStrEq(STRING(theCurrentItem->pev->classname), inItemName)) + { + theIsDone = true; + theItem = theCurrentItem; + } + theCurrentItem = theCurrentItem->m_pNext; + } + } + } + + if(theItem) + { + AvHBasePlayerWeapon* theOriginalDroppedWeapon = dynamic_cast(theItem); + if(theOriginalDroppedWeapon && theOriginalDroppedWeapon->GetIsDroppable()) + { + // Dropping current weapon turns off overwatch + this->TurnOffOverwatch(); + + //char theItemName[256]; + //strcpy(theItemName, STRING(theItem->pev->classname)); + //this->DropPlayerItem(theItemName); + + // Get ammo left, if it's a weapon + int theAmmoLeft = -1; + if(theOriginalDroppedWeapon) + { + theAmmoLeft = theOriginalDroppedWeapon->m_iClip; + GetGameRules()->GetNextBestWeapon(this, theItem); + } + + CBaseEntity* theDroppedEntity = this->CreateAndDrop(STRING(theItem->pev->classname)); + if(theAmmoLeft != -1) + { + CBasePlayerWeapon* theNewlyDroppedWeapon = dynamic_cast(theDroppedEntity); + ASSERT(theNewlyDroppedWeapon); + + // Set ammo, make sure client ammo isn't the same so update is forced + theNewlyDroppedWeapon->m_iClip = theAmmoLeft; + theNewlyDroppedWeapon->m_iClientClip = theAmmoLeft - 1; + + // Means "only ammo is that in the clip" + theNewlyDroppedWeapon->m_iDefaultAmmo = 0; + + ItemInfo theItemInfo; + + if(theOriginalDroppedWeapon->GetItemInfo(&theItemInfo) != 0) + { + int iAmmoIndex = GetAmmoIndex ( (char *) theItemInfo.pszAmmo1 ); + if ( theItemInfo.iSlot == 0 || theItemInfo.iId == AVH_WEAPON_MINE || theItemInfo.iId == AVH_WEAPON_WELDER ) + this->EffectivePlayerClassChanged(); + + if ( iAmmoIndex != -1 && m_rgAmmo[ iAmmoIndex ] > 0) + this->DropAmmo((char *)theItemInfo.pszAmmo1, m_rgAmmo[ iAmmoIndex ], theItemInfo.iMaxAmmo1, theItemInfo.iId, theNewlyDroppedWeapon->pev->angles); + } + + } + + // Finally remove the original + theOriginalDroppedWeapon->DestroyItem(); + + // Weak potential fix for occasional crash. Saw this when someone hit "lastinv" while holding a knife, + // and m_pLastItem was garbage, not NULL + this->m_pLastItem = NULL; + SetAnimation(PLAYER_PRIME); //Elven - hack to kill TPRA if we throw a weapon. + theSuccess = true; + } + } + } + + return theSuccess; +} + +void AvHPlayer :: DropAmmo(char *pszAmmoType, int iAmmoAmt, int iMax, int iWeaponID, Vector vecAngles) +{ + if(!pszAmmoType || !iAmmoAmt || !iMax || !iWeaponID) + return; + + int iAmmoIndex = GetAmmoIndex ( pszAmmoType ); + + //Not a valid ammo type. + if(iAmmoIndex == -1) + return; + + if(m_rgAmmo[ iAmmoIndex ] > 0) + m_rgAmmo[ iAmmoIndex ] -= iAmmoAmt; + else + return; + + int theSubModel = -1; + + switch(iWeaponID) + { + case AVH_WEAPON_PISTOL: theSubModel = 3; break; + case AVH_WEAPON_MG: theSubModel = 1; break; + case AVH_WEAPON_SONIC: theSubModel = 0; break; + case AVH_WEAPON_HMG: theSubModel = 2; break; + case AVH_WEAPON_GRENADE_GUN: theSubModel = 4; break; + } + + if(theSubModel == -1) + return; + + CBaseEntity *theDroppedEntity = this->CreateAndDrop( kwsAmmoPack ); + AvHAmmoPack *theRealAmmoPack = dynamic_cast(theDroppedEntity); + + strncpy(theRealAmmoPack->m_szAmmoType, pszAmmoType, 31); + theRealAmmoPack->m_iMaxAmmo = iMax; + theRealAmmoPack->m_iAmmoAmt = iAmmoAmt; + theRealAmmoPack->m_iWeaponID = iWeaponID; + theRealAmmoPack->pev->body = theSubModel; + theRealAmmoPack->pev->angles = vecAngles; + + theRealAmmoPack->SetThink( &CBaseEntity::SUB_Remove ); + theRealAmmoPack->pev->nextthink = gpGlobals->time + AvHSUGetWeaponStayTime(); +} + +void AvHPlayer::EffectivePlayerClassChanged() +{ + this->mEffectivePlayerClassChanged = true; +} + +void AvHPlayer::NeedsTeamUpdate() +{ + this->mNeedsTeamUpdate = true; +} + +void AvHPlayer::SendTeamUpdate() +{ + this->mSendTeamUpdate = true; +} + +bool AvHPlayer::ExecuteAlienMorphMessage(AvHMessageID inMessageID, bool inInstantaneous) +{ + bool theMessageExecuted = false; + + string theErrorMessage; + bool theHadEnoughPoints = false; + + switch(inMessageID) + { + case ALIEN_LIFEFORM_ONE: + case ALIEN_LIFEFORM_TWO: + case ALIEN_LIFEFORM_THREE: + case ALIEN_LIFEFORM_FOUR: + case ALIEN_LIFEFORM_FIVE: + + case ALIEN_EVOLUTION_ONE: + case ALIEN_EVOLUTION_TWO: + case ALIEN_EVOLUTION_THREE: + //case ALIEN_EVOLUTION_FOUR: + //case ALIEN_EVOLUTION_FIVE: + //case ALIEN_EVOLUTION_SIX: + case ALIEN_EVOLUTION_SEVEN: + case ALIEN_EVOLUTION_EIGHT: + case ALIEN_EVOLUTION_NINE: + case ALIEN_EVOLUTION_TEN: + case ALIEN_EVOLUTION_ELEVEN: + case ALIEN_EVOLUTION_TWELVE: + case ALIEN_HIVE_TWO_UNLOCK: + case ALIEN_HIVE_THREE_UNLOCK: + + // Now only allow upgrading from level1 + if(this->GetCanGestate(inMessageID, theErrorMessage)) + { + // Stay as current lifeform by default + bool theCheckDucking = true; + int theTargetIuser3 = this->pev->iuser3; + switch(inMessageID) + { + case ALIEN_LIFEFORM_ONE: + theTargetIuser3 = AVH_USER3_ALIEN_PLAYER1; + break; + case ALIEN_LIFEFORM_TWO: + theTargetIuser3 = AVH_USER3_ALIEN_PLAYER2; + break; + case ALIEN_LIFEFORM_THREE: + theTargetIuser3 = AVH_USER3_ALIEN_PLAYER3; + break; + case ALIEN_LIFEFORM_FOUR: + theTargetIuser3 = AVH_USER3_ALIEN_PLAYER4; + break; + case ALIEN_LIFEFORM_FIVE: + theTargetIuser3 = AVH_USER3_ALIEN_PLAYER5; + theCheckDucking = false; + break; + } + + int theTargetHull = AvHMUGetHull(theCheckDucking, theTargetIuser3); + vec3_t theOrigin; + GetNewOrigin((AvHUser3)theTargetIuser3, theCheckDucking, theOrigin); + + // removed by to fix gestating in vents. + //theOrigin.z += 5; + + bool theIsEnoughRoom = AvHSUGetIsEnoughRoomForHull(theOrigin, theTargetHull, this->edict()); + //: try again but higher + if(!theIsEnoughRoom) + { + + theOrigin.z += AvHMUGetOriginOffsetForMessageID(inMessageID); + + theIsEnoughRoom = AvHSUGetIsEnoughRoomForHull(theOrigin, theTargetHull, this->edict()); + } + + if(theIsEnoughRoom || inInstantaneous) + { + if(!theIsEnoughRoom) + { + int a = 0; + } + TraceResult tr; + Vector vecStart, vecEnd; + VectorCopy(this->pev->origin, vecStart); + VectorCopy(this->pev->origin, vecEnd); + vecEnd[2]-=100; + UTIL_TraceLine(vecStart, vecEnd, ignore_monsters, dont_ignore_glass, NULL, &tr); + + if ( tr.flFraction == 1.0f || tr.vecPlaneNormal[2] > 0.7 ) { + this->Evolve(inMessageID, inInstantaneous); + theMessageExecuted = true; + } + else { + this->SendMessage(kSurfaceTooSteep); + } + } + else + { + this->SendMessage(kNeedMoreRoomToGestate); + } + } + else + { + this->SendMessage(theErrorMessage.c_str()); + } + break; + } + + return theMessageExecuted; +} + +bool AvHPlayer::ExecuteMessage(AvHMessageID inMessageID, bool inInstantaneous, bool inForce) +{ + bool theMessageExecuted = false; + + AvHTeam* theTeam = this->GetTeamPointer(); + string theErrorMessage; + + // If valid + if((inMessageID != MESSAGE_NULL) && theTeam) + { + bool theIsMarine = theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE; + bool theIsAlien = theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN; + bool theGameStarted = GetGameRules()->GetGameStarted(); + bool theIsInTopDownMode = this->GetIsInTopDownMode(); + bool theIsCombat = GetGameRules()->GetIsCombatMode(); + bool theIsBeingDigested = this->GetIsBeingDigested(); + + // If we can purchase it + string theErrorMessage; + int theCost = 0; + + if(this->GetPurchaseAllowed(inMessageID, theCost, &theErrorMessage) || inForce) + { + // If we're in Combat mode + bool theIsAvailable = true; + + if(theIsCombat) + { + // Try to execute message + theMessageExecuted = this->ExecuteCombatMessage(inMessageID, theIsAvailable, inForce); + if(theMessageExecuted) + { + this->PurchaseCombatUpgrade(inMessageID); + } + } + + // If it's a fall-through alien upgrade, try it next + if(!theMessageExecuted && theIsAlien && !theIsBeingDigested) + { + // Try to morph or build + theMessageExecuted = this->ExecuteAlienMorphMessage(inMessageID, inInstantaneous); + if(!theMessageExecuted && !theIsCombat) + { + switch(inMessageID) + { + case ALIEN_BUILD_RESOURCES: + case ALIEN_BUILD_OFFENSE_CHAMBER: + case ALIEN_BUILD_DEFENSE_CHAMBER: + case ALIEN_BUILD_SENSORY_CHAMBER: + case ALIEN_BUILD_MOVEMENT_CHAMBER: + case ALIEN_BUILD_HIVE: + theMessageExecuted = this->AttemptToBuildAlienStructure(inMessageID); + break; + } + } + } + + // Add upgrade to our list to be preserved on respawn + if(theMessageExecuted && theIsCombat) + this->mCombatNodes.SetResearchDone(inMessageID); + + if(theMessageExecuted) + this->PayPurchaseCost(theCost); + } + + // : 0000971 + int theIssuedOrderIcon = -1; + // : + + if(theIsMarine + && !theIsInTopDownMode + && !theIsBeingDigested) + { + switch (inMessageID) + { + case WEAPON_RELOAD: + if(theGameStarted) + this->ReloadWeapon(); + break; + case WEAPON_DROP: + if(!this->DropItem()) + this->SendMessageOnce(kWeaponCantBeDropped, TOOLTIP); + break; + + case ADMIN_VOTEDOWNCOMMANDER: + if(theTeam->PlayerVote(this->entindex(), theErrorMessage)) + theMessageExecuted = true; + else + this->SendMessage(theErrorMessage.c_str()); + break; + // Talk to your teammates + case SAYING_1: + case SAYING_2: + case SAYING_3: + case SAYING_4: + case SAYING_5: + case SAYING_6: + case SAYING_7: + case SAYING_8: + case SAYING_9: + case ORDER_REQUEST: + case ORDER_ACK: + if(this->PlaySaying(inMessageID)) + { + AvHAlertType theAlertType = ALERT_NONE; + AvHMessageID theMessageID = MESSAGE_NULL; + + if(this->GetIsMarine()) + { + switch(inMessageID) + { + // : 0000971 + // decides whether icon updates should be sent + case SAYING_1: + theIssuedOrderIcon = TEAMMATE_MARINE_ORDER_FOLLOW; + break; + + case SAYING_2: + theIssuedOrderIcon = TEAMMATE_MARINE_ORDER_COVER; + break; + + case SAYING_8: + theIssuedOrderIcon = TEAMMATE_MARINE_ORDER_WELD; + break; + // : + + case SAYING_5: + theAlertType = ALERT_SOLDIER_NEEDS_AMMO; + theMessageID = COMMANDER_NEXTAMMO; + break; + + case SAYING_4: + theAlertType = ALERT_SOLDIER_NEEDS_HEALTH; + theMessageID = COMMANDER_NEXTHEALTH; + break; + + case ORDER_REQUEST: + theAlertType = ALERT_ORDER_NEEDED; + theMessageID = COMMANDER_NEXTIDLE; + break; + } + + // Send alert for commander + if(theAlertType != ALERT_NONE) + { + GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, theAlertType, this->entindex(), theMessageID); + } + } + + theMessageExecuted = true; + } + break; + } + } + else if(theIsAlien && !theIsBeingDigested) + { + switch (inMessageID) + { + // Talk to your teammates + case SAYING_1: + case SAYING_2: + case SAYING_3: + case SAYING_4: + case SAYING_5: + case SAYING_6: + case SAYING_7: + case SAYING_8: + case SAYING_9: + if(this->PlaySaying(inMessageID)) + theMessageExecuted = true; + break; + + case ALIEN_ABILITY_LEAP: + this->StartLeap(); + break; + case IMPULSE_FLASHLIGHT: + // Eat flashlight event. Add special mode for alien view here? + if(!this->mAlienSightActive) + PLAYBACK_EVENT_FULL(FEV_HOSTONLY, this->edict(), gAlienSightOnEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, 0, 0, 0, 0 ); + else + PLAYBACK_EVENT_FULL(FEV_HOSTONLY, this->edict(), gAlienSightOffEventID, 0, this->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, 0, 0, 0, 0 ); + this->mAlienSightActive = !this->mAlienSightActive; + theMessageExecuted = true; + break; + } + // : 0000971 + // decides whether icon updates should be sent + switch (inMessageID) + { + case SAYING_1: + theIssuedOrderIcon = TEAMMATE_ALIEN_ORDER_FOLLOW; + break; + case SAYING_2: + theIssuedOrderIcon = TEAMMATE_ALIEN_ORDER_COVER; + break; + case SAYING_4: + case SAYING_8: + theIssuedOrderIcon = TEAMMATE_ALIEN_ORDER_HEAL; + break; + } + // : + } + + // : 0000971 and 0000992 + if (theIssuedOrderIcon > -1) + { + int theOrderTarget = 0; + + vec3_t vecDir; + VectorCopy(this->GetAutoaimVector(0.0f), vecDir); + VectorNormalize(vecDir); + + float currentResult = 0.0f; + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*); + float dotResult = 0.0f; + float theDistance = 0.0f; + vec3_t vecDistance; + int theTraced = 0; + vec3_t vecFrom, vecTo; + + if ((theEntity->entindex() != this->entindex()) && (theEntity->GetTeam() == this->GetTeam())) + { + VectorSubtract(theEntity->pev->origin, this->pev->origin, vecDistance); + // theDistance = Length(vecDistance); + + VectorNormalize(vecDistance); + dotResult = DotProduct(vecDistance, vecDir); + if ((dotResult > 0.9f) && (dotResult > currentResult)) + { + TraceResult theTrace; + vecFrom = AvHSHUGetRealLocation(this->pev->origin, this->pev->mins, this->pev->maxs); + vecTo = AvHSHUGetRealLocation(theEntity->pev->origin, theEntity->pev->mins, theEntity->pev->maxs); + UTIL_TraceLine(vecFrom, vecTo, ignore_monsters, this->edict(), &theTrace); + if (theTrace.flFraction == 1.0f) + { + theTraced = 1; + currentResult = dotResult; + theOrderTarget = theEntity->entindex(); + } + } + } + //ALERT(at_console, "-------------------\n"); + //ALERT(at_console, UTIL_VarArgs("vecDir %f %f %f\n", vecDir[0], vecDir[1], vecDir[2])); + //ALERT(at_console, UTIL_VarArgs("vecDistance %f %f %f\n", vecDistance[0], vecDistance[1], vecDistance[2])); + //ALERT(at_console, UTIL_VarArgs("vecFrom %f %f %f\n", vecFrom[0], vecFrom[1], vecFrom[2])); + //ALERT(at_console, UTIL_VarArgs("vecTo %f %f %f\n", vecTo[0], vecTo[1], vecTo[2])); + //ALERT(at_console, UTIL_VarArgs("dotResult %f\n", dotResult)); + //ALERT(at_console, UTIL_VarArgs("currentResult %f\n", currentResult)); + //ALERT(at_console, UTIL_VarArgs("theTraced %d\n", theTraced)); + //ALERT(at_console, UTIL_VarArgs("theOrderTarget %d\n", theOrderTarget)); + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + +// ALERT(at_console, UTIL_VarArgs("theIssuedOrderIcon %d source %d target %d\n", theIssuedOrderIcon, this->entindex(), theOrderTarget)); + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*); + if(theEntity->GetTeam() == this->GetTeam()) + { + NetMsg_IssueOrder(theEntity->pev, theIssuedOrderIcon, this->entindex(), theOrderTarget); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + + } + + // Common messages here + if(!theMessageExecuted) + { + switch(inMessageID) + { + case WEAPON_NEXT: + this->NextWeapon(); + theMessageExecuted = true; + break; + case ADMIN_READYROOM: + if(this->GetPlayMode() != PLAYMODE_READYROOM) + { + this->SetPlayMode(PLAYMODE_READYROOM); + theMessageExecuted = true; + } + break; + } + } + } + return theMessageExecuted; +} + +bool AvHPlayer::GetIsAlienSightActive() const +{ + return this->mAlienSightActive && this->IsAlive(); +} + +void AvHPlayer::SetDesiredNetName(string inDesiredNetName) +{ + this->mDesiredNetName = inDesiredNetName; +} + +void AvHPlayer::SetDesiredRoomType(int inRoomType, bool inForceUpdate) +{ + this->mDesiredRoomType = inRoomType; + + if(inForceUpdate) + { + this->mClientDesiredRoomType = -1; + } +} + +void AvHPlayer::ResetPlayerPVS() +{ + // Force recompute of PVS + ::ResetPlayerPVS(this->edict(), this->entindex()); +} + +void AvHPlayer::SetPosition(const Vector& inNewPosition) +{ + // Set new position + VectorCopy(inNewPosition, this->pev->origin); + + this->ResetPlayerPVS(); +} + +// Play sounds quieter when the alien has the silence upgrade +float AvHPlayer::GetAlienAdjustedEventVolume() const +{ + return AvHPlayerUpgrade::GetSilenceVolumeLevel((AvHUser3)this->pev->iuser3, this->pev->iuser4); +} + + + +void AvHPlayer::GetAnimationForActivity(int inActivity, char outAnimation[64], bool inGaitSequence) +{ + strcpy(outAnimation, ""); + + bool theCanCrouch = !((this->GetUser3() == AVH_USER3_ALIEN_PLAYER1) || (this->GetUser3() == AVH_USER3_ALIEN_PLAYER2) || (this->GetUser3() == AVH_USER3_ALIEN_PLAYER3)); + bool theIsCrouched = FBitSet(this->pev->flags, FL_DUCKING); + bool theIsAlien = this->GetIsAlien(); + bool theIsGestating = (this->GetUser3() == AVH_USER3_ALIEN_EMBRYO); + bool theIsDeathAnim = false; + bool theIsReloading = false; + int theDebugAnimations = BALANCE_VAR(kDebugAnimations); + + //bool theIsBlinking = this->GetIsBlinking(); + + switch(inActivity) + { + case ACT_RELOAD: + if(!theIsAlien) + { + strcat(outAnimation, this->m_szAnimExtention); + strcat(outAnimation, "_reload"); + + theIsReloading = true; + }; + break; + // updated by Elven for TPRAs + case ACT_RELOAD_START: + if(!theIsAlien) + { + strcat(outAnimation, this->m_szAnimExtention); + strcat(outAnimation, "_reload_start"); + theIsReloading = true; + }; + break; + case ACT_RELOAD_INSERT: + if(!theIsAlien) + { + strcat(outAnimation, this->m_szAnimExtention); + strcat(outAnimation, "_reload_insert"); + theIsReloading = true; + }; + break; + case ACT_RELOAD_END: + if(!theIsAlien) + { + strcat(outAnimation, this->m_szAnimExtention); + strcat(outAnimation, "_reload_end"); + theIsReloading = true; + }; + break; + case ACT_RANGE_PRIME: + if(theCanCrouch && theIsCrouched) + { + strcpy(outAnimation, "crouch_" ); + } + strcat(outAnimation, this->m_szAnimExtention); + strcat(outAnimation, "_prime"); + break; + + case ACT_RANGE_ATTACK1: + if(theCanCrouch && theIsCrouched && !theIsAlien ) + { + strcpy(outAnimation, "crouch_" ); + } + strcat(outAnimation, this->m_szAnimExtention); + if(theIsAlien) + { + strcat(outAnimation, "_alien"); + } + else + { + strcat(outAnimation, "_fire"); + } + break; + + case ACT_HOP: + strcat(outAnimation, "jump"); + break; + + case ACT_WALK: + if(inGaitSequence || !strcmp(this->m_szAnimExtention, "")) + { + strcat(outAnimation, "walk"); + } + else + { + if(theCanCrouch && theIsCrouched) + { + strcpy(outAnimation, "crouch_" ); + } + strcat(outAnimation, this->m_szAnimExtention); + if(theCanCrouch) + { + if(theIsReloading) + { + strcat(outAnimation, "_reload"); + } + else + { + strcat(outAnimation, "_look"); + } + } + } + break; + + case ACT_RUN: + if(inGaitSequence || !strcmp(this->m_szAnimExtention, "")) + { + strcat(outAnimation, "run"); + } + else + { + if(theCanCrouch && theIsCrouched) + { + strcpy(outAnimation, "crouch_" ); + } + strcat(outAnimation, this->m_szAnimExtention); + if(theCanCrouch) + { + //if(theIsReloading) + //{ + // strcat(outAnimation, "_reload"); + //} + //else + //{ + if(theIsReloading) + { + strcpy(outAnimation, this->m_szAnimExtention); + strcat(outAnimation, "_reload"); + } + else + { + strcat(outAnimation, "_look"); + } + //} + } + } + break; + + case ACT_CROUCHIDLE: + if(theCanCrouch) + { + if(inGaitSequence) + { + strcat(outAnimation, "crouch_idle"); + } + } + break; + case ACT_CROUCH: + if(theCanCrouch) + { + if(inGaitSequence) + { + strcat(outAnimation, "crawl"); + } + else + { + } + } + break; + case ACT_IDLE: + // Weird hack/fix for gyrating and ready room spazzing + //if(this->GetPlayMode() != PLAYMODE_READYROOM) + //{ + if(inGaitSequence) + { + if(theIsReloading) + { + strcat(outAnimation, "_reload"); + } + else + { + strcat(outAnimation, "idle1"); + } + } + else + { + strcat(outAnimation, "look_idle"); + } + //} + break; + + case ACT_DIESIMPLE: + case ACT_DIEVIOLENT: + if(this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER3) + { + strcpy(outAnimation, "death1_die"); + } + else + { + if(theIsCrouched) + { + strcpy(outAnimation, "crouch_die"); + } + else + { + switch(RANDOM_LONG(0, 2)) + { + case 0: + strcpy(outAnimation, "death1_die"); + break; + case 1: + strcpy(outAnimation, "death2_die"); + break; + case 2: + strcpy(outAnimation, "death3_die"); + break; + } + } + } + theIsDeathAnim = true; + break; + case ACT_DIE_HEADSHOT: + strcpy(outAnimation, "head_die"); + theIsDeathAnim = true; + break; + case ACT_DIE_GUTSHOT: + strcpy(outAnimation, "gut_die"); + theIsDeathAnim = true; + break; + case ACT_DIEBACKWARD: + // Hack for skulk until artwork can be updated (it has no back_die) + if(this->pev->iuser3 != AVH_USER3_ALIEN_PLAYER1) + { + strcpy(outAnimation, "back_die"); + } + else + { + strcpy(outAnimation, "gut_die"); + } + theIsDeathAnim = true; + break; + case ACT_DIEFORWARD: + strcpy(outAnimation, "forward_die"); + theIsDeathAnim = true; + break; + case ACT_SWIM: + // die + strcpy(outAnimation, "treadwater"); + break; + } + + if(theIsGestating) + { + float theFullTimeToGestate = GetGameRules()->GetBuildTimeForMessageID(this->mEvolution); + bool theAlmostDoneGestation = (gpGlobals->time > (this->mTimeGestationStarted + theFullTimeToGestate*.75f)); + + switch(inActivity) + { + case ACT_SMALL_FLINCH: + case ACT_BIG_FLINCH: + case ACT_FLINCH_HEAD: + case ACT_FLINCH_CHEST: + case ACT_FLINCH_STOMACH: + case ACT_FLINCH_LEFTARM: + case ACT_FLINCH_RIGHTARM: + case ACT_FLINCH_LEFTLEG: + case ACT_FLINCH_RIGHTLEG: + if(RANDOM_LONG(0, 1)) + { + // flinch1 + strcpy(outAnimation, "flinch1"); + } + else + { + // flinch2 + strcpy(outAnimation, "flinch2"); + } + break; + + case ACT_DIESIMPLE: + case ACT_DIEBACKWARD: + case ACT_DIEFORWARD: + case ACT_DIEVIOLENT: + // die + strcpy(outAnimation, "die"); + theIsDeathAnim = true; + break; + + default: + if(theAlmostDoneGestation) + { + // gestation_fast + strcpy(outAnimation, "gestation_fast"); + } + else + { + // gestation + strcpy(outAnimation, "gestation"); + } + break; + } + } + +#ifdef DEBUG + if(theDebugAnimations && theIsDeathAnim) + { + char theMessage[256]; + sprintf(theMessage, "AvHPlayer::GetAnimationForActivity(death) returned %s\n", outAnimation); + ALERT(at_console, theMessage); + } +#endif +} + +bool AvHPlayer::GetCanGestate(AvHMessageID inMessageID, string& outErrorMessage) +{ + bool theCanGestate = false; + + int theNumHives = this->GetTeamPointer()->GetNumActiveHives(); + + if(this->pev->iuser3 != AVH_USER3_ALIEN_EMBRYO) + { + if(this->mDigestee <= 0) + { + bool theIsRangeRequirementMet = true; + + if(theIsRangeRequirementMet) + { + theCanGestate = true; + } + else + { + // TODO: Add error message + } + } + else + { + outErrorMessage = kNotWhileDigesting; + } + } + + return theCanGestate; +} + +bool AvHPlayer::GetCanCommand(string& outErrorMessage) +{ + bool theCanCommand = false; + + // Jack up command station use time to avoid huge kick-everyone-off-server bug (I think this is an overflow issue) + // I think the overflow issue is fixed, but design-wise, this prevents annoying pop-out/pop-back in attacking (Reaver popping) + const int theCommandStationReuseTime = BALANCE_VAR(kCommandStationReuseTime); + + // Have we been banned from the command station? + AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); + if(!theServerPlayerData || (theServerPlayerData->GetTimeVotedDown() == -1)) + { + //if(!this->GetCurrentWeaponCannotHolster()) + if(this->m_pActiveItem) + { + CBasePlayerWeapon* theCurrentWeapon = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); + if(theCurrentWeapon && theCurrentWeapon->CanHolster()) + { + float theLastTime = this->GetLastTimeInCommandStation(); + if((theLastTime == -1) || (gpGlobals->time > (theLastTime + theCommandStationReuseTime))) + { + if(!this->GetIsEnsnared()) + { + theCanCommand = true; + } + } + else + { + outErrorMessage = kCommandStationWaitTime; + } + } + else + { + outErrorMessage = kWeaponPreventingCommandStation; + } + } + } + else + { + outErrorMessage = kBannedFromCommand; + } + + return theCanCommand; +} + +bool AvHPlayer::GetCanReceiveResources() const +{ + bool theCanReceiveResources = true; + + if(this->GetIsAlien()) + { + theCanReceiveResources = false; + + if(this->GetIsRelevant() && this->IsAlive()) + { + theCanReceiveResources = true; + } + } + + return theCanReceiveResources; +} + +bool AvHPlayer::GetCanUseHive() const { + return (gpGlobals->time > this->mTimeOfLastHiveUse + 0.4f ); +} + +void AvHPlayer::SetTimeOfLastHiveUse(float time) { + this->mTimeOfLastHiveUse=time; +} + +int AvHPlayer::GetEffectivePlayerClass() +{ + AvHPlayerClass theEffectivePlayerClass = PLAYERCLASS_NONE; + + if(this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) + { + theEffectivePlayerClass = PLAYERCLASS_COMMANDER; + } + // Players can be both reinforcing and observer, but reinforcing takes precedence + else if(this->GetPlayMode() == PLAYMODE_REINFORCING) + { + theEffectivePlayerClass = PLAYERCLASS_REINFORCING; + } + // Digesting overrides other states + else if(this->GetIsBeingDigested()) + { + theEffectivePlayerClass = PLAYERCLASS_ALIVE_DIGESTING; + } + else if((this->pev->team == 0) && this->IsObserver()) + { + theEffectivePlayerClass = PLAYERCLASS_SPECTATOR; + } + else + { + // Check team type + if(this->GetIsMarine()) + { + if(this->IsAlive()) + { + if(this->GetHasHeavyArmor()) + { + theEffectivePlayerClass = PLAYERCLASS_ALIVE_HEAVY_MARINE; + } + else if (this->GetHasJetpack()) { + theEffectivePlayerClass = PLAYERCLASS_ALIVE_JETPACK_MARINE; + } + else + { + theEffectivePlayerClass = PLAYERCLASS_ALIVE_MARINE; + } + } + else + { + theEffectivePlayerClass = PLAYERCLASS_DEAD_MARINE; + } + } + else if(this->GetIsAlien()) + { + if(this->IsAlive()) + { + switch(this->pev->iuser3) + { + case AVH_USER3_ALIEN_PLAYER1: + theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL1; + break; + case AVH_USER3_ALIEN_PLAYER2: + theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL2; + break; + case AVH_USER3_ALIEN_PLAYER3: + theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL3; + break; + case AVH_USER3_ALIEN_PLAYER4: + theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL4; + break; + case AVH_USER3_ALIEN_PLAYER5: + theEffectivePlayerClass = PLAYERCLASS_ALIVE_LEVEL5; + break; + case AVH_USER3_ALIEN_EMBRYO: + theEffectivePlayerClass = PLAYERCLASS_ALIVE_GESTATING; + break; + } + } + else + { + theEffectivePlayerClass = PLAYERCLASS_DEAD_ALIEN; + } + } + } + + return theEffectivePlayerClass; +} + +AvHServerPlayerData* AvHPlayer::GetServerPlayerData() +{ + AvHServerPlayerData* theServerPlayerData = NULL; + + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + theServerPlayerData = theTeam->GetServerPlayerData(this->edict()); + } + + return theServerPlayerData; +} + +#ifdef USE_OLDAUTH +int AvHPlayer::GetAuthenticationMask() +{ + int theMask = 0; + + // Get WON id + if(this->GetAllowAuth()) + { + // Use cached value if valid + theMask = this->mCachedAuthenticationMask; + + // Look it up if uninitialized or Steam isn't ready yet + if((theMask == -1) || (theMask == PLAYERAUTH_PENDING)) + { + string theAuthID = AvHSUGetPlayerAuthIDString(this->edict()); + theMask = GetGameRules()->GetAuthenticationMask(theAuthID); + + // Save cached value + this->mCachedAuthenticationMask = theMask; + } + } + + bool theAllowAuthCheatMask = GetGameRules()->GetCheatsEnabled(); + + #ifdef AVH_LAN_PLAYTEST_BUILD + theAllowAuthCheatMask = true; + #endif + + if(theAllowAuthCheatMask) + { + theMask |= this->mAuthCheatMask; + } + return theMask; +} + +bool AvHPlayer::GetAllowAuth() const +{ + return (this->mAllowAuth && (avh_uplink.value > 0)); +} + + +void AvHPlayer::SetAllowAuth(bool inAllowAuth) +{ + this->mAllowAuth = inAllowAuth; +} + +void AvHPlayer::SetAuthCheatMask(int inAuthCheatMask) +{ + this->mAuthCheatMask = inAuthCheatMask; +} + +#endif + +bool AvHPlayer::GetCurrentWeaponCannotHolster() const +{ + bool theCannotHolster = false; + + if(m_pActiveItem) + { + theCannotHolster = !this->m_pActiveItem->CanHolster(); + } + + return theCannotHolster; +} + +bool AvHPlayer::GetInReadyRoom() const +{ + return (this->GetPlayMode() == PLAYMODE_READYROOM); +} + +bool AvHPlayer::GetIsAlien(bool inIncludeSpectating) const +{ + bool theIsAlien = false; + AvHTeam* theTeam = this->GetTeamPointer(inIncludeSpectating); + + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN)) + { + theIsAlien = true; + } + + return theIsAlien; +} + +bool AvHPlayer::GetIsMarine(bool inIncludeSpectating) const +{ + bool theIsMarine = false; + AvHTeam* theTeam = this->GetTeamPointer(inIncludeSpectating); + + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) + { + theIsMarine = true; + } + + return theIsMarine; +} + +bool AvHPlayer::GetIsInTopDownMode(bool inIncludeSpectating) const +{ + return this->mInTopDownMode; +} + +AvHPlayer* AvHPlayer::GetCommander(void) +{ + AvHPlayer* theCommander = NULL; + AvHTeam* theMarinesTeam = this->GetTeamPointer(); + if(theMarinesTeam) + { + int theCommanderIndex = theMarinesTeam->GetCommander(); + AvHSUGetEntityFromIndex(theCommanderIndex, theCommander); + } + + return theCommander; +} + +bool AvHPlayer::GetHasBeenSpectator(void) const +{ + return this->mHasBeenSpectator; +} + +AvHPlayMode AvHPlayer::GetPlayMode(bool inIncludeSpectating) const +{ + int theVarToUse = this->pev->playerclass; + + if(inIncludeSpectating && (this->pev->iuser1 == OBS_IN_EYE)) + { + AvHPlayer* theEntity = NULL; + if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) + { + theVarToUse = theEntity->pev->playerclass; + } + } + + return (AvHPlayMode)theVarToUse; +} + +bool AvHPlayer::GetIsValidReinforcementFor(AvHTeamNumber inTeam) const +{ + bool theSuccess = false; + + if(this->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) + { + if(this->pev->team == inTeam) + { + theSuccess = true; + } + } + + return theSuccess; +} + +int AvHPlayer::GetPointValue(void) const +{ + int thePointValue = BALANCE_VAR(kScoringKillPlayer); + + if(this->GetIsAlien()) + { + switch(this->pev->iuser3) + { + case AVH_USER3_ALIEN_PLAYER2: + thePointValue = BALANCE_VAR(kScoringKillPlayerGorge); + break; + case AVH_USER3_ALIEN_PLAYER3: + thePointValue = BALANCE_VAR(kScoringKillPlayerLerk); + break; + case AVH_USER3_ALIEN_PLAYER4: + thePointValue = BALANCE_VAR(kScoringKillPlayerFade); + break; + case AVH_USER3_ALIEN_PLAYER5: + thePointValue = BALANCE_VAR(kScoringKillPlayerOnos); + break; + } + } + else if(this->GetIsMarine()) + { + if(this->GetHasJetpack()) + { + thePointValue = BALANCE_VAR(kScoringKillPlayerJetpack); + } + else if(this->GetHasHeavyArmor()) + { + thePointValue = BALANCE_VAR(kScoringKillPlayerHA); + } + } + + return thePointValue; +} + +vec3_t AvHPlayer::GetVisualOrigin() const +{ + vec3_t theOrigin = this->pev->origin; + + //theOrigin[2] += this->pev->view_ofs[2]; + if(this->mInTopDownMode) + { + theOrigin[2] = GetGameRules()->GetMapExtents().GetMaxViewHeight(); + } + + return theOrigin; +} + + +int AvHPlayer::GetRespawnCost() const +{ + int theRespawnCost = 0; + const AvHGameplay& theGameplay = GetGameRules()->GetGameplay(); + + if(this->GetClassType() == AVH_CLASS_TYPE_MARINE) + { + // This function shouldn't be used now that there are reinforcements...rework a bit? + theRespawnCost = 0; + } + else if(this->GetClassType() == AVH_CLASS_TYPE_ALIEN) + { + theRespawnCost = theGameplay.GetAlienRespawnCost(); + } + return theRespawnCost; +} + +bool AvHPlayer::GetHasItem(const char *szName) +{ + bool theHasItem = false; + + for(int i = 0; (i < MAX_ITEM_TYPES) && !theHasItem; i++) + { + CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; + while(theCurrentItem && !theHasItem) + { + if(FStrEq(STRING(theCurrentItem->pev->classname), szName)) + { + theHasItem = true; + } + theCurrentItem = theCurrentItem->m_pNext; + } + } + + return theHasItem; +} + +void AvHPlayer::GiveNamedItem(const char *szName, bool inSendMessage) +{ + // Check to make sure we don't have this weapon before giving it. Fixes potential problems where + // weapon settings would be reset if a duplicate weapon was given + if(!this->GetHasItem(szName)) + { + // Send visible pickup message after game has started + bool theSendMessage = GetGameRules()->GetGameStarted(); + CBasePlayer::GiveNamedItem(szName, theSendMessage); + } +} + +int AvHPlayer::GetNumberOfItems() +{ + int theNumItems = 0; + + for(int i = 0; i < MAX_ITEM_TYPES; i++) + { + CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; + while(theCurrentItem) + { + theNumItems++; + theCurrentItem = theCurrentItem->m_pNext; + } + } + + return theNumItems; +} + +void AvHPlayer::GiveResources(float inResources) +{ + this->SetResources(this->GetResources() + inResources); +} + +void AvHPlayer::StartLeap() +{ + // Make sure player has leap + if(this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER1) + { + this->mTimeLeapEnd = gpGlobals->time + kLeapDuration; + } +} + +void AvHPlayer::PlayerTouch(CBaseEntity* inOther) +{ + if(this && this->pev && inOther && this->m_pActiveItem && this->m_pActiveItem->pev) + { + // Uncloak when we touch enemy players to prevent blocking + if(inOther->IsPlayer() && inOther->IsAlive() && (inOther->pev->team != this->pev->team)) + { + this->Uncloak(); + } + + // Don't do "touch" damage too quickly + float theTouchDamageInterval = BALANCE_VAR(kTouchDamageInterval); + if((this->mTimeOfLastTouchDamage == -1) || (gpGlobals->time > (this->mTimeOfLastTouchDamage + theTouchDamageInterval))) + { + entvars_t* theAttacker = this->pev; + entvars_t* theInflictor = this->m_pActiveItem->pev; + + float theScalar = 1.0f; + if((this->mTimeLeapEnd != -1) && (gpGlobals->time < this->mTimeLeapEnd) && inOther->pev->iuser3 != AVH_USER3_HIVE ) + { + if ( this->m_rgpPlayerItems[3] ) + theInflictor = this->m_rgpPlayerItems[3]->pev; + // Do damage to entity + if(GetGameRules()->CanEntityDoDamageTo(this, inOther, &theScalar) && (inOther->pev->team != this->pev->team)) + { + float theDamage = BALANCE_VAR(kLeapDamage)*theScalar*theTouchDamageInterval; + inOther->TakeDamage(theInflictor, theAttacker, theDamage, NS_DMG_NORMAL); + + this->mTimeOfLastTouchDamage = gpGlobals->time; + } + } + + // Are we charging? +/* else if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT) && this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER5) + { + if ( this->m_rgpPlayerItems[4] ) + theInflictor = this->m_rgpPlayerItems[4]->pev; + // Don't do friendly fire + if(GetGameRules()->CanEntityDoDamageTo(this, inOther, &theScalar)) + { + float theDamage = BALANCE_VAR(kChargeDamage)*theScalar*theTouchDamageInterval; + + inOther->TakeDamage(theInflictor, theAttacker, theDamage, NS_DMG_NORMAL); + + if(inOther->IsPlayer() && !inOther->IsAlive()) + { + EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, kChargeKillSound, 1.0, ATTN_NORM); + } + + this->mTimeOfLastTouchDamage = gpGlobals->time; + } + } + */ + } + } +} + +AvHTeamNumber AvHPlayer::GetTeam(bool inIncludeSpectating) const +{ + AvHTeamNumber theTeamNumber = (AvHTeamNumber)this->pev->team; + + if(inIncludeSpectating) + { + CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + theTeamNumber = (AvHTeamNumber)theSpectatingEntity->pev->team; + } + } + + return theTeamNumber; +} + +AvHTeam* AvHPlayer::GetTeamPointer(bool inIncludeSpectating) const +{ + AvHTeamNumber theTeamNumber = this->GetTeam(inIncludeSpectating); + + AvHTeam* theTeamPointer = GetGameRules()->GetTeam(theTeamNumber); + + return theTeamPointer; +} + +float AvHPlayer::GetKilledX() const +{ + return this->mKilledX; +} + +float AvHPlayer::GetKilledY() const +{ + return this->mKilledY; +} + +AvHClassType AvHPlayer::GetClassType(void) const +{ + AvHClassType theClassType = AVH_CLASS_TYPE_UNDEFINED; + + AvHTeamNumber theTeamNumber = (AvHTeamNumber)(this->pev->team); + const AvHTeam* theTeam = GetGameRules()->GetTeam(theTeamNumber); + if(theTeam) + { + theClassType = theTeam->GetTeamType(); + } + + ASSERT(theClassType >= AVH_CLASS_TYPE_UNDEFINED); + ASSERT(theClassType <= AVH_CLASS_TYPE_AUTOASSIGN); + + return theClassType; +} + + +bool AvHPlayer::GetPurchaseAllowed(AvHMessageID inUpgrade, int& outCost, string* outErrorMessage) const +{ + bool thePurchaseAllowed = false; + bool theGameStarted = GetGameRules()->GetGameStarted(); + bool theIsMarine = this->GetTeamPointer()->GetTeamType() == AVH_CLASS_TYPE_MARINE; + bool theIsAlien = this->GetIsAlien(); + int theNumHives = this->GetTeamPointer()->GetNumActiveHives(); + bool theIsBuilder = (this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER2); + bool theIsResearchTech = AvHSHUGetIsResearchTech(inUpgrade); + bool theIsCombatMode = GetGameRules()->GetIsCombatMode(); + string theErrorMessage; + + // Check tech tree first + const AvHTechTree* theTechNodes = &this->GetTeamPointer()->GetTechNodes(); + + // If combat, use Combat nodes + if(GetGameRules()->GetIsCombatMode()) + { + theTechNodes = &this->mCombatNodes; + } + + if(theTechNodes->GetIsMessageAvailable(inUpgrade)) + { + if(theIsResearchTech && (this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) && theIsMarine) + { + thePurchaseAllowed = true; + } + else + { + if(theIsCombatMode && (this->GetExperienceLevel() <= (this->mExperienceLevelsSpent + GetGameRules()->GetCostForMessageID(inUpgrade)))) + { + return false; + } + + switch(inUpgrade) + { + case ALIEN_EVOLUTION_ONE: + case ALIEN_EVOLUTION_TWO: + case ALIEN_EVOLUTION_THREE: + //case ALIEN_EVOLUTION_FOUR: + //case ALIEN_EVOLUTION_FIVE: + //case ALIEN_EVOLUTION_SIX: + case ALIEN_EVOLUTION_SEVEN: + case ALIEN_EVOLUTION_EIGHT: + case ALIEN_EVOLUTION_NINE: + case ALIEN_EVOLUTION_TEN: + case ALIEN_EVOLUTION_ELEVEN: + case ALIEN_EVOLUTION_TWELVE: + // If we are an alien and we don't have the upgrade + if(theIsAlien) + { + AvHUpgradeMask theUpgradeMask = MASK_NONE; + if(AvHGetAlienUpgradeMask(inUpgrade, theUpgradeMask)) + { + if(!GetHasUpgrade(this->pev->iuser4, theUpgradeMask)) + { + AvHAlienUpgradeCategory theCategory = ALIEN_UPGRADE_CATEGORY_INVALID; + if(AvHGetAlienUpgradeCategory(inUpgrade, theCategory)) + { + // Now make sure we have an unspent upgrade available + AvHTeam* theTeamPointer = this->GetTeamPointer(); + if(theTeamPointer && (AvHGetHasFreeUpgradeCategory(theCategory, theTeamPointer->GetAlienUpgrades(), this->pev->iuser4) || GetGameRules()->GetIsCombatMode())) + { + thePurchaseAllowed = true; + } + else + { + theErrorMessage = kUpgradeNotAvailable; + } + } + } + } + } + break; + + case ALIEN_LIFEFORM_ONE: + if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER1)) + { + thePurchaseAllowed = true; + } + break; + case ALIEN_LIFEFORM_TWO: + if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER2)) + { + thePurchaseAllowed = true; + } + break; + case ALIEN_LIFEFORM_THREE: + if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER3)) + { + // if(theNumHives >= 1) + // { + thePurchaseAllowed = true; + // } + // else + // { + // outErrorMessage = kNeedOneHiveToGestate; + // } + } + break; + case ALIEN_LIFEFORM_FOUR: + if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER4)) + { + // if(theNumHives >= 2) + // { + thePurchaseAllowed = true; + // } + // else + // { + // outErrorMessage = kNeedTwoHivesToGestate; + // } + } + break; + case ALIEN_LIFEFORM_FIVE: + if(theIsAlien && theGameStarted && (this->GetUser3() != AVH_USER3_ALIEN_PLAYER5)) + { + // if(theNumHives >= 3) + // { + thePurchaseAllowed = true; + // } + // else + // { + // outErrorMessage = kNeedThreeHivesToGestate; + // } + } + break; + + case ALIEN_BUILD_RESOURCES: + case ALIEN_BUILD_HIVE: + if(theIsBuilder) + { + thePurchaseAllowed = true; + } + else + { + theErrorMessage = kMustBeBuilder; + } + break; + + case ALIEN_BUILD_OFFENSE_CHAMBER: + if(theIsBuilder) + { + if(theNumHives >= 1) + { + thePurchaseAllowed = true; + } + else + { + theErrorMessage = kNeedOneHiveForStructure; + } + } + else + { + theErrorMessage = kMustBeBuilder; + } + break; + + // Make sure we have a hive that can provide this tech + case ALIEN_BUILD_DEFENSE_CHAMBER: + case ALIEN_BUILD_MOVEMENT_CHAMBER: + case ALIEN_BUILD_SENSORY_CHAMBER: + if(theIsBuilder) + { + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if(theEntity && theEntity->GetIsActive() && (theEntity->pev->team == this->pev->team)) + { + AvHMessageID theTechnology = theEntity->GetTechnology(); + if((theTechnology == inUpgrade) || (theTechnology == MESSAGE_NULL)) + { + thePurchaseAllowed = true; + break; + } + } + END_FOR_ALL_ENTITIES(kesTeamHive); + + if(!thePurchaseAllowed) + { + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + // int theNumHives = theTeam->GetNumActiveHives(); + // switch(theNumHives) + // { + // case 0: + // outErrorMessage = kNeedOneHiveForStructure; + // break; + // case 1: + // outErrorMessage = kNeedTwoHivesForStructure; + // break; + // case 2: + // outErrorMessage = kNeedThreeHivesForStructure; + // break; + // } + + theErrorMessage = kNeedsAnotherHiveForStructure; + } + } + } + else + { + theErrorMessage = kMustBeBuilder; + } + break; + + default: + thePurchaseAllowed = true; + break; + } + } + } + + if(thePurchaseAllowed) + { + // This is either resources, energy, or levels + int theCost = GetGameRules()->GetCostForMessageID(inUpgrade); + + if(GetGameRules()->GetIsCombatMode()) + { + int theLevelsFree = max(this->GetExperienceLevel() - this->GetExperienceLevelsSpent() - 1, 0); + if(theCost > theLevelsFree) + { + thePurchaseAllowed = false; + } + } + else + { + if(AvHSHUGetDoesTechCostEnergy(inUpgrade) || GetGameRules()->GetIsCheatEnabled(kcLowCost)) + { + theCost = 0; + } + + if(this->GetResources() < theCost) + { + thePurchaseAllowed = false; + } + } + + outCost = theCost; + } + + if(outErrorMessage) + { + *outErrorMessage = theErrorMessage; + } + + return thePurchaseAllowed; +} + +int AvHPlayer::GiveAmmo( int iAmount, char *szName, int iMax ) +{ + int theReturnValue = -1; + int theRealAmount = iAmount; + string theRealName = szName; + char theRealNameArray[256]; + int theRealMax = iMax; + + // TODO: Change this to a more generic call on AvHBasePlayerWeapon for next client update + AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem); + if(theBaseWeapon != NULL && theBaseWeapon->GetCanBeResupplied()) + { + if(FStrEq(szName, kwsGenericAmmo)) + { + AvHBasePlayerWeapon* theWeaponToGiveTo = theBaseWeapon; + if(theBaseWeapon->UsesAmmo()) + { + // Translate iAmount, szName and iMax into the ammo for our current weapon + ItemInfo theItemInfo; + if(this->m_pActiveItem->GetItemInfo(&theItemInfo) != 0) + { + //theRealAmount = theBaseWeapon->m_iDefaultAmmo; + theRealAmount = theItemInfo.iMaxClip; + theRealName = theItemInfo.pszAmmo1; + theRealMax = theItemInfo.iMaxAmmo1; + } + } + } + } + strcpy(theRealNameArray, theRealName.c_str()); + theReturnValue = CBasePlayer::GiveAmmo(theRealAmount, theRealNameArray, theRealMax); + + return theReturnValue; +} + + +bool AvHPlayer::GetShouldResupplyAmmo() +{ + bool theResupply = false; + + if(this->GetIsMarine() && this->GetIsRelevant()) + { + AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem); + if(theBaseWeapon != NULL && (theBaseWeapon->m_iId != AVH_WEAPON_MINE)) + { + AvHBasePlayerWeapon* theWeaponToGiveTo = theBaseWeapon; + if(theBaseWeapon->UsesAmmo()) + { + // Translate iAmount, szName and iMax into the ammo for our current weapon + ItemInfo theItemInfo; + if(this->m_pActiveItem->GetItemInfo(&theItemInfo) != 0) + { + int theMaxAmmo = theItemInfo.iMaxAmmo1; + if(theMaxAmmo > 0) + { + int i = GetAmmoIndex(theItemInfo.pszAmmo1); + + if ((i > 0) && (i < MAX_AMMO_SLOTS)) + { + + int theCurrentAmmo = this->m_rgAmmo[i]; + + if (theCurrentAmmo < theWeaponToGiveTo->GetClipSize()) { + theResupply = true; + } + } + } + } + } + } + } + return theResupply; +} + +float AvHPlayer::GetCurrentWeaponAmmoPercentage() +{ + float thePercentage = 1.0f; + + if(this->GetIsMarine() && this->GetIsRelevant()) + { + AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem); + if(theBaseWeapon != NULL && (theBaseWeapon->m_iId != AVH_WEAPON_MINE)) + { + AvHBasePlayerWeapon* theWeaponToGiveTo = theBaseWeapon; + if(theBaseWeapon->UsesAmmo()) + { + // Translate iAmount, szName and iMax into the ammo for our current weapon + ItemInfo theItemInfo; + if(this->m_pActiveItem->GetItemInfo(&theItemInfo) != 0) + { + int theMaxAmmo = theItemInfo.iMaxAmmo1; + if(theMaxAmmo > 0) + { + int i = GetAmmoIndex(theItemInfo.pszAmmo1); + + if ((i > 0) && (i < MAX_AMMO_SLOTS)) + { + int theCurrentAmmo = this->m_rgAmmo[i];// + theWeaponToGiveTo->GetShotsInClip(); + thePercentage = (float)theCurrentAmmo/theMaxAmmo; + } + } + } + } + } + } + + return thePercentage; +} + +bool AvHPlayer::GetEnemySighted(void) const +{ + return this->mEnemySighted; +} + +bool AvHPlayer::GetIsFiring(void) const +{ + bool theIsFiring = false; + if(this->pev->button & IN_ATTACK) + { + AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); + if(theWeapon && theWeapon->m_iClip > 0) + { + theIsFiring = true; + } + } + return theIsFiring; +} + +bool AvHPlayer::GetIsInOverwatch(void) const +{ + return this->mInOverwatch; +} + +bool AvHPlayer::GetIsSpectatingTeam(AvHTeamNumber inTeamNumber) const +{ + bool theIsSpectatingTeam = false; + + CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + if((AvHTeamNumber)theSpectatingEntity->pev->team == inTeamNumber) + { + theIsSpectatingTeam = true; + } + } + + return theIsSpectatingTeam; +} + +bool AvHPlayer::GetIsSpectatingPlayer(int inPlayerNumber) const +{ + bool theIsSpectatingPlayer = false; + + CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + if(theSpectatingEntity->entindex() == inPlayerNumber) + { + theIsSpectatingPlayer = true; + } + } + + return theIsSpectatingPlayer; +} + +bool AvHPlayer::GetIsSpeaking(void) const +{ + return this->mIsSpeaking; +} + +AvHMessageID AvHPlayer::GetLastSaying() const +{ + return this->mLastSaying; +} + +bool AvHPlayer::GetOrdersRequested(void) const +{ + return this->mOrdersRequested; +} + +bool AvHPlayer::GetOrderAcknowledged(void) const +{ + return this->mOrderAcknowledged; +} + +string AvHPlayer::GetPlayerName() const +{ + string thePlayerName = (this->pev->netname && STRING(this->pev->netname)[0] != 0 ) ? STRING(this->pev->netname) : "unconnected" ; + return thePlayerName; +} + +int AvHPlayer::GetRelevantWeight(void) const +{ + float theRelevantWeight = 0; + + // Add current weapon if any + //CBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_pActiveItem); + //if(theActiveWeapon) + //{ + // theRelevantWeight += this->GetRelevantWeightForWeapon(theActiveWeapon); + //} + + // Run through items and tally up the ones that we count + for(int i = 0; i< MAX_ITEM_TYPES; i++) + { + AvHBasePlayerWeapon* theCurrentWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); + while(theCurrentWeapon) + { + int theWeight = this->GetRelevantWeightForWeapon(theCurrentWeapon); + + // Active items count full, count less when stowed + float theMultiplier = (theCurrentWeapon == this->m_pActiveItem) ? 1.0f : .7f; + theRelevantWeight += theWeight*theMultiplier; + theCurrentWeapon = dynamic_cast(theCurrentWeapon->m_pNext); + } + } + + return (int)(theRelevantWeight); +} + +int AvHPlayer::GetRelevantWeightForWeapon(AvHBasePlayerWeapon* inWeapon) const +{ + ASSERT(inWeapon != NULL); + + AvHWeaponID theWeaponID = (AvHWeaponID)inWeapon->m_iId; + int theNumRounds = 0; + if(inWeapon->UsesAmmo()) + { + int theAmmoIndex = inWeapon->PrimaryAmmoIndex(); + ASSERT(theAmmoIndex >= 0); + ASSERT(theAmmoIndex < MAX_AMMO_SLOTS); + theNumRounds = max(inWeapon->m_iClip + this->m_rgAmmo[theAmmoIndex], 0); + } + + return GetGameRules()->GetWeightForItemAndAmmo(theWeaponID, theNumRounds); +} + +AvHUser3 AvHPlayer::GetPreviousUser3(bool inIncludeSpectating) const +{ + + AvHUser3 theUser3 = this->mPreviousUser3; + + if(inIncludeSpectating) + { + const CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + const AvHPlayer* theSpectatingPlayer = dynamic_cast(theSpectatingEntity); + if(theSpectatingPlayer) + { + theUser3 = theSpectatingPlayer->GetPreviousUser3(); + } + } + } + + return theUser3; + +} + +AvHUser3 AvHPlayer::GetUser3(bool inIncludeSpectating) const +{ + AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; + + if(inIncludeSpectating) + { + const CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + const AvHPlayer* theSpectatingPlayer = dynamic_cast(theSpectatingEntity); + if(theSpectatingPlayer) + { + theUser3 = theSpectatingPlayer->GetUser3(); + } + } + } + + return theUser3; +} + +AvHMessageID AvHPlayer::GetEvolution(bool inIncludeSpectating) const +{ + + AvHMessageID theEvolution = mEvolution; + + if(inIncludeSpectating) + { + const CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + const AvHPlayer* theSpectatingPlayer = dynamic_cast(theSpectatingEntity); + if(theSpectatingPlayer) + { + theEvolution = theSpectatingPlayer->GetEvolution(); + } + } + } + + return theEvolution; + +} + +bool AvHPlayer::GetSpecialPASOrigin(Vector& outOrigin) +{ + bool theUseSpecial = (this->GetUser3() == AVH_USER3_COMMANDER_PLAYER); + + if(theUseSpecial) + { + VectorCopy(this->mSpecialPASOrigin, outOrigin); + } + + return theUseSpecial; +} + +// Don't check validity. ASSERT if an error is encountered. +void AvHPlayer::GiveUpgrade(AvHMessageID inUpgrade) +{ +} + +void AvHPlayer::GiveTeamUpgrade(AvHMessageID inUpgrade, bool inGive) +{ + AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; + int thePlayerLevel = this->GetExperienceLevel(); + float theHealthPercentage = this->pev->health/AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, thePlayerLevel); + float theArmorPercentage = this->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); + + // If we're in combat mode, handle upgrades differently + + ProcessGenericUpgrade(this->pev->iuser4, inUpgrade, inGive); + + //AvHTeam* theTeam = this->GetTeamPointer(); + //if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) + //{ + AvHUpgradeMask theMask = ProcessGenericUpgrade(this->pev->iuser4, inUpgrade, inGive); + + if(inUpgrade == BUILD_HEAVY) + { + this->EffectivePlayerClassChanged(); + } + //} + + this->pev->health = theHealthPercentage*AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, theUser3, thePlayerLevel); + this->pev->armorvalue = theArmorPercentage*AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, theUser3); +} + +float AvHPlayer::GetResources(bool inIncludeSpectating) const +{ + float theResources = this->mResources; + + if(inIncludeSpectating && (this->pev->iuser1 == OBS_IN_EYE)) + { + AvHPlayer* theEntity = NULL; + if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) + { + theResources = theEntity->GetResources(false); + } + } + + const AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + if(theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) + { + theResources = theTeam->GetTeamResources(); + } + } + + return theResources; +} + +void AvHPlayer::GetSpeeds(int& outBaseSpeed, int& outUnemcumberedSpeed) const +{ + // Use marine speed when in ready room (ie, AVH_CLASS_TYPE_UNDEFINED) + //int theBaseSpeed = 160; + // Counter-strike speeds are around 260 for knife running, and 215 with the para (according to Adrian's memory) + int theBaseSpeed = BALANCE_VAR(kBasePlayerSpeed); + int theUnencumberedSpeed = BALANCE_VAR(kUnencumberedPlayerSpeed); + + if(this->IsObserver()) + { + theBaseSpeed = theUnencumberedSpeed = 1000; + } + else if(this->GetClassType() == AVH_CLASS_TYPE_MARINE) + { + if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) + { + if(this->mInTopDownMode) + { + // For scrolling + theBaseSpeed = theUnencumberedSpeed = BALANCE_VAR(kCommanderPlayerSpeed); + } + } + else if(this->GetHasHeavyArmor()) + { + float theHeavyMultiplier = BALANCE_VAR(kHeavySpeedMultiplier); + theBaseSpeed *= theHeavyMultiplier; + theUnencumberedSpeed *= theHeavyMultiplier; + } + else if(this->GetHasJetpack()) + { + float theJetpackMultiplier = BALANCE_VAR(kJetpackSpeedMultiplier); + theBaseSpeed *= theJetpackMultiplier; + theUnencumberedSpeed *= theJetpackMultiplier; + } + + if(GetHasUpgrade(this->pev->iuser4, MASK_BUFFED)) + { + const float kStimpackSpeedMultiplier = 1 + BALANCE_VAR(kCatalystSpeedIncrease); + theBaseSpeed *= kStimpackSpeedMultiplier; + theUnencumberedSpeed *= kStimpackSpeedMultiplier; + } + } + else if(this->GetClassType() == AVH_CLASS_TYPE_ALIEN) + { + int theSpeedUpgradeLevel = 0; + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_4)) + { + theSpeedUpgradeLevel = 1; + + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_12)) + { + theSpeedUpgradeLevel = 2; + } + else if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_13)) + { + theSpeedUpgradeLevel = 3; + } + } + + // When gestating + float theAlienBaseSpeed = 0; + int theSpeedUpgradeAmount = BALANCE_VAR(kAlienCelerityBonus); + const float kChargingFactor = 2.0f; + + switch(this->pev->iuser3) + { + // Take into account speed upgrade, if this player has it + case AVH_USER3_ALIEN_PLAYER1: + theAlienBaseSpeed = BALANCE_VAR(kSkulkBaseSpeed); + break; + + case AVH_USER3_ALIEN_PLAYER2: + theAlienBaseSpeed = BALANCE_VAR(kGorgeBaseSpeed); + break; + + case AVH_USER3_ALIEN_PLAYER3: + theAlienBaseSpeed = BALANCE_VAR(kLerkBaseSpeed); + + // Compensate for airpseed multiplier, so lerk gets proper speed increase in main mode of locomotion + //theSpeedUpgradeAmount /= BALANCE_VAR(kAirspeedMultiplier); + break; + + case AVH_USER3_ALIEN_PLAYER4: + theAlienBaseSpeed = BALANCE_VAR(kFadeBaseSpeed); + break; + + case AVH_USER3_ALIEN_PLAYER5: + //theAlienBaseSpeed = this->mMaxGallopSpeed; + theAlienBaseSpeed = BALANCE_VAR(kOnosBaseSpeed); + +// if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT)) +// { +// theAlienBaseSpeed *= kChargingFactor; +// theSpeedUpgradeAmount *= kChargingFactor; +// } + break; + } + + theUnencumberedSpeed = theBaseSpeed = theAlienBaseSpeed + (theSpeedUpgradeLevel*theSpeedUpgradeAmount); + + // Update kMaxPlayerSpeed to make sure footsteps are played correctly + ASSERT(theUnencumberedSpeed <= kMaxGroundPlayerSpeed); + } + + outBaseSpeed = theBaseSpeed; + outUnemcumberedSpeed = theUnencumberedSpeed; + + // Take into account ensnare + if(this->GetIsEnsnared()) + { + float thePercentageComplete = (gpGlobals->time - this->mLastTimeEnsnared)/(this->mTimeToBeUnensnared - this->mLastTimeEnsnared); + thePercentageComplete = max(min(thePercentageComplete, 1.0f), 0.0f); + //ASSERT(thePercentageComplete >= 0.0f); + //ASSERT(thePercentageComplete <= 1.0f); + + float theSpeedFactor = min(BALANCE_VAR(kEnsnareMinimumSpeedFactor) + (1.0f - BALANCE_VAR(kEnsnareMinimumSpeedFactor))*thePercentageComplete, BALANCE_VAR(kEnsnareMaximumSpeedFactor)); + ASSERT(theSpeedFactor >= BALANCE_VAR(kEnsnareMinimumSpeedFactor)); + ASSERT(theSpeedFactor <= 1.0f); + + outBaseSpeed *= theSpeedFactor; + outUnemcumberedSpeed *= theSpeedFactor; + } + + // Reduce speed a bit when player has carapace + if(this->GetIsAlien()) + { + int theCarapaceLevel = AvHPlayerUpgrade::GetArmorUpgrade((AvHUser3)this->pev->iuser3, this->pev->iuser4); + float theSpeedFactor = (1.0f - BALANCE_VAR(kCarapaceSlowFactor)*theCarapaceLevel); + + outBaseSpeed *= theSpeedFactor; + outUnemcumberedSpeed *= theSpeedFactor; + } + + if(this->GetIsMetabolizing()) + { + float theMetabolizeSpeedFactor = BALANCE_VAR(kMetabolizeSpeedFactor); + outBaseSpeed *= theMetabolizeSpeedFactor; + outUnemcumberedSpeed *= theMetabolizeSpeedFactor; + } + + if(this->GetIsDigesting()) + { + float theDigestingSpeedFactor = BALANCE_VAR(kDigestingSpeedFactor); + outBaseSpeed *= theDigestingSpeedFactor; + outUnemcumberedSpeed *= theDigestingSpeedFactor; + } + + if(this->GetIsStunned()) + { + // MUHAHAHA! + outBaseSpeed = outUnemcumberedSpeed = 0.0f; + } + + // If we're a fade using blink, increase our speed massively +// if(this->GetIsBlinking()) +// { +// outBaseSpeed *= 10.0f; +// outUnemcumberedSpeed = outBaseSpeed; +// } + +// if(GetGameRules()->GetIsCombatMode()) +// { +// int theSpeedIncrease = (this->GetExperienceLevel() - 1)*BALANCE_VAR(kCombatLevelSpeedIncrease); +// +// outBaseSpeed += theSpeedIncrease; +// outUnemcumberedSpeed += theSpeedIncrease; +// } + + if(GetGameRules()->GetIsCheatEnabled(kcHighSpeed)) + { + outBaseSpeed = outUnemcumberedSpeed = kMaxGroundPlayerSpeed*.7f; + } +} + +int AvHPlayer::GetMaxWalkSpeed() const +{ + return this->mMaxWalkSpeed; +} + +void AvHPlayer::PickSkin() +{ + int theSkin = 0; + + if(this->GetIsMarine()) + { + theSkin = RANDOM_LONG(0, 1); + } + + this->SetSkin(theSkin); +} + +void AvHPlayer::SetSkin(int inSkin) +{ + if(this->pev) + { + this->pev->skin = inSkin; + } +} + +void AvHPlayer::GiveOrderToSelection(AvHOrder& inOrder) +{ + // Set the player list as the selected players + EntityListType theReceivers = this->mSelected; + + // Set the order for the team + AvHTeam* theTeam = this->GetTeamPointer(); + ASSERT(theTeam); + + int theOrderID=inOrder.GetOrderID(); + if(GetGameRules()->GetIsCheatEnabled(kcOrderSelf)) + { + AvHOrder theOrder=inOrder; + int theSelfIndex = this->entindex(); + theOrder.SetReceiver(theSelfIndex); + theOrder.SetOrderID(); + theTeam->SetOrder(theOrder); + + } + if ( this->mSelected.size() > 0 ) { + for(EntityListType::iterator theIter = theReceivers.begin(); theIter != theReceivers.end(); theIter++) + { + if ( inOrder.GetTargetIndex() != *theIter ) + { + AvHOrder theOrder=inOrder; + theOrder.SetReceiver(*theIter); + theOrder.SetOrderID(); + theTeam->SetOrder(theOrder); + } + } + } + + + // set this to true to indicate they don't need help + this->mHasGivenOrder = true; +} + +bool AvHPlayer::GiveOrderToSelection(AvHOrderType inOrder, Vector inNormRay) +{ + bool theSuccess = false; + AvHOrder theNewOrder; + + Vector theOrigin = this->GetVisualOrigin(); + +// #ifdef DEBUG +// vec3_t theStartPoint; +// VectorMA(theOrigin, kSelectionStartRange, inNormRay, theStartPoint); +// +// vec3_t theEndPoint; +// VectorMA(theOrigin, kSelectionEndRange, inNormRay, theEndPoint); +// +// vec3_t theValidOrigin; +// AvHSHUServerGetFirstNonSolidPoint(theStartPoint, theEndPoint, theValidOrigin); +// +// theValidOrigin.z -= BALANCE_VAR(kBiteDamage); +// +// CBaseEntity* pEnt = CBaseEntity::Create(kwsDebugEntity, theValidOrigin, Vector(0, 0, 0)); +// ASSERT(pEnt); +// pEnt->pev->movetype = MOVETYPE_FLY; +// pEnt->pev->solid = SOLID_NOT; +// #endif + + if(AvHCreateSpecificOrder((AvHTeamNumber)(this->pev->team), theOrigin, inOrder, inNormRay, theNewOrder)) + { + this->GiveOrderToSelection(theNewOrder); + + theSuccess = true; + } + + return theSuccess; +} + +void AvHPlayer::HolsterCurrent() +{ + if(this->m_pActiveItem) + { + + CBasePlayerWeapon* theCurrentWeapon = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); + + if(theCurrentWeapon && theCurrentWeapon->CanHolster()) + { + theCurrentWeapon->Holster(); + } + + } +} + +void AvHPlayer::Kick() +{ + char theCommandBuffer[256]; + sprintf(theCommandBuffer, "kick \"%s\"\n", STRING(this->pev->netname)); + SERVER_COMMAND(theCommandBuffer); +} + +void AvHPlayer::ImpulseCommands() +{ + AvHMessageID theMessage = (AvHMessageID)pev->impulse; + + this->PlayerUse(); + + bool theHandledMessage = false; + + if(theMessage != MESSAGE_NULL) + { + // These things can only happen during play, not in ready room, reinforcement or observer modes + if(this->GetPlayMode() == PLAYMODE_PLAYING || (GetGameRules()->GetCheatsEnabled())) + { + if(this->ExecuteMessage(theMessage)) + { + theHandledMessage = true; + } + } + + if(!theHandledMessage) + { + CBasePlayer::ImpulseCommands(); + } + + // Very important I found out + this->pev->impulse = 0; + } +} + +void AvHPlayer::ItemPostFrame(void) +{ + // Check if player tried to do something while we were in the ready room. If so, display tutorial message. + if(this->GetPlayMode() == PLAYMODE_READYROOM) + { + // +movement: Removed case for +attack2 + if((this->pev->button & IN_ATTACK) /* || (this->pev->button & IN_ATTACK2) */ || (this->pev->button & IN_RELOAD)) + { + this->SendMessageOnce(kReadyRoomMessage, TOOLTIP); + } + } + + if(!this->GetIsBeingDigested()) + { + CBasePlayer::ItemPostFrame(); + } + + this->UpdateAmbientSounds(); +} + +void AvHPlayer::Init() +{ + int i; + + // Copy the server variables from the game rules. + + AvHGamerules* theGameRules = GetGameRules(); + + mServerVariableList.clear(); + mLastUpdateTime = -1; + for (i = 0; i < theGameRules->GetNumServerVariables(); i++) + { + mServerVariableList.push_back(ServerVariable()); + mServerVariableList.back().mCvar = theGameRules->GetServerVariable(i); + mServerVariableList.back().mForceResend = true; + } + + // Reset to default team + strcpy(this->m_szTeamName, kUndefinedTeam); + + this->mResources = 0; + this->mScore = 0; + this->mSavedCombatFrags = 0; + this->mLastModelIndex = -1; +#ifdef USE_OLDAUTH + this->mCachedAuthenticationMask = -1; + +#endif + + this->mFirstUpdate = true; + this->mNewMap = true; + + this->mHasSeenTeamA = false; + this->mHasSeenTeamB = false; + + this->mPendingCommand = NULL; + this->mIsSpeaking = false; + this->mOrdersRequested = false; + this->mOrderAcknowledged = false; + this->mEnemySighted = false; + this->mTriggerUncloak = false; + this->mTimeOfLastSaying = 0; + this->mLastSaying = MESSAGE_NULL; + this->mTimeOfLastEnemySighting = 0; + this->mClientInTopDownMode = false; + this->mInTopDownMode = false; + this->mClientCommander = -1; + + vec3_t theOrigin( 0, 0, 0 ); + VectorCopy(theOrigin, this->mPositionBeforeTopDown); + VectorCopy(theOrigin, this->mAnglesBeforeTopDown); + VectorCopy(theOrigin, this->mViewAnglesBeforeTopDown); + VectorCopy(theOrigin, this->mViewOfsBeforeTopDown); + VectorCopy(theOrigin, this->mLastGallopViewDirection); + this->mAnimExtensionBeforeTopDown = ""; + this->mTimeStartedTopDown = -1; + + if(this->pev) + { + this->pev->team = TEAM_IND; + this->ClearUserVariables(); + + this->pev->rendermode = kRenderNormal; + this->pev->renderfx = kRenderFxNone; + this->pev->renderamt = 0; + this->pev->skin = 0; + this->pev->solid = SOLID_NOT; + this->pev->frags = 0; + this->m_iDeaths = 0; + this->pev->playerclass = PLAYMODE_UNDEFINED; + } + + this->mClientInOverwatch = false; + this->mInOverwatch = false; + this->mOverwatchEnabled = true; + this->mOverwatchTarget = -1; + this->mTimeOfLastOverwatchPreventingAction = -1; + this->mTimeLastSeenOverwatchTarget = 0; + this->mOverwatchFiredThisThink = false; + + // : 0000953 + this->mTimeLastJoinTeam = -1; + // + + this->mTimeOfLastHiveUse = -1; + // alien upgrades + + this->mTimeOfLastRegeneration = -1; + this->mTimeOfLastDCRegeneration = -1; + this->mTimeOfLastPheromone = -1; + this->mMaxGallopSpeed = 0; + + this->mClientResearchingTech = MESSAGE_NULL; + + this->mAttackOneDown = false; + this->mAttackTwoDown = false; + this->mPlacingBuilding = false; + + this->mPreviousUser3 = AVH_USER3_NONE; + this->mSavedJetpackEnergy = 0; + + this->mHasBeenSpectator = false; + this->mHasLeftReadyRoom = false; + this->mQueuedThinkMessage = ""; + + // Clear out tech nodes + this->mClientTechNodes.Clear(); + this->mClientTechSlotList.clear(); + + // Clear out map locations + this->mClientInfoLocations.clear(); + + // Clear out hive info + this->mClientHiveInfo.clear(); + + this->mClientGamma = kDefaultMapGamma; + + // Clear sent message list + // Don't clear this message, don't keep sending tutorial messages + //this->mSentMessageList.clear(); + + //this->mClientBlipList.clear(); + //this->mBlipList.clear(); + + this->mAlienSightActive = false; + this->mNumHives = 0; + + this->mSpecialPASOrigin.x = this->mSpecialPASOrigin.y = this->mSpecialPASOrigin.z = 0.0f; + this->mClientSpecialPASOrigin.x = this->mClientSpecialPASOrigin.y = this->mClientSpecialPASOrigin.z = 0.0f; + this->mTimeOfLastPASUpdate = -1; + + // : 984 + // record the last time the player attempted to go to the readyroom + this->mTimeOfLastF4 = -1.0f; + + this->mTimeOfLastTeleport = -1; + this->mTimeOfLastHelpText = -1; + this->mTimeOfLastUse = -1; + this->mTimeLeapEnd = -1; + this->mTimeOfLastRedeem = -1; + + memset(&this->mClientDebugCSPInfo, 0, sizeof(weapon_data_t)); + memset(&this->mDebugCSPInfo, 0, sizeof(weapon_data_t)); + this->mClientNextAttack = 0; + + this->mMaxWalkSpeed = CBasePlayer::GetMaxWalkSpeed(); + this->mTimeToBeUnensnared = -1; + this->mLastTimeEnsnared = -1; + this->mLastTimeStartedPlaying = -1; + + this->mTimeToBeFreeToMove = -1; + this->mTimeToEndCatalyst = -1; + + this->mLastTimeInCommandStation = -1; + this->mLastTimeRedemptionTriggered = -1; + this->mLastTimeCheckedRedemption = -1; + + this->mJetpackEnergy = 1.0f; + this->mJetpacking = false; + this->mLastPowerArmorThink = -1; + this->mLastInventoryThink = -1; + + this->mTimeLastPlaying = -1; + this->mTimeGestationStarted = -1; + + this->mEvolution = MESSAGE_NULL; + this->mHealthPercentBefore = -1; + this->mArmorPercentBefore = -1; + + this->mTimeStartedScream = -1; + //this->mNumParticleTemplatesSent = 0; + //this->mTimeOfLastParticleTemplateSending = -1; + + this->mEnemyBlips.Clear(); + this->mFriendlyBlips.Clear(); + + this->mSelected.clear(); + this->mClientTrackingEntity = this->mTrackingEntity = 0; + this->mClientSelectAllGroup.clear(); + + for(i = 0; i < kNumHotkeyGroups; i++) + { + this->mClientGroups[i].clear(); + this->mClientGroupAlerts[i] = ALERT_NONE; + } + + this->mProgressBarEntityIndex = -1; + // Don't set this, must propagate + //this->mClientProgressBarEntityIndex = -1; + this->mProgressBarParam = -1; + this->mProgressBarCompleted = -1; + this->mTimeProgressBarTriggered = -1; + this->mTimeOfLastFogTrigger = -1; + this->mFogExpireTime = -1; + this->mCurrentFogEntity = -1; + + // Don't set this, we need to propagate lack of fog + //this->mClientCurrentFogEntity = -1; + + //this->mUpgrades.clear(); + this->mClientOrders.clear(); + + this->mMouseWorldPos = this->mAttackOnePressedWorldPos = this->mAttackTwoPressedWorldPos = theOrigin; + + this->mClientEntityHierarchy.Clear(); + + this->mKilledX = this->mKilledY = -1; + + this->mHasGivenOrder = false; + this->mTimeOfLastSignificantCommanderAction = -1; + this->mPreThinkTicks = 0; + + this->mDesiredNetName = ""; +#ifdef USE_OLDAUTH + this->mAuthCheatMask = 0; + this->mAllowAuth = true; +#endif + + this->mTimeOfLastClassAndTeamUpdate = -1; + this->mEffectivePlayerClassChanged = false; + this->mNeedsTeamUpdate = false; + this->mSendTeamUpdate = false; + this->mSendSpawnScreenFade = false; + this->mClientMenuTechSlots = 0; + + memset(&this->mClientRequests, 0, sizeof(int)*kNumRequestTypes); + + this->mDigestee = 0; + this->mTimeOfLastDigestDamage = 0; + this->mTimeOfLastCombatThink = 0; + this->mDesiredRoomType = this->mClientDesiredRoomType = 0; + + this->mTimeOfLastConstructUseAnimation = 0; + this->mTimeOfLastConstructUse = -1; + this->mTimeOfLastResupply = 0; + + this->mTimeOfMetabolizeEnd = -1; + + this->m_DefaultSpectatingMode = OBS_IN_EYE; + this->m_DefaultSpectatingTarget = 1; + + this->mLastSelectEvent = MESSAGE_NULL; + VectorCopy(g_vecZero, this->mPositionBeforeLastGotoGroup); + + this->mTimeOfLastSporeDamage = -1; + this->mTimeOfLastTouchDamage = -1; + + this->mLastMessageSent = ""; + + this->mExperience = 0.0; + this->mExperienceLevelsSpent = 0; + this->mClientExperienceLevelsSpent = 0; + this->mClientPercentToNextLevel = 0.0f; + this->mCombatNodes.Clear(); + this->mPurchasedCombatUpgrades.clear(); + this->mGiveCombatUpgrades.clear(); + this->mMarineHUDUpgrades=0; + this->mNumSensory=0; + this->mNumMovement=0; + this->mNumDefense=0; + + this->mTimeOfLastMovementSound = -1.0f; +} + +void AvHPlayer::InitializeFromTeam(float inHealthPercentage, float inArmorPercentage) +{ + // Set base health and armor + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + this->pev->health = this->pev->max_health = max(theMaxHealth*inHealthPercentage,1.0f);//: prevent bug with players evolving down from higher lifeform from getting negative health but still "alive" + + this->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3)*inArmorPercentage; + + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + ASSERT(theTeam); + + // If he's already on the team, this does nothing (his rank keeps over death) + if(theTeam->AddPlayer(this->entindex())) + { + GetGameRules()->RecalculateHandicap(); + + float theStartingPoints = theTeam->GetInitialPlayerPoints(this->edict()); + if(this->GetIsMarine()) + { + theStartingPoints += this->GetResources(); + } + + // Set starting resources + this->SetResources(theStartingPoints, false); + + // If we're in combat mode, we use our own tech nodes instead of our team's + if(GetGameRules()->GetIsCombatMode()) + { + AvHTechTree theInitialTechNodes = theTeam->GetTechNodes(); + + // Removed restoration of previous levels and experience due to abuse + //// Restore saved/spent nodes if we've already been playing + //AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); + //if(theServerPlayerData) + //{ + // AvHTechNodes theSavedTechNodes = theServerPlayerData->GetCombatNodes(); + // if(theSavedTechNodes.GetNumNodes() > 0) + // { + // theInitialTechNodes = theSavedTechNodes; + // } + // + // int a = this->mExperienceLevelsSpent; + // + // this->SetExperienceLevelsSpent(theServerPlayerData->GetExperienceLevelsSpent()); + // + // MessageIDListType thePurchasedCombatUpgrades = theServerPlayerData->GetPurchasedCombatUpgrades(); + // for(MessageIDListType::iterator theIter = thePurchasedCombatUpgrades.begin(); theIter != thePurchasedCombatUpgrades.end(); theIter++) + // { + // this->PurchaseCombatUpgrade(*theIter); + // } + //} + + // Save it + this->SetCombatNodes(theInitialTechNodes); + + // Set initial experience (restore old experience if player disconnected) + this->mExperience = theTeam->GetInitialExperience(this->edict()); + } + + // Random multiracial marines + this->PickSkin(); + } + } +} + +bool AvHPlayer::GetIsRelevant(bool inIncludeSpectating) const +{ + bool theIsRelevant = false; + + // Use ourself unless we're spectating + const AvHPlayer* thePlayer = this; + + if(inIncludeSpectating) + { + const CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + const AvHPlayer* theSpectatingPlayer = dynamic_cast(theSpectatingEntity); + if(theSpectatingPlayer) + { + thePlayer = theSpectatingPlayer; + } + } + } + + if(thePlayer->IsAlive() && (thePlayer->GetPlayMode() == PLAYMODE_PLAYING) && !thePlayer->GetIsSpectator() && (thePlayer->pev->team != TEAM_IND)) + { + theIsRelevant = true; + } + + return theIsRelevant; +} + +bool AvHPlayer::GetCanBeAffectedByEnemies() const +{ + bool theCanBeAffected = this->GetIsRelevant(false) && !this->GetIsTemporarilyInvulnerable() && !this->GetIsBeingDigested() && !this->GetIsInTopDownMode(); + return theCanBeAffected; +} + +float AvHPlayer::GetOpacity() const +{ + float theOpacity = AvHCloakable::GetOpacity(); + + //if(this->GetIsBlinking()) + //{ + // theOpacity = .05f; + //} + + return theOpacity; +} + +bool AvHPlayer::GetIsMetabolizing() const +{ + bool theIsMetabolizing = false; + + if((gpGlobals->time < this->mTimeOfMetabolizeEnd) && (this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER4)) + { + theIsMetabolizing = true; + } + + return theIsMetabolizing; +} + +void AvHPlayer::SetTimeOfMetabolizeEnd(float inTime) +{ + this->mTimeOfMetabolizeEnd = inTime; +} + +bool AvHPlayer::GetIsSelected(int inEntityIndex) const +{ + bool theIsSelected = false; + + for(EntityListType::const_iterator theIter = this->mSelected.begin(); theIter != this->mSelected.end(); theIter++) + { + if(*theIter == inEntityIndex) + { + theIsSelected = true; + break; + } + } + + return theIsSelected; +} + +bool AvHPlayer::RemoveSelection(int inEntityIndex) +{ + bool theSuccess = false; + + EntityListType::iterator theFoundIter = std::find(this->mSelected.begin(), this->mSelected.end(), (EntityInfo)inEntityIndex); + if(theFoundIter != this->mSelected.end()) + { + this->mSelected.erase(theFoundIter); + theSuccess = true; + } + + return theSuccess; +} + +void AvHPlayer::SetSelection(int inEntityIndex, bool inClearPreviousSelection) +{ + if(inClearPreviousSelection) + { + this->mSelected.clear(); + } + + this->mSelected.push_back(inEntityIndex); +} + + +bool AvHPlayer::GetIsSpectator() const +{ + return (this->pev->flags & FL_PROXY); +} + +void AvHPlayer::SetIsSpectator() +{ + this->pev->flags |= FL_PROXY; +} + +float AvHPlayer::GetLastTimeInCommandStation() const +{ + return this->mLastTimeInCommandStation; +} + +// Clears the player's impulse and sends "invalid action" notification if not a legal action +void AvHPlayer::ValidateClientMoveEvents() +{ + // If player tries to execute an alien ability that they don't have, stop them dead. + AvHMessageID theMessageID = (AvHMessageID)this->mCurrentCommand.impulse; + + if(theMessageID != MESSAGE_NULL) + { + // Assume it's invalid + bool theIsValid = false; + this->mCurrentCommand.impulse = MESSAGE_NULL; + this->pev->impulse = MESSAGE_NULL; + const float kMinSayingInterval = 3.0f; + + // Process universally allowable impulses + switch(theMessageID) + { + case COMM_CHAT_PUBLIC: + case COMM_CHAT_TEAM: + case COMM_CHAT_NEARBY: + case IMPULSE_FLASHLIGHT: + case IMPULSE_SPRAYPAINT: + case IMPULSE_DEMORECORD: + case ADMIN_READYROOM: + theIsValid = true; + break; + + case SAYING_1: + case SAYING_2: + case SAYING_3: + case SAYING_4: + case SAYING_5: + case SAYING_6: + case SAYING_7: + case SAYING_8: + case SAYING_9: +// : 0000008 +// preventing spamming of request and ack + case ORDER_REQUEST: + case ORDER_ACK: +// : + if(GetGameRules()->GetCheatsEnabled() || (gpGlobals->time > (this->mTimeOfLastSaying + kMinSayingInterval))) + { + theIsValid = true; + } + else + { + int a = 0; + } + break; + + default: + if(GetGameRules()->GetIsCombatMode()) + { + theIsValid = true; + } + break; + } + + // If not universally allowable + AvHTeam* theTeam = this->GetTeamPointer(); + if(!theIsValid && theTeam) + { + AvHUser3 theUser3 = this->GetUser3(); + + // Marines + if(this->GetIsMarine()) + { + // Soldiers + if((theUser3 == AVH_USER3_MARINE_PLAYER) && this->IsAlive()) + { + switch(theMessageID) + { + // Validate orders + // : 0000008 + // preventing spamming of request and ack + //case ORDER_REQUEST: + //case ORDER_ACK: + // : + + // Validate weapon switches + case WEAPON_NEXT: + case WEAPON_RELOAD: + case WEAPON_DROP: + + // Only soldiers can vote + case ADMIN_VOTEDOWNCOMMANDER: + theIsValid = true; + } + } + // Commanders + else if(theUser3 == AVH_USER3_COMMANDER_PLAYER) + { + switch(theMessageID) + { + // Validate commander movement and input + case COMMANDER_MOUSECOORD: + case COMMANDER_MOVETO: + case COMMANDER_SCROLL: + case COMMANDER_DEFAULTORDER: + case COMMANDER_SELECTALL: + case COMMANDER_REMOVESELECTION: + + // Validate hotgroup creation and selection + case GROUP_CREATE_1: + case GROUP_CREATE_2: + case GROUP_CREATE_3: + case GROUP_CREATE_4: + case GROUP_CREATE_5: + case GROUP_SELECT_1: + case GROUP_SELECT_2: + case GROUP_SELECT_3: + case GROUP_SELECT_4: + case GROUP_SELECT_5: + + // Special impulses to handle requests + case COMMANDER_NEXTIDLE: + case COMMANDER_NEXTAMMO: + case COMMANDER_NEXTHEALTH: + theIsValid = true; + } + + // Validate research and building + // Make sure it comes from the commander and also that the right building is selected and not busy + if(AvHSHUGetIsBuildTech(theMessageID) || AvHSHUGetIsResearchTech(theMessageID) || AvHSHUGetDoesTechCostEnergy(theMessageID) || (theMessageID == BUILD_RECYCLE)) + { + if(theTeam->GetTechNodes().GetIsMessageAvailableForSelection(theMessageID, this->mSelected)) + { + // Make sure only one structure is selected, and make sure the structure that's selected allows this action + if(AvHSHUGetIsBuildTech(theMessageID)) //doesn't depend on selection + { + theIsValid = true; + } + else + { + if(this->mSelected.size() == 1) + { + int theEntityIndex = *this->mSelected.begin(); + CBaseEntity* theEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theEntityIndex)); + AvHBaseBuildable* theBaseBuildable = dynamic_cast(theEntity); + if(theBaseBuildable && theBaseBuildable->GetIsTechnologyAvailable(theMessageID)) + { + theIsValid = true; + } + } + } + } + } + } + } + // Aliens + else if(this->GetIsAlien() && this->IsAlive()) + { + //AvHAlienAbilityWeapon* theAlienAbilityWeapon = dynamic_cast(this->m_pActiveItem); + + switch(theMessageID) + { + // Validate all alien abilities + case ALIEN_ABILITY_LEAP: + case ALIEN_ABILITY_CHARGE: + // Check that weapon is active + if(this->GetHasActiveAlienWeaponWithImpulse(theMessageID)) + { + // Check that we have enough energy, and that it's enabled + //if(theAlienAbilityWeapon->IsUseable()) + //{ + // Deduct energy cost + //ItemInfo theItemInfo; + //theAlienAbilityWeapon->GetItemInfo(&theItemInfo); + //float theEnergy = AvHMUGetEnergyCost(theItemInfo.iId); + theIsValid = true; + //} + } + + // Removed by mmcguire. + // This code is no longer needed because the pm_shared code + // now ignores ability impulses that come from the console. + + /* + else + { + // If players are trying to use the abilities when not equipped via the impulse key, stop them dead (or worse?) + //VectorCopy(g_vecZero, this->pev->velocity); + //this->pev->fixangle = TRUE; + this->Killed(this->pev, 1); + + char theString[512]; + sprintf(theString, "Player \"%s\" executed impulse %d illegally and was killed.\n", STRING(this->pev->netname), theMessageID); + ALERT(at_logged, theString); + } + */ + break; + + case ALIEN_ABILITY_BLINK: + theIsValid = true; + break; + + // Validate lifeform changes and evolutions + case ALIEN_EVOLUTION_ONE: + case ALIEN_EVOLUTION_TWO: + case ALIEN_EVOLUTION_THREE: + case ALIEN_EVOLUTION_SEVEN: + case ALIEN_EVOLUTION_EIGHT: + case ALIEN_EVOLUTION_NINE: + case ALIEN_EVOLUTION_TEN: + case ALIEN_EVOLUTION_ELEVEN: + case ALIEN_EVOLUTION_TWELVE: + + case ALIEN_LIFEFORM_ONE: + case ALIEN_LIFEFORM_TWO: + case ALIEN_LIFEFORM_THREE: + case ALIEN_LIFEFORM_FOUR: + case ALIEN_LIFEFORM_FIVE: + + case ALIEN_BUILD_RESOURCES: + case ALIEN_BUILD_OFFENSE_CHAMBER: + case ALIEN_BUILD_DEFENSE_CHAMBER: + case ALIEN_BUILD_SENSORY_CHAMBER: + case ALIEN_BUILD_MOVEMENT_CHAMBER: + case ALIEN_BUILD_HIVE: + + // Validate weapon switches + case WEAPON_NEXT: + theIsValid = true; + break; + } + } + } + + if(theIsValid) + { + this->mCurrentCommand.impulse = theMessageID; + this->pev->impulse = theMessageID; + } + } +} + +bool AvHPlayer::GetHasActiveAlienWeaponWithImpulse(AvHMessageID inMessageID) const +{ + bool theHasWeapon = false; + + for(int i = 0; (i < MAX_ITEM_TYPES) && !theHasWeapon; i++) + { + CBasePlayerItem* theCurrentItem = this->m_rgpPlayerItems[i]; + if(theCurrentItem) + { + AvHAlienAbilityWeapon* theBaseWeapon = dynamic_cast(theCurrentItem); + while(theBaseWeapon && !theHasWeapon) + { + if((theBaseWeapon->GetAbilityImpulse() == inMessageID) && theBaseWeapon->GetEnabledState() && theBaseWeapon->IsUseable()) + { + theHasWeapon = true; + } + theCurrentItem = theCurrentItem->m_pNext; + theBaseWeapon = dynamic_cast(theCurrentItem); + } + } + } + + return theHasWeapon; +} + +bool AvHPlayer::GetHasSeenATeam() +{ + return (this->mHasSeenTeamA || this->mHasSeenTeamB); +} + +bool AvHPlayer::GetHasSeenTeam(AvHTeamNumber inNumber) const +{ + bool theHasBeenOnTeam = false; + + if(inNumber == GetGameRules()->GetTeamA()->GetTeamNumber()) + { + theHasBeenOnTeam = this->mHasSeenTeamA; + } + else if(inNumber == GetGameRules()->GetTeamB()->GetTeamNumber()) + { + theHasBeenOnTeam = this->mHasSeenTeamB; + } + + return theHasBeenOnTeam; +} + +void AvHPlayer::SetHasSeenTeam(AvHTeamNumber inNumber) +{ + if(inNumber == GetGameRules()->GetTeamA()->GetTeamNumber()) + { + this->mHasSeenTeamA = true; + } + else if(inNumber == GetGameRules()->GetTeamB()->GetTeamNumber()) + { + this->mHasSeenTeamB = true; + } +} + +float AvHPlayer::GetTimeOfLastSporeDamage() const +{ + return this->mTimeOfLastSporeDamage; +} + +void AvHPlayer::SetTimeOfLastSporeDamage(float inTime) +{ + this->mTimeOfLastSporeDamage = inTime; +} + +// Assumes that the current input is legal +void AvHPlayer::HandleTopDownInput() +{ + // From CBasePlayer::PreThink(): + bool theAttackOneDown = FBitSet(this->mCurrentCommand.buttons, IN_ATTACK); + // +movement: Removed case for +attack2 + bool theAttackTwoDown = false; //FBitSet(this->mCurrentCommand.buttons, IN_ATTACK2); + bool theJumpHit = FBitSet(this->mCurrentCommand.buttons, IN_JUMP); + bool theCrouchDown = FBitSet(this->mCurrentCommand.buttons, IN_DUCK); + + // If we are a commander + if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) + { + // Ensure that orders are given through attack2, even though used as +movement + theAttackTwoDown = FBitSet(this->mCurrentCommand.buttons, IN_ATTACK2); + + this->mLastTimeInCommandStation = gpGlobals->time; + + // Fetch world x and world y from move. + // Note: there is some inaccuracy here for two reasons. One, these values were propagated + // over the network and were optimized for it, and two, the selection assumes that the rays + // emanate from the CURRENT view origin. When clicking on one unit, this is right. When + // dragging a box around units, the view origin could have moved substantially. This inaccuracy + // should really only be noticed when drag selecting huge groups of units. Send view origin as well? + this->mMouseWorldPos.x = this->mCurrentCommand.upmove/kSelectionNetworkConstant; + this->mMouseWorldPos.y = this->mCurrentCommand.sidemove/kSelectionNetworkConstant; + this->mMouseWorldPos.z = this->mCurrentCommand.forwardmove/kSelectionNetworkConstant; + + // Fetch potential world location of mouse click. worldx -> cmd->upmove, worldy -> cmd->sidemove + //if(this->pev->impulse == COMMANDER_MOUSECOORD) + AvHMessageID theMessageID = (AvHMessageID)this->mCurrentCommand.impulse; + + bool theIsBuildTech = AvHSHUGetIsBuildTech(theMessageID); + bool theIsResearchTech = AvHSHUGetIsResearchTech(theMessageID); + bool theIsRecycleMessage = (theMessageID == BUILD_RECYCLE); + + if(AvHSHUGetIsGroupMessage(theMessageID)) + { + this->GroupMessage(theMessageID); + } + else if(theMessageID == COMMANDER_SELECTALL) + { + bool theClearSelection = true; + + // Run through all players on our team + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetTeam() == this->GetTeam()) + { + if(/*theEntity->GetIsRelevant() &&*/ (theEntity != this)) + { + this->SetSelection(theEntity->entindex(), theClearSelection); + theClearSelection = false; + } + } + + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + // Set "selectall" group + //this->mSelectAllGroup = this->mSelected; + this->GetTeamPointer()->SetSelectAllGroup(this->mSelected); + } + else if(theMessageID == COMMANDER_REMOVESELECTION) + { + // TODO: + gSelectionHelper.ClearSelection(); + this->mTrackingEntity = 0; + } + else if((theMessageID == COMMANDER_NEXTIDLE) || (theMessageID == COMMANDER_NEXTAMMO) || (theMessageID == COMMANDER_NEXTHEALTH) || theJumpHit) + { + // Jump to first idle soldier and remove from list + bool theSuccess = false; + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + if(theJumpHit) + { + theMessageID = MESSAGE_NULL; + } + + AvHAlert theAlert; + if(theTeam->GetLastAlert(theAlert, true, theJumpHit, &theMessageID)) + { + int theEntityIndex = theAlert.GetEntityIndex(); + CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theEntityIndex)); + if(theBaseEntity) + { + VectorCopy(theBaseEntity->pev->origin, this->pev->origin); + + switch(theMessageID) + { + case COMMANDER_NEXTIDLE: + case COMMANDER_NEXTAMMO: + case COMMANDER_NEXTHEALTH: + // Set selection to player + this->mSelected.clear(); + this->mSelected.push_back(theEntityIndex); + break; + } + } + } + } + } + else if((theMessageID == COMMANDER_SCROLL) || (theMessageID == COMMANDER_MOVETO)) + { + // Stop tracking + this->mTrackingEntity = 0; + + // Commander didn't make a mistake when going to hotgroup, so clear event (so next group select will go to group, not to last position before) + this->mLastSelectEvent = MESSAGE_NULL; + } + else if((theMessageID == COMMANDER_MOUSECOORD) || theIsBuildTech || theIsResearchTech || theIsRecycleMessage /*|| (theMessageID == COMMANDER_DEFAULTORDER)*/) + { + bool theAttackOnePressed = (theAttackOneDown && !this->mAttackOneDown); + bool theAttackTwoPressed = (theAttackTwoDown && !this->mAttackTwoDown); + bool theAttackOneReleased = (!theAttackOneDown && this->mAttackOneDown); + bool theAttackTwoReleased = !theAttackTwoDown && this->mAttackTwoDown; + + // if left button is now down and it wasn't previously down + if(theAttackOnePressed) + { + // Save world location of press + this->mAttackOnePressedWorldPos = this->mMouseWorldPos; + } + + // if right button just down + if(theAttackTwoPressed) + { + // remember it's world location + this->mAttackTwoPressedWorldPos = this->mMouseWorldPos; + } + + Vector theReleasePosition; + if(theAttackOneReleased || theAttackTwoReleased) + { + // Save world position of release. + theReleasePosition = this->mMouseWorldPos; + + //char theMessage[256]; + //sprintf(theMessage, "LMB released, selecting\n"); + //ClientPrint(this->pev, HUD_PRINTTALK, theMessage); + } + + // Watch for selection events + if(theMessageID == COMMANDER_MOUSECOORD) + { + if(theAttackOneReleased) + { + if(!this->mPlacingBuilding) + { + if(!AvHToggleUseable(this, this->GetVisualOrigin(), this->mAttackOnePressedWorldPos)) + { + // Clear existing selection + //this->mSelected.clear(); + //this->mClientSelected.clear(); + + gSelectionHelper.QueueSelection(this->GetVisualOrigin(), this->mAttackOnePressedWorldPos, theReleasePosition, (AvHTeamNumber)this->pev->team); + } + this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; + } + this->mPlacingBuilding = false; + } + else if(theAttackTwoReleased) + { + // Check to be sure that players are selected + if(this->mSelected.size() > 0) + { + bool theNonPlayerSelected = false; + for(EntityListType::iterator theIter = this->mSelected.begin(); theIter != this->mSelected.end(); theIter++) + { + AvHPlayer* thePlayer = NULL; + AvHSUGetEntityFromIndex(*theIter, thePlayer); + if(!thePlayer) + { + theNonPlayerSelected = true; + break; + } + } + + if(!theNonPlayerSelected || GetGameRules()->GetIsCheatEnabled(kcOrderSelf)) + { + // Check if right-clicked a useable thing + // Trace to see if the commander clicked a useable thing + //if(!AvHToggleUseable(this, this->pev->origin, this->mAttackTwoPressedWorldPos)) + //{ + + if(!this->GiveOrderToSelection(ORDERTYPEL_DEFAULT, this->mAttackTwoPressedWorldPos)) + { + // This location better be off the map or something, default orders should nearly always go through + this->SendMessage(kInvalidOrderGiven, TOOLTIP); + } + else + { + this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; + } + } + } + //} + } + } + // Issue movement order to selection + //else if(theMessageID == COMMANDER_DEFAULTORDER) + //{ + // // Build order + // AvHOrder theOrder; + // theOrder.SetOrderType(ORDERTYPEL_DEFAULT); + // + // float theWorldX = this->mCurrentCommand.upmove*kWorldPosNetworkConstant; + // float theWorldY = this->mCurrentCommand.sidemove*kWorldPosNetworkConstant; + // theOrder.SetLocation(vec3_t(theWorldX, theWorldY, 0.0f)); + // + // this->GiveOrderToSelection(theOrder); + //} + // Watch for build events + else if(theIsBuildTech) + { + this->mPlacingBuilding = true; + + if(GetGameRules()->GetGameStarted()) + { + // 551 + // Hack to stop free scans. This should be reworked as a generic solution for energy based build events. + bool theCanBuild=true; + if(this->mSelected.size() > 0) + { + int theEntityForResearch = *this->mSelected.begin(); + CBaseEntity* theEntity = AvHSUGetEntityFromIndex(theEntityForResearch); + + AvHObservatory* theObs = dynamic_cast(theEntity); + if ( theObs ) + { + if ( (theObs->GetIsTechnologyAvailable(BUILD_SCAN)) == false && (theMessageID == BUILD_SCAN) ) + theCanBuild = false; + } + if(!theObs && theMessageID == BUILD_SCAN) { + theCanBuild = false; + } + } + + if ( theCanBuild ) { + // + this->BuildTech(theMessageID, this->mAttackOnePressedWorldPos); + this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; + +// : 0001014 +// // If player(s) selected when something built, give default order to it (assumes that players can't be selected along with other non-players) +// if(AvHSHUGetIsBuilding(theMessageID)) +// { +// if(this->mSelected.size() > 0) +// { +// int theFirstEntitySelected = *this->mSelected.begin(); +// if((theFirstEntitySelected >= 1) && (theFirstEntitySelected <= gpGlobals->maxClients)) +// { +// if(!this->GiveOrderToSelection(ORDERTYPEL_DEFAULT, this->mAttackOnePressedWorldPos)) +// { +// this->SendMessage(kInvalidOrderGiven, true); +// } +// } +// } +// } +// : + } + } + } + else if(theIsResearchTech) + { + if(GetGameRules()->GetGameStarted()) + { + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam && (theTeam->GetResearchManager().GetIsMessageAvailable(theMessageID))) + { + if(this->mSelected.size() == 1) + { + int theEntityForResearch = *this->mSelected.begin(); + this->Research(theMessageID, theEntityForResearch); + this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; + } + } + } + } + // Check for recycling action + else if(theIsRecycleMessage) + { + for(EntityListType::iterator theIter = this->mSelected.begin(); theIter != this->mSelected.end(); theIter++) + { + AvHBaseBuildable* theBuildable = NULL; + AvHSUGetEntityFromIndex(*theIter, theBuildable); + if(theBuildable) + { + if((theBuildable->pev->team == this->pev->team) && (theBuildable->pev->team != 0)) + { + theBuildable->StartRecycle(); + this->mTimeOfLastSignificantCommanderAction = gpGlobals->time; + + const char* theBuildableName = STRING(theBuildable->pev->classname); + this->LogPlayerAction("recycle", theBuildableName); + } + } + } + } + + // Update + this->mAttackOneDown = theAttackOneDown; + this->mAttackTwoDown = theAttackTwoDown; + } + } + else + { + // TODO: Make sure we think this player is a commander to prevent cheating on client + // TODO: If they sent COMMANDER_MOUSECOORD and they aren't a commander, there's trouble + } + + // Process selections if waiting + gSelectionHelper.ProcessPendingSelections(); + + if(gSelectionHelper.SelectionResultsWaiting()) + { + EntityListType theNewSelection; + gSelectionHelper.GetAndClearSelection(theNewSelection); + + //string theMessage = "selecting: "; + + // If crouch is down + bool theToggleSelection = theCrouchDown; + if(theToggleSelection) + { + bool theNewSelectionIsAllPlayers = true; + + // Check to make sure the whole new selection is all players + for(EntityListType::iterator theIterator = theNewSelection.begin(); theIterator != theNewSelection.end(); theIterator++) + { + if(*theIterator > gpGlobals->maxClients) + { + theNewSelectionIsAllPlayers = false; + } + } + + // If not, clear the current selection and clear crouch + if(!theNewSelectionIsAllPlayers) + { + theToggleSelection = false; + } + } + + if(!theToggleSelection) + { + this->mSelected.clear(); + } + + // Get player for each one, make sure it's selectable by this player + for(EntityListType::iterator theIterator = theNewSelection.begin(); theIterator != theNewSelection.end(); /* no increment*/) + { + // If this player is selectable + AvHPlayer* thePlayer = NULL; + AvHSUGetEntityFromIndex(*theIterator, thePlayer); + if(!thePlayer || GetGameRules()->GetIsPlayerSelectableByPlayer(thePlayer, this)) + { + // Add to debug message + //char theNumber[16]; + //sprintf(theNumber, "%d ", *theIterator); + //theMessage += string(theNumber); + int theCurrentEntity = *theIterator; + + // Toggle selection of this player + if(theToggleSelection) + { + // Is entity is already selected? + EntityListType::iterator theFindIter = std::find(this->mSelected.begin(), this->mSelected.end(), theCurrentEntity); + bool theEntityIsSelected = (theFindIter != this->mSelected.end()); + if(theEntityIsSelected) + { + this->mSelected.erase(theFindIter); + } + else + { + this->SetSelection(theCurrentEntity, false); + } + } + else + { + this->SetSelection(theCurrentEntity, false); + } + + // Increment + theIterator++; + + this->mTrackingEntity = 0; + } + else + { + // erase it from the list + theIterator = theNewSelection.erase(theIterator); + } + } + + // If we selected at least one unit, display debug message +// if(this->mSelected.size() > 0) +// { +// theMessage += "\n"; +// ClientPrint(this->pev, HUD_PRINTTALK, theMessage.c_str()); +// } + } + + // Temporary for fun +// AvHTeam* theTeam = this->GetTeamPointer(); +// if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN)) +// { +// if(theAttackOneDown && (this->GetUser3() != ROLE_ALIEN2) && (this->GetUser3() != ROLE_ALIEN5)) +// { +// this->PlayRandomRoleSound(kPlayerLevelAttackSoundList, CHAN_WEAPON); +// } +// } +} + +void AvHPlayer::Jump() +{ + Vector vecWallCheckDir;// direction we're tracing a line to find a wall when walljumping + Vector vecAdjustedVelocity; + Vector vecSpot; + TraceResult tr; + + if (FBitSet(pev->flags, FL_WATERJUMP)) + return; + + if (pev->waterlevel >= 2) + { + return; + } + + // If this isn't the first frame pressing the jump button, break out. + if ( !FBitSet( m_afButtonPressed, IN_JUMP ) ) + return; // don't pogo stick + + if ( !(pev->flags & FL_ONGROUND) || !pev->groundentity ) + { + return; + } + +// many features in this function use v_forward, so makevectors now. + UTIL_MakeVectors (pev->angles); + + SetAnimation( PLAYER_JUMP ); + + if ( FBitSet(pev->flags, FL_DUCKING ) || FBitSet(m_afPhysicsFlags, PFLAG_DUCKING) ) + { + //if ( m_fLongJump && (pev->button & IN_DUCK) && gpGlobals->time - m_flDuckTime < 1 && pev->velocity.Length() > 50 ) + if ( (pev->button & IN_DUCK) && gpGlobals->time - m_flDuckTime < 1 && pev->velocity.Length() > 50 ) + {// If jump pressed within a second of duck while moving, long jump! + SetAnimation( PLAYER_SUPERJUMP ); + } + } + + // If you're standing on a conveyor, add it's velocity to yours (for momentum) + entvars_t *pevGround = VARS(pev->groundentity); + if ( pevGround && (pevGround->flags & FL_CONVEYOR) ) + { + pev->velocity = pev->velocity + pev->basevelocity; + } +} + +void AvHPlayer::Killed( entvars_t *pevAttacker, int iGib ) +{ + if(this->GetUser3() != AVH_USER3_COMMANDER_PLAYER) + { + // Save death position + this->mKilledX = this->pev->origin.x; + this->mKilledY = this->pev->origin.y; + + this->mIsScreaming = false; + + this->PackDeadPlayerItems(); + + //this->StopDigestion(false); + this->ResetBehavior(false); + + if(GetGameRules()->GetIsCombatMode()) + { + this->ProcessCombatDeath(); + } + + // Fade out already performed when we start being digested + bool theFadeOut = !this->GetIsBeingDigested(); + + if(!this->GetIsBeingDigested()) + { + if(theFadeOut) + { + Vector theFadeColor; + theFadeColor.x = 0; + theFadeColor.y = 0; + theFadeColor.z = 0; + UTIL_ScreenFade(this, theFadeColor, kTransitionFadeTime, 0.0f, 255, FFADE_OUT | FFADE_STAYOUT); + } + } + else + { + SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, false); + } + + // This line caused a dynamic_cast failure when a shotty killed a flier, then it set it's ensnare state to false, + // which tried to deploy his current weapon, which got the most recent weapon it used, which was garbage for some reason + //this->SetEnsnareState(false); + SetUpgradeMask(&this->pev->iuser4, MASK_ENSNARED, false); + this->mTimeToBeUnensnared = -1; + this->mLastTimeEnsnared = -1; + + //this->mAlienSightActive = false; + this->mEvolution = MESSAGE_NULL; + this->SetUsedKilled(false); + + this->PlayRandomRoleSound(kPlayerLevelDieSoundList); + + this->Uncloak(); + + int thePriority = 0; + bool theIsDramatic = false; + switch(this->GetUser3(false)) + { + case AVH_USER3_ALIEN_EMBRYO: + thePriority = kGestateDeathPriority; + break; + case AVH_USER3_ALIEN_PLAYER2: + thePriority = kGorgeDeathPriority; + break; + case AVH_USER3_ALIEN_PLAYER3: + thePriority = kLerkDeathPriority; + break; + case AVH_USER3_ALIEN_PLAYER4: + thePriority = kFadeDeathPriority; + break; + case AVH_USER3_ALIEN_PLAYER5: + thePriority = kOnosDeathPriority; + theIsDramatic = true; + break; + } + + if(this->GetHasHeavyArmor()) + { + thePriority = kHeavyDeathPriority; + } + + if(thePriority > 0) + { + GetGameRules()->MarkDramaticEvent(thePriority, this->entindex(), theIsDramatic, (entvars_t*)pevAttacker); + } + + // Added this to make player models fade out + this->pev->solid = SOLID_NOT; + + // We can only die when we're playing, not in ready room, reinforcement mode or observation + // (see ClientKill) + CBasePlayer::Killed(pevAttacker, iGib); + + //: remove parasite flag if they are a marine. + if(this->GetIsMarine()) + SetUpgradeMask(&this->pev->iuser4, MASK_PARASITED, false); + + this->EffectivePlayerClassChanged(); + + GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_PLAYER_DIED, this->entindex()); + + + +// // Print death anim +// const char* theMainSequence = this->LookupSequence(this->pev->sequence); +// if(!theMainSequence) +// { +// theMainSequence = "None"; +// } +// +// const char* theGaitSequence = this->LookupSequence(this->pev->gaitsequence); +// if(!theGaitSequence) +// { +// theGaitSequence = "None"; +// } +// +// char theMessage[128]; +// sprintf(theMessage, "Death animation: %s, %s", theMainSequence, theGaitSequence); +// UTIL_SayText(theMessage, this); + } +} + +//Activity AvHPlayer::GetDeathActivity (void) +//{ +// Activity theDeathActivity = ACT_DIESIMPLE; +// +// switch(RANDOM_LONG(0, 3)) +// { +// case 1: +// theDeathActivity = ACT_DIEBACKWARD; +// break; +// case 2: +// theDeathActivity = ACT_DIEFORWARD; +// break; +// case 3: +// theDeathActivity = ACT_DIEVIOLENT; +// break; +// } +// +// return theDeathActivity; +//} + + +void AvHPlayer::NextWeapon() +{ + if(this->GetIsAbleToAct()) + { + CBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_pActiveItem); + + if(theActiveWeapon) + { + CBasePlayerWeapon* theNextWeapon = dynamic_cast(theActiveWeapon->m_pNext); + if(theNextWeapon) + { + this->m_pLastItem = this->m_pActiveItem; + this->m_pActiveItem->Holster(); + this->m_pActiveItem = theNextWeapon; + this->m_pActiveItem->Deploy(); + } + else + { + int theSlot = theActiveWeapon->iItemSlot(); + for(int i = 0; i < MAX_ITEM_TYPES; i++) + { + theNextWeapon = dynamic_cast(this->m_rgpPlayerItems[(theSlot + i + 1) % MAX_ITEM_TYPES]); + if(theNextWeapon) + { + this->m_pLastItem = this->m_pActiveItem; + this->m_pActiveItem->Holster(); + this->m_pActiveItem = theNextWeapon; + this->m_pActiveItem->Deploy(); + break; + } + } + } + } + } +} + +void AvHPlayer::ObserverModeIllegal() +{ + // Blackout screen or something if observers aren't allowed + UTIL_ScreenFade( CBaseEntity::Instance(this->edict()), Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE ); + this->SendMessage(kNoSpectating); +} + +void AvHPlayer::PackDeadPlayerItems(void) +{ + //to do - drop everything that's not in the standard loadout + LMG. + this->DropItem(kwsMachineGun); + this->DropItem(kwsShotGun); + this->DropItem(kwsHeavyMachineGun); + this->DropItem(kwsGrenadeGun); + this->DropItem(kwsMine); + this->DropItem(kwsWelder); +} + +void AvHPlayer::PlayRandomRoleSound(string inSoundListName, int inChannel, float inVolume) +{ + char theListName[256]; + AvHUser3 theUser3 = this->GetUser3(); + if(theUser3 != AVH_USER3_NONE) + { + sprintf(theListName, inSoundListName.c_str(), (int)theUser3); + gSoundListManager.PlaySoundInList(theListName, this, inChannel, inVolume); + } +} + +float AvHPlayer::GetTimeOfLastConstructUse() const +{ + return this->mTimeOfLastConstructUse; +} + +void AvHPlayer::SetTimeOfLastConstructUse(float inTime) +{ + this->mTimeOfLastConstructUse = inTime; +} + +void AvHPlayer::PlayerConstructUse() +{ + AvHTeam* theTeam = this->GetTeamPointer(); + ASSERT(theTeam); + if(theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) + { + this->HolsterWeaponToUse(); + } + else if(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + const float kConstructAnimationInterval = 1.1f; + const int kConstructAnimationIndex = 4; + + if((this->mTimeOfLastConstructUseAnimation == 0) || (gpGlobals->time > (this->mTimeOfLastConstructUseAnimation + kConstructAnimationInterval))) + { + // Play special builder animation + PLAYBACK_EVENT_FULL(0, this->edict(), gWeaponAnimationEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 0, kConstructAnimationIndex, 0, 0 ); + + // Delay idle + if(this->m_pActiveItem) + { + CBasePlayerWeapon* theCurrentWeapon = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); + if(theCurrentWeapon) + { + theCurrentWeapon->m_flTimeWeaponIdle += 2.0f; + } + } + + this->mTimeOfLastConstructUseAnimation = gpGlobals->time; + } + } +} + +bool AvHPlayer::PlaySaying(AvHMessageID inMessageID) +{ + bool thePlayedSaying = false; + char theSoundList[256]; + memset(theSoundList, 0, 256*sizeof(char)); + + if(this->pev->iuser3 == AVH_USER3_MARINE_PLAYER) + { + switch(inMessageID) + { + case SAYING_1: + case SAYING_2: + case SAYING_3: + case SAYING_4: + case SAYING_5: + case SAYING_6: + case SAYING_7: + case SAYING_8: + case SAYING_9: + sprintf(theSoundList, kSoldierSayingList, (inMessageID - SAYING_1 + 1)); + break; + + case ORDER_REQUEST: + strcpy(theSoundList, kSoldierOrderRequestList); + break; + + case ORDER_ACK: + strcpy(theSoundList, kSoldierOrderAckList); + break; + } + } + else if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) + { + switch(inMessageID) + { + case SAYING_1: + case SAYING_2: + case SAYING_3: + case SAYING_4: + case SAYING_5: + case SAYING_6: + case SAYING_7: + case SAYING_8: + case SAYING_9: + sprintf(theSoundList, kCommanderSayingList, (inMessageID - SAYING_1 + 1)); + break; + } + } + else if(this->GetIsAlien()) + { + switch(inMessageID) + { + case SAYING_1: + case SAYING_2: + case SAYING_3: + case SAYING_4: + case SAYING_5: + case SAYING_6: + case SAYING_8: + // : 0001088 added SAYING_8 + sprintf(theSoundList, kAlienSayingList, (inMessageID - SAYING_1 + 1)); + break; + } + } + + if(strcmp(theSoundList, "")) + { + if(inMessageID == ORDER_REQUEST) + { + this->mOrdersRequested = true; + this->mOrderAcknowledged = false; + this->mIsSpeaking = false; + } + else if(inMessageID == ORDER_ACK) + { + this->mOrderAcknowledged = true; + this->mIsSpeaking = false; + this->mOrdersRequested = false; + } + else + { + this->mIsSpeaking = true; + this->mOrderAcknowledged = false; + this->mOrdersRequested = false; + } + + this->mTimeOfLastSaying = gpGlobals->time; + this->mLastSaying = inMessageID; + + //int pitch = 95;// + RANDOM_LONG(0,29); + //EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, theSaying, 1, ATTN_NORM, 0, pitch); + gSoundListManager.PlaySoundInList(theSoundList, this, CHAN_VOICE, 1.0f); + + thePlayedSaying = true; + } + + return thePlayedSaying; +} + +bool AvHPlayer::PlayHUDSound(AvHHUDSound inSound) const +{ + return this->PlayHUDSound( inSound, pev->origin[0], pev->origin[1] ); +} + +bool AvHPlayer::PlayHUDSound(AvHHUDSound inSound, float x, float y) const +{ + bool theSuccess = false; + + if((inSound > HUD_SOUND_INVALID) && (inSound < HUD_SOUND_MAX)) + { + NetMsg_PlayHUDNotification( this->pev, 0, inSound, x, y ); + theSuccess = true; + } + + return theSuccess; +} + +void AvHPlayer::PlayHUDStructureNotification(AvHMessageID inMessageID, const Vector& inLocation) +{ + if(GetGameRules()->GetGameStarted()) + { + // This player built a structure. Tell all his teammates + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + + if(theEntity->GetIsRelevant() && !theEntity->GetIsBeingDigested()) + { + // Don't send our own messages to ourself unless cheats are enabled + if(GetGameRules()->GetCheatsEnabled() || (this != theEntity)) + { + bool theShowNotification = false; + + // Show to friendlies... + if(theEntity->pev->team == this->pev->team) + { + theShowNotification = true; + } + + if(theShowNotification) + { + NetMsg_PlayHUDNotification( theEntity->pev, 1, inMessageID, inLocation.x, inLocation.y ); + } + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + } +} + +void AvHPlayer::ProcessEvolution() +{ + this->SaveHealthArmorPercentages(); + + UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kGestationSound, 1.0f, .5, SND_STOP, 100); + + // Go back to the role we were before, unless we're morphing into a new lifeform + AvHUser3 theNewUser3 = this->mPreviousUser3; + + bool theChangedLifeforms = false; + switch(this->mEvolution) + { + case ALIEN_LIFEFORM_ONE: + theNewUser3 = AVH_USER3_ALIEN_PLAYER1; + theChangedLifeforms = true; + break; + case ALIEN_LIFEFORM_TWO: + theNewUser3 = AVH_USER3_ALIEN_PLAYER2; + theChangedLifeforms = true; + break; + case ALIEN_LIFEFORM_THREE: + theNewUser3 = AVH_USER3_ALIEN_PLAYER3; + theChangedLifeforms = true; + break; + case ALIEN_LIFEFORM_FOUR: + theNewUser3 = AVH_USER3_ALIEN_PLAYER4; + theChangedLifeforms = true; + break; + case ALIEN_LIFEFORM_FIVE: + theNewUser3 = AVH_USER3_ALIEN_PLAYER5; + theChangedLifeforms = true; + break; + } + + // This shouldn't be needed now that SetUser3 takes into account moving the origin. + // Position player a bit higher off the ground so he doesn't get stuck + //this->pev->origin.z += AvHMUGetOriginOffsetForUser3(theNewUser3); + + if(theChangedLifeforms) + { + if(GetGameRules()->GetIsCombatMode()) + this->SetLifeformCombatNodesAvailable(false); // Only allow one lifeform change + else + { + int iUpgrades = MASK_UPGRADE_1 + | MASK_UPGRADE_2 + | MASK_UPGRADE_3 + | MASK_UPGRADE_4 + | MASK_UPGRADE_5 + | MASK_UPGRADE_6 + | MASK_UPGRADE_7 + | MASK_UPGRADE_8 + | MASK_UPGRADE_9 + | MASK_UPGRADE_10 + | MASK_UPGRADE_11 + | MASK_UPGRADE_12 + | MASK_UPGRADE_13 + | MASK_UPGRADE_14 + | MASK_UPGRADE_15; + + SetUpgradeMask(&this->pev->iuser4, (AvHUpgradeMask)iUpgrades, false); + } + } + + this->SetUser3(theNewUser3, true); + + //this->mPreviousUser3 = User3_UNDEFINED; + + // int theMaxArmor = 0; + // float theArmorPercentage = 0; + // int theNewMaxArmor = 0; + + switch(this->mEvolution) + { + case ALIEN_EVOLUTION_ONE: + case ALIEN_EVOLUTION_TWO: + case ALIEN_EVOLUTION_THREE: + case ALIEN_EVOLUTION_SEVEN: + case ALIEN_EVOLUTION_EIGHT: + case ALIEN_EVOLUTION_NINE: + case ALIEN_EVOLUTION_TEN: + case ALIEN_EVOLUTION_ELEVEN: + case ALIEN_EVOLUTION_TWELVE: + + // If it's the exoskeleton upgrade, upgrade now + // theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, this->mUser3); + // theArmorPercentage = this->pev->armorvalue/theMaxArmor; + + ProcessGenericUpgrade(this->pev->iuser4, this->mEvolution); + + // If player has already decided on the direction to upgrade, spend any extra upgrade levels in that category. + // This has to happen immediately, or else spamming the upgrade button can get another evolution before this is called in InternalAlienUpgradesThink. + AvHTeam* theTeamPointer = this->GetTeamPointer(); + ASSERT(theTeamPointer); + AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); + AvHAddHigherLevelUpgrades(theUpgrades, this->pev->iuser4); + + //this->PlayHUDSound(HUD_SOUND_ALIEN_UPGRADECOMPLETE); + +// if(this->mEvolution == ALIEN_EVOLUTION_TWO) +// { +// theNewMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, this->mUser3); +// this->pev->armorvalue = theArmorPercentage*theNewMaxArmor; +// } + break; + } + + this->PlayRandomRoleSound(kPlayerLevelSpawnSoundList, CHAN_ITEM, this->GetAlienAdjustedEventVolume()); + this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; + + this->RevertHealthArmorPercentages(); + this->mEvolution = MESSAGE_NULL; +} + +void AvHPlayer::RevertHealthArmorPercentages() +{ + // Preserve armor and health percentages + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + this->pev->health = max(this->mHealthPercentBefore*theMaxHealth,1.0f);//: prevent bug with players evolving down from higher lifeform from getting negative health but still "alive" + + int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); + this->pev->armorvalue = max(this->mArmorPercentBefore*theMaxArmor, 0.0f); + + // Assumes a push/pop kind of deal + this->mHealthPercentBefore = this->mArmorPercentBefore = 1.0f; +} + +void AvHPlayer::SaveHealthArmorPercentages() +{ + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + this->mHealthPercentBefore = this->pev->health/(float)theMaxHealth; + this->mHealthPercentBefore = min(max(0.0f, this->mHealthPercentBefore), 1.0f); + + int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); + this->mArmorPercentBefore = this->pev->armorvalue/(float)theMaxArmor; + this->mArmorPercentBefore = min(max(0.0f, this->mArmorPercentBefore), 1.0f); +} + +void AvHPlayer::ProcessResourceAdjustment(AvHMessageID inMessageID) +{ + // TODO: Mark cheater if this isn't the case? + if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) + { + // Make sure we have resource tower selected + if(this->mSelected.size() == 1) + { + int theEntIndex = *this->mSelected.begin(); + AvHFuncResource* theFuncResource; + if(AvHSUGetEntityFromIndex(theEntIndex, theFuncResource)) + { + // Get particle system + int theParticleSystemIndex = theFuncResource->GetParticleSystemIndex(); + AvHParticleSystemEntity* theParticleSystemEntity = NULL; + if(AvHSUGetEntityFromIndex(theParticleSystemIndex, theParticleSystemEntity)) + { + // Adjust particle system for resource tower + + // Get custom data + uint16 theCustomData = theParticleSystemEntity->GetCustomData(); + + // Adjust custom data by inMessageID + ASSERT(false); + //switch(inMessageID) + //{ + //case RESOURCE_ADJUST_ONE_UP: + // theCustomData = min(theCustomData+1, 0xF); + // break; + //case RESOURCE_ADJUST_ONE_DOWN: + // theCustomData = max(theCustomData-1, 0); + // break; + //} + + // Set custom data again + theParticleSystemEntity->SetCustomData(theCustomData); + } + } + } + } +} + +void AvHPlayer::Evolve(AvHMessageID inMessageID, bool inInstantaneous) +{ + // TODO: Put in a waiting time or some other effects? + if(this->GetTeamPointer()->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + this->PlayHUDSound(HUD_SOUND_POINTS_SPENT); + + int theDramaticPriority = 0; + switch(inMessageID) + { + case ALIEN_LIFEFORM_ONE: + theDramaticPriority = kEvolveLevelOnePriority; + break; + case ALIEN_LIFEFORM_TWO: + theDramaticPriority = kEvolveLevelTwoPriority; + break; + case ALIEN_LIFEFORM_THREE: + theDramaticPriority = kEvolveLevelThreePriority; + break; + case ALIEN_LIFEFORM_FOUR: + theDramaticPriority = kEvolveLevelFourPriority; + break; + case ALIEN_LIFEFORM_FIVE: + theDramaticPriority = kEvolveLevelFivePriority; + break; + default: + theDramaticPriority = kEvolveUpgradePriority; + break; + } + + if(!inInstantaneous) + { + GetGameRules()->MarkDramaticEvent(theDramaticPriority, this); + + this->mTimeGestationStarted = gpGlobals->time; + this->mPreviousUser3 = (AvHUser3)this->pev->iuser3; + this->mEvolution = inMessageID; + + this->SetUser3(AVH_USER3_ALIEN_EMBRYO); + this->BecomePod(); + } + else + { + this->mPreviousUser3 = (AvHUser3)this->pev->iuser3; + this->mEvolution = inMessageID; + this->ProcessEvolution(); + } + } +} + +void AvHPlayer::LogEmitRoleChange() +{ + const char* theUser3Name = AvHSHUGetClassNameFromUser3((AvHUser3)this->pev->iuser3); + if(theUser3Name != NULL) + { + UTIL_LogPrintf("%s changed role to \"%s\"\n", + GetLogStringForPlayer( this->edict() ).c_str(), + theUser3Name + ); + } +} + +void AvHPlayer::LogPlayerAction(const char* inActionDescription, const char* inActionData) +{ + if(inActionDescription && inActionData) + { + UTIL_LogPrintf("%s triggered \"%s\" (type \"%s\")\n", + GetLogStringForPlayer( this->edict() ).c_str(), + inActionDescription, + inActionData); + } +} + +void AvHPlayer::LogPlayerActionPlayer(CBasePlayer* inActionPlayer, const char* inAction) +{ + if(inAction) + { + UTIL_LogPrintf("%s triggered \"%s\" against %s\n", + GetLogStringForPlayer( inActionPlayer->edict() ).c_str(), + inAction, + GetLogStringForPlayer( this->edict() ).c_str() + ); + } +} + +void AvHPlayer::LogPlayerAttackedPlayer(CBasePlayer* inAttackingPlayer, const char* inWeaponName, float inDamage) +{ + if(inWeaponName) + { + edict_t* theAttacker = inAttackingPlayer->edict(); + edict_t* theReceiver = this->edict(); + + bool theLogAttack = false; + int theLogDetail = ns_cvar_float(&avh_logdetail); + + if((theLogDetail > 0) && (theAttacker->v.team != theReceiver->v.team)) + { + theLogAttack = true; + } + else if((theLogDetail > 1) && (theAttacker->v.team == theReceiver->v.team)) + { + theLogAttack = true; + } + else if(theLogDetail > 2) + { + theLogAttack = true; + } + + if(theLogAttack) + { + // Remove "weapon_" prefix + string theKillerName(inWeaponName); + AvHSHUMakeViewFriendlyKillerName(theKillerName); + int theDamage = (int)inDamage; + + UTIL_LogPrintf("%s attacked %s with \"%s\" (damage \"%d\")\n", + GetLogStringForPlayer( theAttacker ).c_str(), + GetLogStringForPlayer( theReceiver ).c_str(), + theKillerName.c_str(), + theDamage + ); + } + } +} + +void AvHPlayer::LogPlayerKilledPlayer(CBasePlayer* inAttackingPlayer, const char* inWeaponName) +{ + if(inWeaponName) + { + edict_t* theAttacker = inAttackingPlayer->edict(); + edict_t* theReceiver = this->edict(); + + bool theLogAttack = false; + int theLogDetail = ns_cvar_float(&avh_logdetail); + + if((theLogDetail > 0) && (theAttacker->v.team != theReceiver->v.team)) + { + theLogAttack = true; + } + else if((theLogDetail > 1) && (theAttacker->v.team == theReceiver->v.team)) + { + theLogAttack = true; + } + else if(theLogDetail > 2) + { + theLogAttack = true; + } + + if(theLogAttack) + { + // Remove "weapon_" prefix + string theKillerName(inWeaponName); + AvHSHUMakeViewFriendlyKillerName(theKillerName); + + UTIL_LogPrintf("%s killed %s with \"%s\"\n", + GetLogStringForPlayer( theAttacker ).c_str(), + GetLogStringForPlayer( theReceiver ).c_str(), + theKillerName.c_str() + ); + } + } +} + +void AvHPlayer::Research(AvHMessageID inUpgrade, int inEntityIndex) +{ + if(this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) + { + bool theIsResearchable; + int theResearchCost; + float theResearchTime; + AvHTeam* theTeam = this->GetTeamPointer(); + + CBaseEntity* theEntity = AvHSUGetEntityFromIndex(inEntityIndex); + + if(theEntity && theTeam && (theEntity->pev->team == this->pev->team)) + { + AvHResearchManager& theResearchManager = theTeam->GetResearchManager(); + if(inUpgrade == MESSAGE_CANCEL) + { + // Remember research tech and time done + float thePercentageComplete = 0.0f; + AvHMessageID theCancelledTechnology = MESSAGE_NULL; + if(theResearchManager.CancelResearch(inEntityIndex, thePercentageComplete, theCancelledTechnology)) + { + if(theResearchManager.GetResearchInfo(theCancelledTechnology, theIsResearchable, theResearchCost, theResearchTime)) + { + ASSERT(thePercentageComplete >= 0.0f); + ASSERT(thePercentageComplete < 1.0f); + const float kRefundFactor = 1.0f; + float theRefund = kRefundFactor*(1.0f - thePercentageComplete)*theResearchCost; + theRefund = min(theRefund, (float)theResearchCost); + this->SetResources(this->GetResources() + theRefund); + } + + char* theResearchName = NULL; + if(AvHSHUGetResearchTechName(inUpgrade, theResearchName)) + { + this->LogPlayerAction("research_cancel", theResearchName); + } + } + } + else if(theResearchManager.GetResearchInfo(inUpgrade, theIsResearchable, theResearchCost, theResearchTime)) + { + // Look up cost, deduct from player + if(this->GetResources() >= theResearchCost) + { + // Remember research tech and time done + if(theResearchManager.SetResearching(inUpgrade, inEntityIndex)) + { + this->PayPurchaseCost(theResearchCost); + // Tell everyone on this team about research and time research done, so their + // HUD is updated, and so they can't then walk up to station and start researching something else + this->PlayHUDStructureNotification(inUpgrade, theEntity->pev->origin); + + char* theResearchName = NULL; + if(AvHSHUGetResearchTechName(inUpgrade, theResearchName)) + { + this->LogPlayerAction("research_start", theResearchName); + } + } + } + else + { + this->PlayHUDSound(HUD_SOUND_MARINE_MORE); + } + } + } + } +} + +// Digestion functions +int AvHPlayer::GetDigestee() const +{ + return this->mDigestee; +} + +void AvHPlayer::SetDigestee(int inPlayerID) +{ + this->mDigestee = inPlayerID; +} + +void AvHPlayer::StartDigestion(int inDigestee) +{ + AvHPlayer* theDigestee = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inDigestee))); + if(theDigestee) + { + theDigestee->SetBeingDigestedMode(true); + SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, true); + + this->SetDigestee(inDigestee); + + EMIT_SOUND(ENT(this->pev), CHAN_VOICE, kDevourSwallowSound, this->GetAlienAdjustedEventVolume(), ATTN_NORM); + + this->mTimeOfLastDigestDamage = gpGlobals->time; + } +} + +void AvHPlayer::StopDigestion(bool inDigested) +{ + // Play digest complete sound and stop digestion + int theDigesteeIndex = this->GetDigestee(); + this->SetDigestee(0); + + AvHPlayer* theDigestee = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theDigesteeIndex))); + if(theDigestee) + { + if(inDigested) + { + this->SetDesiredRoomType(0, true); + + EMIT_SOUND(ENT(this->pev), CHAN_VOICE, kDevourCompleteSound, this->GetAlienAdjustedEventVolume(), ATTN_NORM); + } + else + { + theDigestee->SetBeingDigestedMode(false); + + EMIT_SOUND(ENT(this->pev), CHAN_VOICE, kDevourCancelSound, this->GetAlienAdjustedEventVolume(), ATTN_NORM); + } + } + + SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, false); + +} + +void AvHPlayer::InternalDigestionThink() +{ + // If we have a digestee + int theDigesteeIndex = this->GetDigestee(); + AvHPlayer* theDigestee = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theDigesteeIndex))); + if(theDigestee) + { + bool thePlayerWasDigested = false; + + // If digestee is alive and still in the game (hasn't disconnected or switched teams) + if(theDigestee->GetIsRelevant()) + { + if(RANDOM_LONG(0, 110) == 0) + { + // Play digesting sound occasionally + EMIT_SOUND(this->edict(), CHAN_AUTO, kDigestingSound, this->GetAlienAdjustedEventVolume(), ATTN_NORM); + } + + // Do damage to digestee + float theTimePassed = gpGlobals->time - this->mTimeOfLastDigestDamage; + if(theTimePassed > 1.0f) + { + // Find devour weapon if we have one, so death message appears properly + entvars_t* theInflictor = this->pev; + CBasePlayerItem* theDevourWeapon = this->HasNamedPlayerItem(kwsDevour); + if(theDevourWeapon) + { + theInflictor = theDevourWeapon->pev; + } + + const float theCombatModeScalar = GetGameRules()->GetIsCombatMode() ? BALANCE_VAR(kCombatModeTimeScalar) : 1.0f; + theDigestee->pev->takedamage = DAMAGE_YES; + float theDamage = theTimePassed*BALANCE_VAR(kDevourDamage)*(1.0f/theCombatModeScalar); + theDigestee->TakeDamage(theInflictor, this->pev, theDamage, DMG_DROWN); + theDigestee->pev->takedamage = DAMAGE_NO; + + // Get health back too + this->Heal(theDamage, false); + + this->mTimeOfLastDigestDamage = gpGlobals->time; + } + + // Set the digestee's position to our own + VectorCopy(this->pev->origin, theDigestee->pev->origin); + VectorCopy(this->pev->angles, theDigestee->pev->angles); + + // Set status bar estimating how long before player will be digested (for both digestee and digester) + theDigestee->TriggerProgressBar(theDigesteeIndex, 3); + + // Set fuser3 appropriately + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theDigestee->pev->iuser4, theDigestee->GetUser3(), this->GetExperienceLevel()); + float theDigestingScalar = (((float)theMaxHealth - theDigestee->pev->health)/(float)theMaxHealth); + /*this->pev->fuser3 =*/ theDigestee->pev->fuser3 = theDigestingScalar*kNormalizationNetworkFactor; + + int thePercent=theDigestingScalar*100.0f ; + thePercent=min(100, max(0, thePercent)); + this->TriggerProgressBar(theDigesteeIndex, 5, thePercent); + + // Set sound effects as player gets more and more digested + int theDesiredRoomType = 26; // strange sounds right before you die + if(theDigestingScalar < .33f) + { + // Water 1 + theDesiredRoomType = 14; + } + else if(theDigestingScalar < .66f) + { + // Water 2 + theDesiredRoomType = 15; + } + else if(theDigestingScalar < .9f) + { + // Water 3 + theDesiredRoomType = 16; + } + theDigestee->SetDesiredRoomType(theDesiredRoomType); + + if(theDigestee->pev->health <= 0) + { + thePlayerWasDigested = true; + } + } + + // If digestee is dead and no longer relevant + if(!theDigestee->IsAlive() || !theDigestee->GetIsRelevant() || (theDigestee->GetTeam() == this->GetTeam())) + { + this->mProgressBarParam = -1; + this->StopDigestion(thePlayerWasDigested); + } + } +} + +bool AvHPlayer::GetDoesCurrentStateStopOverwatch() const +{ + AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); + const AvHTeam* theTeam = this->GetTeamPointer(); + + bool theCurrentStateStopsOverwatch = false; +// bool theCurrentStateStopsOverwatch = ((this->pev->button != 0 && !this->mOverwatchFiredThisThink) || /*(this->pev->velocity.Length() > kOverwatchBreakingVelocity) ||*/ !theWeapon || ((this->pev->iuser3 != AVH_USER3_MARINE_PLAYER) && !GetHasUpgrade(this->pev->iuser4, MASK_MARINE_OVERWATCH))); +// +// // Overwatch not valid for alien players +// if(theTeam && (theTeam->GetTeamType() != AVH_CLASS_TYPE_MARINE)) +// { +// theCurrentStateStopsOverwatch = true; +// } + + return theCurrentStateStopsOverwatch; +} + +bool AvHPlayer::GetIsEntityInSight(CBaseEntity* inEntity) +{ + + bool theSuccess = false; + AvHTeam* theTeam = this->GetTeamPointer(); + // Elven - we don't want marines who are being digested to be able to sight anything. + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) && GetHasUpgrade(this->pev->iuser4, MASK_DIGESTING)) + { + return theSuccess; + } + // Check if the entitiy is in the player's view frustum. + UTIL_MakeVectors ( pev->v_angle ); + + Vector center = inEntity->pev->origin + (inEntity->pev->maxs + inEntity->pev->mins) / 2; + Vector sightLine = center - (pev->origin + pev->view_ofs); + + Vector hSightLine = sightLine - DotProduct(sightLine, gpGlobals->v_up) * gpGlobals->v_up; + Vector vSightLine = sightLine - DotProduct(sightLine, gpGlobals->v_right) * gpGlobals->v_right; + + float hDot = DotProduct(hSightLine.Normalize(), gpGlobals->v_forward); + float vDot = DotProduct(vSightLine.Normalize(), gpGlobals->v_forward); + + float hAngle = acosf(hDot) * 180 / M_PI; + float vAngle = acosf(vDot) * 180 / M_PI; + + if (hAngle > 180) hAngle -= 360; + if (vAngle > 180) vAngle -= 360; + + float aspect = 1.333; // 640/480 + + if (fabs(hAngle) <= pev->fov / 2 && fabs(vAngle) <= pev->fov / (2 * aspect)) + { + if (FVisible(inEntity)) + { + + theSuccess = true; + } + } + +// if(GET_RUN_CODE(4096)) +// { +// if (!theSuccess) +// { +// +// UTIL_MakeVectors ( pev->v_angle ); +// +// const float kMaxDistanceForSighting = 10000; +// +// // Trace a ray in the direction the player is aiming. +// +// Vector theStart = EyePosition(); +// Vector theEnd; +// VectorMA(theStart, kMaxDistanceForSighting, gpGlobals->v_forward, theEnd); +// +// TraceResult tr; +// UTIL_TraceLine(theStart, theEnd, dont_ignore_monsters, ignore_glass, ENT(pev), &tr); +// +// if (tr.flFraction != 1 && tr.pHit == ENT(inEntity->pev)) +// { +// int a = 0; +// theSuccess = true; +// } +// +// } +// } + // TODO: Make this better so we can see edges of things? What about big aliens? + + return theSuccess; + +} + +char* AvHPlayer::GetPlayerModelKeyName() +{ + // Default to marine in ready room + char* theModelKeyName = kSoldierName; + + AvHUser3 theUser3 = this->GetUser3(); + + switch(theUser3) + { + case AVH_USER3_MARINE_PLAYER: + theModelKeyName = kSoldierName; + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_13)) + { + theModelKeyName = kHeavyName; + } + break; + case AVH_USER3_COMMANDER_PLAYER: + theModelKeyName = kCommanderName; + break; + case AVH_USER3_ALIEN_PLAYER1: + theModelKeyName = kAlien1Name; + break; + case AVH_USER3_ALIEN_PLAYER2: + theModelKeyName = kAlien2Name; + break; + case AVH_USER3_ALIEN_PLAYER3: + theModelKeyName = kAlien3Name; + break; + case AVH_USER3_ALIEN_PLAYER4: + theModelKeyName = kAlien4Name; + break; + case AVH_USER3_ALIEN_PLAYER5: + theModelKeyName = kAlien5Name; + break; + case AVH_USER3_ALIEN_EMBRYO: + theModelKeyName = kAlienGestationName; + break; + } + + return theModelKeyName; +} + +void AvHPlayer::HandleOverwatch(void) +{ + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE)) + { + this->mOverwatchFiredThisThink = false; + + // If in overwatch + if(this->mInOverwatch) + { + // do we have a target we're firing at? + if(this->mOverwatchTarget != -1) + { + // is target around? + edict_t* theTargetEdict = INDEXENT(this->mOverwatchTarget); + if(!FNullEnt(theTargetEdict)) + { + // if so, move aim toward it but not farther than x degrees from starting pos + CBaseEntity* theTarget = CBaseEntity::Instance(theTargetEdict); + this->TurnOverwatchTowardsTarget(theTarget); + + // is it within our weapon range? + float theTargetDistance = (theTarget->pev->origin - this->pev->origin).Length(); + AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); + if(theWeapon) + { + if(theWeapon->GetRange() >= theTargetDistance) + { + // do we have a clear line of sight to it? + bool theCanSeeTarget = this->GetIsEntityInSight(theTarget); + if(theCanSeeTarget || (gpGlobals->time - this->mTimeLastSeenOverwatchTarget < kOverwatchKeepFiringAfterMissingTargetTime)) + { + // do we have ammo left? + if(!theWeapon->UsesAmmo() || (theWeapon->m_iClip > 0)) + { + // FIRE! + this->pev->button |= IN_ATTACK; + this->mOverwatchFiredThisThink = true; + } + + // Update last time we saw our target + if(theCanSeeTarget) + { + this->mTimeLastSeenOverwatchTarget = gpGlobals->time; + + // Playback event to increase tension, but keep network usage down (once or twice a second?) + if(this->pev->fuser2 != -1) + { + //if(RANDOM_LONG(0, 100) == 0) + //{ + //PLAYBACK_EVENT_FULL(0, this->edict(), gTensionOverwatchEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + //} + } + } + } + + // if not, has it been a long time since we've seen it, or did we see it die? Reset. + if( (gpGlobals->time - this->mTimeLastSeenOverwatchTarget > kOverwatchLostTargetTime) || + (theCanSeeTarget && !theTarget->IsAlive())) + { + this->ResetOverwatch(); + } + } + } + else + { + // This can happen when dropping weapons or switching roles + this->TurnOffOverwatch(); + } + } + else + { + this->ResetOverwatch(); + } + } + // is there a new target in range? + else + { + this->AcquireOverwatchTarget(); + } + + // see if we moved so we're out + if(this->GetDoesCurrentStateStopOverwatch()) + { + this->TurnOffOverwatch(); + } + } + else + { + if(this->GetDoesCurrentStateStopOverwatch()) + { + this->mTimeOfLastOverwatchPreventingAction = gpGlobals->time; + } + + // if overwatch is enabled, see if we've been still long enough to put us into it + if(this->mOverwatchEnabled && !(this->pev->flags & FL_FAKECLIENT)) + { + if(this->mTimeOfLastOverwatchPreventingAction != -1) + { + if(gpGlobals->time - this->mTimeOfLastOverwatchPreventingAction >= kOverwatchAcquireTime) + { + // if so, set overwatch on, make sure to set the current weapon into overwatch + this->TurnOnOverwatch(); + } + } + } + } + } +} + +void AvHPlayer::InternalAlienUpgradesThink() +{ + // If we're an alien player + AvHTeam* theTeam = this->GetTeamPointer(); + + if(this->GetIsAlien()) + { + if(theTeam && !GetGameRules()->GetIsCombatMode()) + { + // Preserve health and armor percentages as we get and remove upgrades + float theHealthPercentage = this->pev->health/AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + float theArmorPercentage = this->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); + + ASSERT(theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN); + AvHAlienUpgradeListType theUpgrades = theTeam->GetAlienUpgrades(); + + // If player has already decided on the direction to upgrade, spend any extra upgrade levels in that category + AvHAddHigherLevelUpgrades(theUpgrades, this->pev->iuser4); + + // If we have more upgrades then we should, remove one randomly + int theNumRemoved = AvHRemoveExcessUpgrades(theUpgrades, this->pev->iuser4); + if(theNumRemoved > 0) + { + // Play a sound indicating this has happened + this->PlayHUDSound(HUD_SOUND_ALIEN_UPGRADELOST); + } + + // If we're cloaked, and we no longer have any sensory upgrades, trigger uncloak + //int theNumSensoryUpgrades = AvHGetNumUpgradesInCategoryInList(theUpgrades, ALIEN_UPGRADE_CATEGORY_SENSORY); + //if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_CLOAKED) && (theNumSensoryUpgrades == 0)) + //{ + // this->TriggerUncloak(); + //} + + this->pev->health = theHealthPercentage*AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + this->pev->armorvalue = theArmorPercentage*AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); + } + + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && this->IsAlive()) + { + // If we have cloaking, update our cloak state + this->InternalAlienUpgradesCloakingThink(); + + // If we have regeneration, heal us + this->InternalAlienUpgradesRegenerationThink(); + + // If we have pheromones, update them + //this->InternalAlienUpgradesPheromonesThink(); + } + } + + // Update ensnare here + if(this->GetIsEnsnared()) + { + if(gpGlobals->time > this->mTimeToBeUnensnared) + { + this->SetEnsnareState(false); + } + } + + // Update stun + if(this->GetIsStunned()) + { + if(gpGlobals->time > this->mTimeToBeFreeToMove) + { + this->SetIsStunned(false); + } + } +} + +bool AvHPlayer::GetIsCloaked() const +{ + bool theIsCloaked = false; + + if( (this->GetOpacity() < 0.1f)) + { + theIsCloaked = true; + } + + return theIsCloaked; +} + +bool AvHPlayer::GetIsPartiallyCloaked() const +{ + bool theIsCloaked = false; + + if( (this->GetOpacity() < 0.6f)) + { + theIsCloaked = true; + } + + return theIsCloaked; +} + +bool AvHPlayer::GetRandomGameStartedTick(float inApproximateFrameRate) +{ + bool theTimeToTick = false; + + if(GetGameRules()->GetGameStarted() && (inApproximateFrameRate > 0)) + { + ASSERT(this->mPreThinkFrameRate > 0); + + int theUpperBound = (int)(this->mPreThinkFrameRate/inApproximateFrameRate); + if(RANDOM_LONG(1, theUpperBound) == 1) + { + theTimeToTick = true; + } + } + + return theTimeToTick; +} + +void AvHPlayer::TriggerUncloak() +{ + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && this->IsAlive()) + { + this->mTriggerUncloak = true; + + SetUpgradeMask(&this->pev->iuser4, MASK_SENSORY_NEARBY, false); + + this->Uncloak(); + } +} + +void AvHPlayer::InternalAlienUpgradesPheromonesThink() +{ + const float kPheromoneUpdateInterval = .4f; + const float kPheromoneBaseRange = 600; + const int kMaxPheromonePuffs = 3; + + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_8)) + { + int theRange = kPheromoneBaseRange; + + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_14)) + { + theRange *= 2; + } + else if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_15)) + { + theRange *= 3; + } + + if(this->mTimeOfLastPheromone == -1 || (gpGlobals->time > this->mTimeOfLastPheromone + kPheromoneUpdateInterval)) + { + typedef std::map PlayerDistanceListType; + PlayerDistanceListType thePlayerDistanceList; + + // Look for players in range to draw pheromones of + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + + // Find nearest distance to friendly and relevant player + if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team) && !GetHasUpgrade(theEntity->pev->iuser4, MASK_TOPDOWN) && (theEntity != this) /*&& !this->GetIsEntityInSight(theEntity)*/) + { + double theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); + if(theDistance < theRange) + { + // Choose nearest x players to emit from + thePlayerDistanceList[theEntity->entindex()] = theDistance; + } + } + + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + for(int theNumPheromonePuffs = 0; (theNumPheromonePuffs < kMaxPheromonePuffs) && (thePlayerDistanceList.size() > 0); theNumPheromonePuffs++) + { + // Find the nearest entity + PlayerDistanceListType::iterator theClosestIter = thePlayerDistanceList.begin(); + float theClosestDistance = sqrt(kMaxMapDimension*kMaxMapDimension + kMaxMapDimension*kMaxMapDimension); + + for(PlayerDistanceListType::iterator theIter = thePlayerDistanceList.begin(); theIter != thePlayerDistanceList.end(); theIter++) + { + float theCurrentRange = theIter->second; + if(theCurrentRange < theClosestDistance) + { + theClosestDistance = theCurrentRange; + theClosestIter = theIter; + } + } + + // Play a puff for it + int theCurrentEntityIndex = theClosestIter->first; + CBaseEntity* theCurrentEntity = (CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theCurrentEntityIndex))); + ASSERT(theCurrentEntity); + + AvHSUPlayParticleEvent(kpsPheromoneEffect, this->edict(), theCurrentEntity->pev->origin, FEV_HOSTONLY); + + // Delete that entity + thePlayerDistanceList.erase(theClosestIter); + } + + this->mTimeOfLastPheromone = gpGlobals->time; + } + } +} + +float AvHPlayer::GetCloakTime() const +{ + float theCloakTime = AvHCloakable::GetCloakTime(); + + if(this->GetIsAlien()) + { + // If we have cloaking upgrade, we cloak faster + int theCloakingLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_7); + if ( GetHasUpgrade(this->pev->iuser4, MASK_SENSORY_NEARBY) ) + theCloakingLevel=3; + if(theCloakingLevel > 0) + { + theCloakTime = BALANCE_VAR(kCloakTime)/theCloakingLevel; + } + } + + return theCloakTime; +} + +void AvHPlayer::InternalAlienUpgradesCloakingThink() +{ + // : + // 0000342 - Cloaking no longer depends on speed. + + // For some reason the lerk moves faster when turning + //const float kWalkSpeedFactor = (this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER3) ? .95f : .7f; + //const int kMaxSpeed = this->pev->maxspeed; + //elven - needs to update the speed factor here to count for celerity - see bug 342 + //int theBaseSpeed, theUnencumberedSpeed; + //this->GetSpeeds(theBaseSpeed, theUnencumberedSpeed); + //const int kMaxSpeed = this->pev->maxspeed; + //const int kMaxSpeed = (theUnencumberedSpeed > this->pev->maxspeed ) ? theUnencumberedSpeed : this->pev->maxspeed; + + //// If player moving too fast or trigger uncloak is set + //if( ( (this->pev->velocity.Length() > kMaxSpeed*kWalkSpeedFactor) && !GetHasUpgrade(this->pev->iuser4, MASK_SENSORY_NEARBY)) || this->mTriggerUncloak) + if(this->mTriggerUncloak) + { + //Uncloak + this->Uncloak(); + } + // : + else + { + // If we have cloaking upgrade + int theCloakingLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_7); + if(theCloakingLevel > 0) + { + // If time needed to cloak has passed + // : 864 + float theMaxWalkSpeed=this->pev->maxspeed * ((this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER3) ? .95f : .7f); + float theSpeed=AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_SENSORY_NEARBY) ? 0.0f : this->pev->velocity.Length(); + this->SetSpeeds(theSpeed, this->pev->maxspeed*1.05, theMaxWalkSpeed); +// if ( this->pev->velocity.Length() < theMaxWalkSpeed ) +// { +// ALERT(at_console, "Cloaking\n"); + this->Cloak(); +// } + } + } + + this->mTriggerUncloak = false; +} + +bool AvHPlayer::Redeem() +{ + bool theSuccess = false; + + // Can only be pulled back if we have at least one active hive + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam && (theTeam->GetNumActiveHives() > 0)) + { + vector theSafeHives; + vector theAttackedHives; + // Bring player back + if(this->GetTeamPointer()->GetNumActiveHives() > 0) + { + + // Loop through the hives for this team, look for the farthest one (hives under attack take precedence) + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if(theEntity->pev->team == this->pev->team) + { + bool theHiveIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntity->entindex()); + // allow teleport to any built hive, or unbuilt hives under attack. + if(!theEntity->GetIsSpawning()) + { + if ( theHiveIsUnderAttack ) + theAttackedHives.push_back(theEntity->entindex()); + else + theSafeHives.push_back(theEntity->entindex()); + + } + } + END_FOR_ALL_ENTITIES(kesTeamHive) + + } + vector *tmpPtr=&theSafeHives; + if ( theSafeHives.size() == 0 ) + tmpPtr=&theAttackedHives; + + int teleportHiveIndex=-1; + if ( tmpPtr->size() > 0 ) { + int index=RANDOM_LONG(0, tmpPtr->size()-1); + teleportHiveIndex=(*tmpPtr)[index]; + } + + // If we have a valid hive index, jump the player to it + if(teleportHiveIndex != -1) + { + // Play redeem effect where it happened so attackers know it happened + PLAYBACK_EVENT_FULL(0, this->edict(), gStartCloakEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + + // Move him to it! + AvHHive* theHive = NULL; + AvHSUGetEntityFromIndex(teleportHiveIndex, theHive); + if(theHive) + { + CBaseEntity* theSpawnEntity = GetGameRules()->GetRandomHiveSpawnPoint(this, theHive->pev->origin, theHive->GetMaxSpawnDistance()); + if(theSpawnEntity) + { + Vector theMinSize; + Vector theMaxSize; + this->GetSize(theMinSize, theMaxSize); + + int theOffset = AvHMUGetOriginOffsetForUser3(AvHUser3(this->pev->iuser3)); + Vector theOriginToSpawn = theSpawnEntity->pev->origin; + theOriginToSpawn.z += theOffset; + + if(AvHSUGetIsEnoughRoomForHull(theOriginToSpawn, AvHMUGetHull(false, this->pev->iuser3), this->edict())) + { + this->SetPosition(theOriginToSpawn); + this->pev->velocity = Vector(0, 0, 0); + if(this->GetIsDigesting()) + { + this->StopDigestion(false); + } + + mTimeOfLastRedeem = gpGlobals->time; + theSuccess = true; + + // Play teleport sound before and after + PLAYBACK_EVENT_FULL(0, this->edict(), gStartCloakEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); } + } + } + } + } + return theSuccess; +} + +void AvHPlayer::InternalAlienUpgradesRegenerationThink() +{ + // - 0000856 - Add innate regeneration for all alien players. + // Add a small and silent innate health and armor regeneration for + // all alien players, similar to the innate regeneration of all alien + // chambers. If a player chooses the regeneration upgrade, it replaces + // the innate regeneration rather than adding to it. + + // If enough time has elapsed to heal + if((this->mTimeOfLastRegeneration == -1) || (gpGlobals->time - this->mTimeOfLastRegeneration > BALANCE_VAR(kAlienRegenerationTime))) + { + int theRegenLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_2); + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + + float theRegenAmount = 0.0f; + float theRegenPercentage = BALANCE_VAR(kAlienInnateRegenerationPercentage); + + // If we have regeneration upgrade, multiply the amount by the regen level + if(theRegenLevel > 0) + { + theRegenPercentage = BALANCE_VAR(kAlienRegenerationPercentage); + theRegenAmount = (theRegenPercentage*theMaxHealth)*theRegenLevel; + } + + // Innate regeneration is at a fixed rate + else { + theRegenAmount = theRegenPercentage*(float)theMaxHealth; + } + // Always do at least 1 health of innate regeneration + theRegenAmount=max(theRegenAmount, 1.0f); + + // Don't play a sound for innate regeneration + this->Regenerate(theRegenAmount, theRegenLevel > 0 ); + } + + // Do we have the redemption? + int theRedemptionLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_3); + if(theRedemptionLevel > 0) + { + // Is the player really hurting? + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + + if((this->pev->health < theMaxHealth*BALANCE_VAR(kRedemptionThreshold)) && this->IsAlive()) + { + const float kPullBackTime = 20.0f; + if((this->mLastTimeRedemptionTriggered == -1) || (gpGlobals->time > (this->mLastTimeRedemptionTriggered + kPullBackTime))) + { + // Chance per second + float theRedemptionChance = theRedemptionLevel*BALANCE_VAR(kRedemptionChance); + + // How many times is this being called per second? + float theThinkInterval = 1.0f; + + if(this->mLastTimeCheckedRedemption > 0) + { + // The longer the time between checks, the higher the chance of being redeemed + theThinkInterval = (gpGlobals->time - this->mLastTimeCheckedRedemption); + } + + // Chance per second times seconds elapsed + if(RANDOM_FLOAT(0, 1.0f) <= theRedemptionChance*theThinkInterval) + { + if(this->Redeem()) + { + this->mLastTimeRedemptionTriggered = gpGlobals->time; + } + } + } + } + } + + this->mLastTimeCheckedRedemption = gpGlobals->time; +} + +void AvHPlayer::ProcessEntityBlip(CBaseEntity* inEntity) +{ + // : 982 + // Make alien hivesight range a balance var + const float kAlienFriendlyBlipRange = BALANCE_VAR(kHiveSightRange); + + // Is player alien? + bool theIsAlien = this->GetIsAlien(true); + + // Is player marine? + bool theIsMarine = this->GetIsMarine(true); + + ASSERT(theIsAlien || theIsMarine); + // Friendly? + bool theIsFriendly = ((inEntity->pev->team == 0) || (inEntity->pev->team == this->GetTeam())) ; + CBaseEntity* theSpectatingEntity = this->GetSpectatingEntity(); + if(theSpectatingEntity) + { + theIsFriendly = ((theSpectatingEntity->pev->team == 0) || (theSpectatingEntity->pev->team == inEntity->pev->team)); + } + + // If this player in an alien + if(theIsAlien && this->IsAlive(true) && !GetHasUpgrade(inEntity->pev->iuser4, MASK_TOPDOWN)) + { + // elven added - don't let digesting players get rendered on parasite + bool theEntityIsDigesting = GetHasUpgrade(inEntity->pev->iuser4, MASK_DIGESTING); + bool theEntityIsParasited = (theEntityIsDigesting) ? false : GetHasUpgrade(inEntity->pev->iuser4, MASK_PARASITED); + bool theEntityIsNearSensory = (theEntityIsDigesting) ? false : GetHasUpgrade(inEntity->pev->iuser4, MASK_SENSORY_NEARBY); + bool theEntityIsInHiveSight = (theEntityIsNearSensory || theEntityIsParasited || (GetHasUpgrade(inEntity->pev->iuser4, MASK_VIS_SIGHTED) && inEntity->IsPlayer()) || theIsFriendly || (inEntity->pev->iuser3 == AVH_USER3_HIVE)); + + //bool theHasHiveSightUpgrade = true;//GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_UPGRADE_11) || GetGameRules()->GetIsTesting(); + bool theEntityIsInSight = this->GetIsEntityInSight(inEntity); + + // If we're processing a relevant player + AvHPlayer* theOtherPlayer = dynamic_cast(inEntity); + bool theIsSpectatingEntity = this->GetIsSpectatingPlayer(inEntity->entindex()); + if(theOtherPlayer && (theOtherPlayer != this) && !theIsSpectatingEntity && theOtherPlayer->GetIsRelevant()) + { + // Calculate angle and distance to player + Vector theVectorToEntity = inEntity->pev->origin - this->pev->origin; + float theDistanceToEntity = theVectorToEntity.Length(); + + // If friendly + if(theEntityIsInHiveSight && theIsFriendly && !theEntityIsInSight) + { + int8 theBlipStatus = kFriendlyBlipStatus; + if(GetGameRules()->GetIsEntityUnderAttack(inEntity->entindex())) + { + theBlipStatus = kVulnerableFriendlyBlipStatus; + } + else if(theOtherPlayer->GetUser3() == AVH_USER3_ALIEN_PLAYER2) + { + theBlipStatus = kGorgeBlipStatus; + } + + //if(theOtherPlayer->GetEnemySighted()) + //{ + // theBlipStatus = 1; + //} + + if(theBlipStatus || (theDistanceToEntity < kAlienFriendlyBlipRange)) + { + // Info is the player index by default + int theBlipInfo = theOtherPlayer->entindex(); + this->mFriendlyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, theBlipStatus, theBlipInfo); + } + } + // else if enemy + else + { + // If it's in range + //if(theDistanceToEntity < kAlienEnemyBlipRange) + //{ + + int8 theBlipStatus = kEnemyBlipStatus; + + bool theDrawBlip = false; + + if(!theIsFriendly && !theEntityIsInSight) + { + // If they're parasited + if(theEntityIsParasited) + { + theDrawBlip = true; + } + + // If we have scent of fear upgrade - don't render if being eaten. changed by elven. + if( (theEntityIsNearSensory || GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_9)) && !theEntityIsParasited && !GetHasUpgrade(inEntity->pev->iuser4, MASK_DIGESTING)) + { + int theRange = BALANCE_VAR(kScentOfFearRadiusPerLevel); + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_14)) + { + theRange *= 2; + } + else if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_15)) + { + theRange *= 3; + } + + // ...and blip is within range + if( (theRange > theDistanceToEntity) || theEntityIsNearSensory ) + { +// int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theOtherPlayer->pev->iuser4, theOtherPlayer->GetUser3()); +// +// // ...and blip is under attack or weak +// if(GetGameRules()->GetIsEntityUnderAttack(theOtherPlayer->entindex()) || (theOtherPlayer->pev->health < (theMaxHealth/3))) +// { + theBlipStatus = kVulnerableEnemyBlipStatus; + theDrawBlip = true; +// } + } + } + } + + // Add it if it's in hive sight, or if we have scent of fear and he's marked + if(theDrawBlip) + { + this->mEnemyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, theBlipStatus); + } + //} + } + } + // else if we're processing a generic buildable + else + { + AvHBaseBuildable* theBaseBuildable = dynamic_cast(inEntity); + if(theBaseBuildable) + { + // If friendly hive + if(theIsFriendly) + { + // If not visible + if(!theEntityIsInSight) + { + // If it's being hurt + bool theDrawBlip=false; + AvHTeam* theTeam = this->GetTeamPointer(); + + AvHAlertType theAlertType = ALERT_NONE; + int8 theBlipStatus = kFriendlyBlipStatus; + bool theIsUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theBaseBuildable->entindex()); + if(theIsUnderAttack) + { + theBlipStatus = kVulnerableFriendlyBlipStatus; + theDrawBlip=true; + } + + // Add it if relevant + AvHHive* theHive = dynamic_cast(inEntity); + if(theHive ) + { + theDrawBlip=true; + if(!theIsUnderAttack) + { + theBlipStatus = kHiveBlipStatus; + } + if ( theHive && !theHive->GetIsBuilt() ) + { + theBlipStatus=kUnbuiltHiveBlipsStatus; + } + } + if ( theDrawBlip == true ) + { + // Info is the player index or the kBaseBlipInfoOffset + the iuser3 + int theBlipInfo = kBaseBlipInfoOffset + inEntity->pev->iuser3; + if(theBlipInfo >= sizeof(char)*128) + { + ASSERT(false); + } + this->mFriendlyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, theBlipStatus, theBlipInfo); + } + } + } + // If enemy + else + { + // If it's in hive sight, but not visible + if(!theEntityIsInSight && theEntityIsInHiveSight) + { + // If within range + //if(theDistanceToEntity < kAlienEnemyBlipRange) + //{ + // Add it + this->mEnemyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, kEnemyStructureBlipsStatus); + //} + } + } + } + } + } + // else if this player is a marine + else if(theIsMarine && this->IsAlive(true)) + { + bool theTeamHasMotionTracking = GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_8); + + // If player is commander (or is any marine with motion tracking?) + if((this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) || (theTeamHasMotionTracking)) + { + + // If enemy, add it to enemy list if "detected" // Elven - we don't want motion blips on aliens visible to us. + // && !(inEntity->pev->iuser4 & MASK_VIS_SIGHTED) <- this won't work as if I see an alien, other marines won't see MT if they're in another room + bool visibleToThis = this->GetIsEntityInSight(inEntity); + if(!theIsFriendly && (inEntity->pev->iuser4 & MASK_VIS_DETECTED) && !visibleToThis && inEntity->IsAlive())//: make sure there alive + { + this->mEnemyBlips.AddBlip(inEntity->pev->origin.x, inEntity->pev->origin.y, inEntity->pev->origin.z, kMarineEnemyBlipStatus); + } + } + } +} + +void AvHPlayer::ClearBlips() +{ + this->mEnemyBlips.Clear(); + this->mFriendlyBlips.Clear(); +} + +void AvHPlayer::ClientDisconnected() +{ + this->InitBalanceVariables(); + this->ResetGameNewMap(); + this->ResetEntity(); +} + +void AvHPlayer::ResetGameNewMap() +{ + this->mNumParticleTemplatesSent = 0; + this->mTimeOfLastParticleTemplateSending = -1; + this->mClientGamma = kDefaultMapGamma; + this->mNewMap = true; +} + +void AvHPlayer::InternalCommanderThink() +{ + // Finally, if we are commander, set the special PAS origin to use + if(this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) + { + // Only update every so often + const float kUpdatePASInterval = 1.0f; + float theTime = gpGlobals->time; + if((this->mTimeOfLastPASUpdate == -1) || (theTime > (this->mTimeOfLastPASUpdate + kUpdatePASInterval))) + { + // Default to our last known "real-world" origin, in case there are no other players? + VectorCopy(this->mPositionBeforeTopDown, this->mSpecialPASOrigin); + + // Max map size is 8012x8012 + double theShortestDistance = 64192144; + + // Loop through all players + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + + // Find nearest distance to friendly and relevant player + if(theEntity->GetIsRelevant() && (theEntity->pev->team == this->pev->team) && (theEntity != this)) + { + double theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); + if(theDistance < theShortestDistance) + { + VectorCopy(theEntity->pev->origin, this->mSpecialPASOrigin); + theShortestDistance = theDistance; + + //SET_VIEW(ENT(this->pev), ENT(theEntity->pev)); + } + } + + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + this->mTimeOfLastPASUpdate = theTime; + } + } +} + +void AvHPlayer::InternalBoundResources() +{ + // Aliens have a max resource amount, put any that overflows back into the team + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam && this->GetIsAlien()) + { + float theMaxResources = theTeam->GetMaxResources((AvHUser3)this->pev->iuser3); + + float theExtraResources = this->mResources - theMaxResources; + if(theExtraResources > 0) + { + theTeam->SetTeamResources(theTeam->GetTeamResources() + theExtraResources); + } + + this->mResources = min(this->mResources, theMaxResources); + } + + if(GetGameRules()->GetIsCombatMode()) + { + this->mResources = 0; + } +} + +bool AvHPlayer::QueryEnemySighted(CBaseEntity* inEntity) +{ + bool theSuccess = false; + + if((this->pev->team != inEntity->pev->team) && (this->pev->team != TEAM_IND) && (inEntity->pev->team != TEAM_IND)) + { + if(inEntity != this) + { + if(inEntity->IsAlive() && this->GetIsEntityInSight(inEntity)) + { + AvHPlayer* thePlayer = dynamic_cast(inEntity); + //this->CompareToPlayer(inEntity); //: WTF? + if(!thePlayer || (thePlayer->GetIsRelevant() && !thePlayer->GetIsCloaked())) + { + this->mEnemySighted = true; + this->mTimeOfLastEnemySighting = gpGlobals->time; + theSuccess = true; + } + } + } + } + return theSuccess; +} + +void AvHPlayer::InternalAlienThink() +{ + if(this->GetIsAlien() && this->IsAlive()) + { + // Are we gestating? Has enough time passed so we are something new? + if(this->pev->iuser3 == AVH_USER3_ALIEN_EMBRYO) + { + float theCurrentTime = gpGlobals->time; + float theFullTimeToGestate = GetGameRules()->GetBuildTimeForMessageID(this->mEvolution); + if(GetGameRules()->GetIsTesting()) + { + theFullTimeToGestate = 1.0f; + } + + this->TriggerProgressBar(this->entindex(), 3); + + // If changing this, make sure to change spectator behavior in InternalCommonThink + float theTimeElapsed = theCurrentTime - this->mTimeGestationStarted; + this->pev->fuser3 = (theTimeElapsed/theFullTimeToGestate)*kNormalizationNetworkFactor; + + if(theTimeElapsed >= theFullTimeToGestate) + { + this->ProcessEvolution(); + + // Set ourselves crouching so we have a smaller chance of getting stuck + if(AvHMUGetCanDuck(this->pev->iuser3)) + { + SetBits(this->pev->flags, FL_DUCKING); + } + } + } + + // Has enough time passed since we started screaming? + if(this->mIsScreaming) + { + if(gpGlobals->time > (this->mTimeStartedScream + BALANCE_VAR(kPrimalScreamDuration))) + { + this->mIsScreaming = false; + } + } + + // Uncloak if we are charging, leaping or blinking + if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT)) + { + this->TriggerUncloak(); + } + } +} + +void AvHPlayer::InternalCommonThink() +{ + if(GetGameRules()->GetGameStarted()) + { + this->mPreThinkTicks++; + + float theTimePassed = gpGlobals->time - GetGameRules()->GetTimeGameStarted(); + this->mPreThinkFrameRate = this->mPreThinkTicks/theTimePassed; + +// if(RANDOM_LONG(0, 125) == 0) +// { +// char theMessage[128]; +// sprintf(theMessage, "Num ents: %d\n", GetGameRules()->GetNumEntities()); +// //sprintf(theMessage, "Time passed: %f, ticks: %d, rate: %f, %d\n", theTimePassed, this->mPreThinkTicks, this->mPreThinkFrameRate, gNumFullPackCalls); +// UTIL_SayText(theMessage, this); +// } + } + else + { + this->mPreThinkTicks = 0; + this->mPreThinkFrameRate = 30; + } + + // If we're not in the ready room, and the a victor has just been determined, see if it's time to reset us + // This has to be done at a different time per player, to avoid overflows + if((GetGameRules()->GetVictoryTeam() != TEAM_IND) && (this->GetPlayMode() != PLAYMODE_READYROOM)) + { + // Get victory time, see if it's time to reset us + int thePlayerIndex = this->entindex(); + int theSecondToReset = kVictoryIntermission - 1 - kMaxPlayers/kResetPlayersPerSecond + (thePlayerIndex - 1)/kResetPlayersPerSecond; + //ASSERT(theSecondToReset >= 0); + //ASSERT(theSecondToReset < kVictoryIntermission); + + // NOTE: This depends on gamerules not allong players join a team after victory has been declared + float theVictoryTime = GetGameRules()->GetVictoryTime(); + if(gpGlobals->time > (theVictoryTime + theSecondToReset)) + { + this->SetPlayMode(PLAYMODE_READYROOM, true); + } + } + + // Must be called every frame to prevent exploiting + this->SetModelFromState(); + + + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + // Clear existing upgrades for marines. Aliens have their own individual upgrades. + if(this->GetIsMarine() && !GetGameRules()->GetIsCombatMode()) + { + int theInvertedUpgradeMask = ~kUpgradeBitMask; + this->pev->iuser4 &= theInvertedUpgradeMask; + } + + // Set the current upgrades + this->pev->iuser4 |= theTeam->GetTeamWideUpgrades(); + } + + // Update active and inactive inventory + const float kUpdateInventoryInterval = .5f; + if(gpGlobals->time > (this->mLastInventoryThink + kUpdateInventoryInterval)) + { + this->UpdateInventoryEnabledState(this->mNumHives, true); + this->mLastInventoryThink = gpGlobals->time; + } + + // Remember last time we were playing + if(this->GetPlayMode() == PLAYMODE_PLAYING) + { + this->mTimeLastPlaying = gpGlobals->time; + } + + this->InternalBoundResources(); + + // Players keep their health in fuser2 + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + int theCurrentHealth = max(0.0f, this->pev->health); + int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); + int theCurrentArmor = max(0.0f, this->pev->armorvalue); + + // Draw ring to take into account health and armor for aliens, + // Send armor and health for marines + if(this->GetIsMarine()) + { + // : 991 use a composite for marine armour and health + int theCurrentArmorPercent=(theCurrentArmor*100)/theMaxArmor; + int theCurrentHealthPercent=(theCurrentHealth*100)/theMaxHealth; + this->pev->fuser2= (float)( ((theCurrentArmorPercent&0x7F) << 7 ) + (theCurrentHealthPercent & 0x7F)); + } + else + { + float theScalar = (float)(theCurrentHealth + theCurrentArmor)/(float)(theMaxHealth + theMaxArmor); + this->pev->fuser2 = theScalar*kNormalizationNetworkFactor; + } + + //float theRandomAngle = RANDOM_FLOAT(0, 50); + //this->pev->v_angle.x = theRandomAngle; + //VectorCopy(this->pev->angles, this->mAnglesBeforeTopDown); + //VectorCopy(this->pev->v_angle, this->mViewAnglesBeforeTopDown); + + if(GetGameRules()->GetCountdownStarted() && !GetGameRules()->GetGameStarted() && (GetPlayMode() == PLAYMODE_PLAYING) && !GetGameRules()->GetCheatsEnabled()) + { + this->pev->flags |= FL_FROZEN; + DROP_TO_FLOOR(this->edict()); + } + else + { + this->pev->flags &= ~FL_FROZEN; + } + + // If we have a different desired name, change to it + if(GetGameRules()->GetArePlayersAllowedToJoinImmediately()) + { + // If our desired net name hasn't been set, set it now + if(this->mDesiredNetName != "") + { + char* theInfoBuffer = g_engfuncs.pfnGetInfoKeyBuffer(this->edict()); + + char theBuffer[256]; + strcpy(theBuffer, this->mDesiredNetName.c_str()); + g_engfuncs.pfnSetClientKeyValue(this->entindex(), theInfoBuffer, "name", theBuffer); + + this->mDesiredNetName = ""; + } + } + + // If we're spectating a target, set our health and armorvalue to theirs + // For spectators that are tracking players, move to their location, to prevent problems outside of the PVS + AvHPlayer* theEntity = NULL; + if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) + { + // If entity is no longer spectatable, jump to the next target + if(theEntity->GetIsInTopDownMode() || (theEntity->GetPlayMode() != PLAYMODE_PLAYING)) + { + this->Observer_FindNextPlayer(); + } + else + { + this->pev->health = theEntity->pev->health; + this->pev->armorvalue = theEntity->pev->armorvalue; + this->pev->fuser3 = theEntity->pev->fuser3; + + if((theEntity->GetUser3() == AVH_USER3_ALIEN_EMBRYO) || (theEntity->GetIsBeingDigested())) + { + this->TriggerProgressBar(theEntity->entindex(), 3); + } + + // Hacky way to make sure player is in PVS of target + float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin); + if(theDistance > 700) + { + VectorCopy(theEntity->pev->origin, this->pev->origin); + } + } + } + + // If we are digesting a player, process him (process even for marines in case we're testing) + this->InternalDigestionThink(); + + // Update FOV and view height every tick, needed for first-person spectating + this->SetViewForUser3(); + + PropagateServerVariables(); + +} + +void AvHPlayer::PropagateServerVariables() +{ + if ( gpGlobals->time > (mLastUpdateTime + 0.5f) ) + { + for (int i = 0; i < (signed)mServerVariableList.size(); i++) + { + //std::string theValue = CVAR_GET_STRING( mServerVariableList[i].mName.c_str() ); + if ( mServerVariableList[i].mCvar ) { + const cvar_t *theCvar=mServerVariableList[i].mCvar; + const bool forceResend=mServerVariableList[i].mForceResend; + mServerVariableList[i].mForceResend=false; + int theValue = theCvar->value; + if ( forceResend || ( mServerVariableList[i].mLastValueSent != theValue) ) + { + NetMsg_ServerVar( this->pev, theCvar->name, theValue ); + mServerVariableList[i].mLastValueSent = theValue; + break; // Only send one message per tick to avoid overflow. + } + } + } + mLastUpdateTime = gpGlobals->time; + } +} + +void AvHPlayer::InternalMarineThink() +{ + if(this->GetIsMarine() && (this->pev->iuser3 != AVH_USER3_COMMANDER_PLAYER)) + { + // Slowly heal power armor over time + if(this->GetHasPowerArmor()) + { + if(this->mLastPowerArmorThink != -1) + { + float theTimePassed = gpGlobals->time - this->mLastPowerArmorThink; + if(theTimePassed > 0.0f) + { + // Key regen to velocity to enhance leap-frogging, ala Halo + float theVelocity = this->pev->velocity.Length(); + float theRegenFactor = .5f; + if(theVelocity > 0) + { + //theRegenFactor = .3f - .3f*(theVelocity/kMaxGroundPlayerSpeed); + theRegenFactor = 0.1f; + } + theRegenFactor = max(min(theRegenFactor, 1.0f), 0.0f); + const float kPowerRegenRate = theRegenFactor*2.0f; + + int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); + if(this->pev->armorvalue < theMaxArmor) + { + this->pev->armorvalue = min((float)theMaxArmor, this->pev->armorvalue + kPowerRegenRate*theTimePassed); + } + } + } + this->mLastPowerArmorThink = gpGlobals->time; + } + + // Update buffed + if(this->GetIsCatalysted()) + { + if(gpGlobals->time > this->mTimeToEndCatalyst) + { + this->SetIsCatalysted(false); + } + } + } +} + +void AvHPlayer::InternalPreThink() +{ + PROFILE_START() + this->InternalCommonThink(); + PROFILE_END(kPlayerCommonThink); + + PROFILE_START() + this->InternalAlienThink(); + PROFILE_END(kPlayerAlienThink) + + PROFILE_START() + this->InternalMarineThink(); + PROFILE_END(kPlayerMarineThink) + + PROFILE_START() + this->InternalCommanderThink(); + PROFILE_END(kPlayerCommanderThink) + + PROFILE_START() + this->InternalAlienUpgradesThink(); + PROFILE_END(kPlayerAlienUpgradesThink) + + PROFILE_START() + this->InternalCombatThink(); + PROFILE_END(kPlayerCombatThink) + //this->InternalEnemySightedPreThink(); + + PROFILE_START() + this->InternalSpeakingThink(); + PROFILE_END(kPlayerSpeakingThink) + + PROFILE_START() + this->InternalProgressBarThink(); + PROFILE_END(kPlayerProgressBarThink) + + PROFILE_START() + this->InternalFogThink(); + PROFILE_END(kPlayerFogThink) + + PROFILE_START() + this->InternalHUDThink(); + PROFILE_END(kPlayerHUDThink) + + this->InternalMovementThink(); +} + +// Charge pushback +void AvHPlayer::InternalMovementThink() +{ + const char *theSoundToPlay; + bool thePlaySound = false; + float theSoundDelay = 0.0f; + + // SKULK + if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT) && (GetUser3() == AVH_USER3_ALIEN_PLAYER1)) + { + thePlaySound = true; + theSoundDelay = kLeapROF; + theSoundToPlay = kLeapSound; + + // Ensure that we do leap damage + this->StartLeap(); + } + + // FADE + if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT) && (GetUser3() == AVH_USER3_ALIEN_PLAYER4)) + { + thePlaySound = true; + theSoundDelay = 2.0f; + theSoundToPlay = kBlinkSound; + } + + // ONOS + if(GetHasUpgrade(this->pev->iuser4, MASK_ALIEN_MOVEMENT) && (GetUser3() == AVH_USER3_ALIEN_PLAYER5)) + { + thePlaySound = true; + theSoundDelay = 2.0f; + theSoundToPlay = "weapons/charge2.wav"; + + // Push back players + CBaseEntity* theEntity = NULL; + float radius = (float)BALANCE_VAR(kChargePushbackRadius); + float maxpushbackspeedfactor = (float)BALANCE_VAR(kChargeMaxPushbackSpeedFactor); + float pushbackfactor = (float)BALANCE_VAR(kChargeMaxPushbackForce); + + // Ensure that we don't push back players in the readyroom + if (this->GetPlayMode() == PLAYMODE_PLAYING) + { + // Find all entities around the onos + while((theEntity = UTIL_FindEntityInSphere(theEntity, this->pev->origin, radius)) != NULL) + { + if (theEntity->IsPlayer() && theEntity->IsAlive() && theEntity->entindex() != this->entindex()) + { + float distance = VectorDistance(this->pev->origin, theEntity->pev->origin); + if (distance >= 0.0f && distance <=radius) + { + float factor = pushbackfactor / (radius / distance); + + float weigthFactor=1.0f; + int veriticalLimit=110; + switch(theEntity->pev->iuser3) { + case AVH_USER3_ALIEN_PLAYER4: + weigthFactor=0.70f; + veriticalLimit=85; + break; + case AVH_USER3_ALIEN_PLAYER5: + weigthFactor=0.55f; + veriticalLimit=55; + break; + case AVH_USER3_MARINE_PLAYER: + if (GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_13)) { + weigthFactor=0.65f; + veriticalLimit=75; + } + break; + default: + break; + } + factor*=weigthFactor; + + vec3_t direction, heading; + VectorSubtract(theEntity->pev->origin, this->pev->origin, direction); + VectorNormalize(direction); + + VectorCopy(this->pev->velocity, heading); + VectorNormalize(heading); + + float dot = DotProduct(heading, direction); + dot=max(dot, 0.4f); + if (dot > 0.0f) + { + ALERT(at_console, "dot=%f direction = {%f, %f, %f}\n", dot, direction[0],direction[1],direction[2]); + VectorScale(direction, factor * dot, direction); + VectorAdd(theEntity->pev->velocity, direction, theEntity->pev->velocity); + if (Length(theEntity->pev->velocity) > theEntity->pev->maxspeed * maxpushbackspeedfactor) + { + VectorNormalize(theEntity->pev->velocity); + VectorScale(theEntity->pev->velocity, theEntity->pev->maxspeed * maxpushbackspeedfactor, theEntity->pev->velocity); + } + theEntity->pev->velocity[2] = max(veriticalLimit, theEntity->pev->velocity[2]); + + // Don't do "touch" damage too quickly + float theTouchDamageInterval = BALANCE_VAR(kTouchDamageInterval); + if((this->mTimeOfLastTouchDamage == -1) || (gpGlobals->time > (this->mTimeOfLastTouchDamage + theTouchDamageInterval))) + { + entvars_t* theInflictor=this->pev; + if ( this->m_rgpPlayerItems[4] ) + theInflictor = this->m_rgpPlayerItems[4]->pev; + float theScalar=0.0f; + if(GetGameRules()->CanEntityDoDamageTo(this, theEntity, &theScalar)) + { + float theDamage = BALANCE_VAR(kChargeDamage)*theScalar*theTouchDamageInterval; + ALERT(at_console, "doing %f damage\n", theDamage); + theEntity->TakeDamage(theInflictor, this->pev, theDamage, NS_DMG_NORMAL); + + if(theEntity->IsPlayer() && !theEntity->IsAlive()) + { + EMIT_SOUND(ENT(this->pev), CHAN_WEAPON, kChargeKillSound, 1.0, ATTN_NORM); + } + + this->mTimeOfLastTouchDamage = gpGlobals->time; + } + } + } + + // ALERT(at_console, UTIL_VarArgs("direction %f %f %f heading %f %f %f endvel %f %f %f\n", direction[0], direction[1], direction[2], heading[0], heading[1], heading[2], theEntity->pev->velocity[0], theEntity->pev->velocity[1], theEntity->pev->velocity[2])); + } + } + } + } + } + + if (thePlaySound && (mTimeOfLastMovementSound + theSoundDelay < gpGlobals->time)) + { + float theVolumeScalar = this->GetAlienAdjustedEventVolume(); + if (this->GetPlayMode() != PLAYMODE_PLAYING) + theVolumeScalar *= 0.2f; + float thePitch = 94.0f + (float)RANDOM_LONG(0, 16); + + if (theSoundToPlay == kLeapSound ) { + for ( int i=0; i < MAX_ITEM_TYPES; i++ ) { + AvHLeap* theLeap = dynamic_cast(this->m_rgpPlayerItems[i]); + if ( theLeap && theLeap->IsUseable() ) { + theLeap->PlaybackLeapEvent(); + this->mTimeOfLastMovementSound = gpGlobals->time; + break; + } + } + } + else { + EMIT_SOUND_DYN(ENT(this->pev), CHAN_WEAPON, theSoundToPlay, theVolumeScalar, ATTN_NORM, 0, 100); + this->mTimeOfLastMovementSound = gpGlobals->time; + } + } +} + +void AvHPlayer::InternalFogThink() +{ + if((this->mTimeOfLastFogTrigger != -1) && (gpGlobals->time > this->mTimeOfLastFogTrigger + this->mFogExpireTime)) + { + this->mCurrentFogEntity = -1; + } +} + +void AvHPlayer::InternalHUDThink() +{ + // Pull weapon out if we're done using something + if(this->mTimeOfLastUse != -1) + { + const float kUseTime = .5f; + if(gpGlobals->time > this->mTimeOfLastUse + kUseTime) + { + // Don't deploy while you're a commander (happens if you finish building something next to command station and immediately jump in) + if(!this->GetIsInTopDownMode() && !this->GetIsBeingDigested()) + { + this->DeployCurrent(); + this->mTimeOfLastUse = -1; + } + } + } + + // Don't hide chat by default + int theHideHUD = HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT | HIDEHUD_HEALTH; + + // Use local player or player we're spectating + AvHPlayer* theVisiblePlayer = this; + if(this->pev->iuser1 == OBS_IN_EYE) + { + AvHPlayer* theEntity = NULL; + if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity)) + { + theVisiblePlayer = theEntity; + } + } + + AvHPlayMode thePlayMode = theVisiblePlayer->GetPlayMode(); + AvHUser3 theUser3 = theVisiblePlayer->GetUser3(); + + if((thePlayMode == PLAYMODE_READYROOM) || (thePlayMode == PLAYMODE_REINFORCING) || (thePlayMode == PLAYMODE_AWAITINGREINFORCEMENT)) + { + theHideHUD = HIDEHUD_FLASHLIGHT | HIDEHUD_WEAPONS | HIDEHUD_HEALTH; + + // Only hide health when not following a target + if((thePlayMode == PLAYMODE_AWAITINGREINFORCEMENT) && (this->pev->iuser1 == OBS_IN_EYE)) + { + //theHideHUD &= ~HIDEHUD_WEAPONS; + theHideHUD &= ~HIDEHUD_HEALTH; + } + } + else if(thePlayMode == PLAYMODE_PLAYING) + { + theHideHUD = 0; + + if(!GetGameRules()->FAllowFlashlight()) + { + theHideHUD |= HIDEHUD_FLASHLIGHT; + } + + if(!theVisiblePlayer->pev->viewmodel) + { + //theHideHUD |= HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT; + } + + if(theUser3 == AVH_USER3_ALIEN_EMBRYO) + { + //theHideHUD |= HIDEHUD_HEALTH; + } + + if(GetHasUpgrade(theVisiblePlayer->pev->iuser4, MASK_TOPDOWN) || theVisiblePlayer->GetIsBeingDigested()) + { + theHideHUD |= HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT | HIDEHUD_HEALTH; + } + else + { + + // If we have no other weapons, hide ammo + + if(!HasWeapons()) + { + theHideHUD |= HIDEHUD_WEAPONS; + theHideHUD |= HIDEHUD_FLASHLIGHT; + } + + if(theUser3 == AVH_USER3_ALIEN_EMBRYO) + { + theHideHUD |= HIDEHUD_WEAPONS; + theHideHUD |= HIDEHUD_FLASHLIGHT; + } + } + } + else if(thePlayMode == PLAYMODE_OBSERVER) + { + theHideHUD = HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT | HIDEHUD_HEALTH; + + // Only hide health when not following a target + if((this->pev->iuser1 == OBS_IN_EYE)) + { + //theHideHUD &= ~HIDEHUD_WEAPONS; + theHideHUD &= ~HIDEHUD_HEALTH; + } + } + else + { + int a = 0; + } + + this->m_iHideHUD = theHideHUD; +} + + +void AvHPlayer::InternalProgressBarThink() +{ + // If some time has passed since the progress bar was triggered, send down a message to kill it + const float kProgressBarTimeOut = .2f; + if(this->mTimeProgressBarTriggered != -1) + { + if(gpGlobals->time > this->mTimeProgressBarTriggered + kProgressBarTimeOut) + { + this->mTimeProgressBarTriggered = -1; + this->mProgressBarEntityIndex = -1; + this->mProgressBarParam = -1; + this->mProgressBarCompleted = -1; + } + } +} + +void AvHPlayer::InternalSpeakingThink() +{ + if((this->mIsSpeaking || this->mOrderAcknowledged || this->mOrdersRequested) && (gpGlobals->time - this->mTimeOfLastSaying >= kSpeakingTime)) + { + this->mIsSpeaking = false; + this->mOrderAcknowledged = false; + this->mOrdersRequested = false; + } +} + +void AvHPlayer::PreThink( void ) +{ + // Get play mode + AvHPlayMode thePlayMode = this->GetPlayMode(); + bool theRunThink = ((thePlayMode == PLAYMODE_READYROOM) && GET_RUN_CODE(8)) || + ((thePlayMode == PLAYMODE_OBSERVER) && (GET_RUN_CODE(16))) || + (this->GetIsAlien() && GET_RUN_CODE(32)) || + (this->GetIsMarine() && GET_RUN_CODE(64)); + + if(theRunThink) + { + PROFILE_START() + CBasePlayer::PreThink(); + PROFILE_END(kCBasePlayerPreThink) + + PROFILE_START() + this->InternalPreThink(); + PROFILE_END(kPlayerInternalPreThink) + + PROFILE_START() + this->ValidateClientMoveEvents(); + PROFILE_END(kValidateClientMoveEvents) + + PROFILE_START() + this->HandleTopDownInput(); + PROFILE_END(kHandleTopDownInput) + + PROFILE_START() + this->RecalculateSpeed(); + PROFILE_END(kRecalculateSpeed) + + PROFILE_START() + if(this->mQueuedThinkMessage != "") + { + this->SendMessage(this->mQueuedThinkMessage.c_str(), TOOLTIP); + this->mQueuedThinkMessage = ""; + } + if(this->mPendingCommand) + { + // Is this bad? + GetGameRules()->ClientCommand(this, this->mPendingCommand); + this->mPendingCommand = NULL; + } + PROFILE_END(kPlayerPreThinkMisc) + } +} + +bool AvHPlayer::PayPurchaseCost(int inCost) +{ + bool theSuccess = false; + + if(GetGameRules()->GetIsCombatMode()) + { + if(inCost <= (this->GetExperienceLevel() - this->GetExperienceLevelsSpent() - 1)) + { + this->SetExperienceLevelsSpent(this->GetExperienceLevelsSpent() + inCost); + theSuccess = true; + } + } + else + { + if(inCost <= this->GetResources()) + { + this->SetResources(this->GetResources() - inCost); + theSuccess = true; + } + } + + return theSuccess; +} + +void AvHPlayer::RecalculateSpeed(void) +{ + // Look at inventory and set speed from weight + int theRelevantWeight = this->GetRelevantWeight(); + + int theMaxWeight = GetGameRules()->GetMaxWeight(); + + int theBaseSpeed, theUnencumberedSpeed; + this->GetSpeeds(theBaseSpeed, theUnencumberedSpeed); + this->mMaxWalkSpeed = theUnencumberedSpeed*.75f; + + // Calculate the max speed + int theMaxSpeed = theUnencumberedSpeed - (theRelevantWeight/(float)theMaxWeight)*(theUnencumberedSpeed - theBaseSpeed); + theMaxSpeed = max(theMaxSpeed, theBaseSpeed); + theMaxSpeed = min(theMaxSpeed, theUnencumberedSpeed); + + // Set it but only if it changed (just in case there's a hidden performance or network cost) + if(this->pev->maxspeed != theMaxSpeed) + { + //char theMessage[256]; + //sprintf(theMessage, "New weight is %d, setting speed to %d.\n", theRelevantWeight, theMaxSpeed); + //ClientPrint(this->pev, HUD_PRINTTALK, theMessage); + + this->pev->maxspeed = theMaxSpeed; + g_engfuncs.pfnSetClientMaxspeed( ENT(this->pev), theMaxSpeed); + } +} + +void AvHPlayer::ReloadWeapon(void) +{ + if(this->m_pActiveItem) + { + CBasePlayerWeapon* theGun = (CBasePlayerWeapon *)this->m_pActiveItem->GetWeaponPtr(); + if(theGun) + { + //SetAnimation(PLAYER_RELOAD); + theGun->Reload(); + } + } +} + +// Reset stuff +void AvHPlayer::ResetEntity(void) +{ + CBasePlayer::ResetEntity(); + + this->mHasSeenTeamA = false; + this->mHasSeenTeamB = false; + + this->ResetBehavior(true); + + this->UpdateTopDownMode(); + + // Preserve items we want to survive init + bool theSavedNewMap = this->mNewMap; + string theSavedDesiredNetName = this->mDesiredNetName; + AvHBaseInfoLocationListType theSavedClientInfoLocations = this->mClientInfoLocations; + + this->Init(); + + this->mNewMap = theSavedNewMap; + this->mDesiredNetName = theSavedDesiredNetName; + this->mClientInfoLocations = theSavedClientInfoLocations; + this->mMarineHUDUpgrades=0; + this->mNumSensory=0; + this->mNumMovement=0; + this->mNumDefense=0; +} + +void AvHPlayer::ResetOverwatch() +{ + // clear target + this->mOverwatchTarget = -1; + this->pev->fuser1 = -1; + this->pev->fuser2 = -1; + + // Set facing back to original facing + VectorCopy(this->mOverwatchFacing, this->pev->angles); + this->pev->fixangle = TRUE; +} + + +#include "engine/studio.h" + +void AvHPlayer::SetModelFromState() +{ + // Default to marine in ready room + char* theModelName = NULL; + + AvHUser3 theUser3 = this->GetUser3(); + + switch(theUser3) + { + case AVH_USER3_MARINE_PLAYER: + theModelName = kMarineSoldierModel; + if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_13)) + { + theModelName = kHeavySoldierModel; + } + break; + case AVH_USER3_COMMANDER_PLAYER: + theModelName = kMarineCommanderModel; + break; + case AVH_USER3_ALIEN_PLAYER1: + theModelName = kAlienLevelOneModel; + break; + case AVH_USER3_ALIEN_PLAYER2: + theModelName = kAlienLevelTwoModel; + break; + case AVH_USER3_ALIEN_PLAYER3: + theModelName = kAlienLevelThreeModel; + break; + case AVH_USER3_ALIEN_PLAYER4: + theModelName = kAlienLevelFourModel; + break; + case AVH_USER3_ALIEN_PLAYER5: + theModelName = kAlienLevelFiveModel; + break; + case AVH_USER3_ALIEN_EMBRYO: + theModelName = kAlienGestateModel; + break; + default: + if(this->GetPlayMode() == PLAYMODE_READYROOM) + { + theModelName = kReadyRoomModel; + } + break; + } + + //SET_MODEL(ENT(pev), theModelName); + + // Alternative method of setting the model on the server: + if(theModelName) + { + pev->model = MAKE_STRING(theModelName); + this->mLastModelIndex = MODEL_INDEX(theModelName); + } + + if(this->mLastModelIndex != 1) + { + pev->modelindex = mLastModelIndex; + } + + // Set body group for marine armor + this->pev->body = 0; + if(this->pev->iuser3 == AVH_USER3_MARINE_PLAYER && GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_7)) + { + this->pev->body = 1; + } +} + +void AvHPlayer::SetMoveTypeForUser3() +{ + switch(this->pev->iuser3) + { + case AVH_USER3_ALIEN_EMBRYO: +// this->pev->movetype = MOVETYPE_PUSH; +// break; + + case AVH_USER3_NONE: + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: + this->pev->movetype = MOVETYPE_WALK; + break; + } +} + +void AvHPlayer::GetSize(Vector& outMinSize, Vector& outMaxSize) const +{ + bool theIsDucking = FBitSet(this->pev->flags, FL_DUCKING); + + AvHSHUGetSizeForPlayerUser3((AvHUser3)this->pev->iuser3, outMinSize, outMaxSize, theIsDucking); +} + +void AvHPlayer::SetWeaponsForUser3() +{ + AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; + + bool theTeamHasGrenades = false; + + if(!GetGameRules()->GetIsCombatMode()) + { + AvHTeam* theTeamPointer = this->GetTeamPointer(false); + if(theTeamPointer) + { + theTeamHasGrenades = theTeamPointer->GetResearchManager().GetTechNodes().GetIsTechResearched(TECH_RESEARCH_GRENADES); + } + } + + switch(theUser3) + { + case AVH_USER3_NONE: + break; + + case AVH_USER3_MARINE_PLAYER: + if(this->mPreviousUser3 != AVH_USER3_COMMANDER_PLAYER) + { + this->GiveNamedItem(kwsMachineGun); + this->GiveNamedItem(kwsPistol); + this->GiveNamedItem(kwsKnife); + + if(theTeamHasGrenades) + { + this->GiveNamedItem(kwsGrenade); + } + } + break; + case AVH_USER3_COMMANDER_PLAYER: + break; + + // NOTE: When moving weapons/abilities around, be sure to change AvHBasePlayerWeapon::GetAnimationExtension(), and AvHSHUGetIsWeaponFocusable(). + case AVH_USER3_ALIEN_PLAYER1: + this->DestroyAllItems(FALSE); + this->GiveNamedItem(kwsBiteGun); + this->GiveNamedItem(kwsParasiteGun); + this->GiveNamedItem(kwsLeap); + this->GiveNamedItem(kwsDivineWind); + this->SwitchWeapon(kwsBiteGun); + break; + + case AVH_USER3_ALIEN_PLAYER2: + this->DestroyAllItems(FALSE); + this->GiveNamedItem(kwsHealingSpray); + this->GiveNamedItem(kwsSpitGun); + this->GiveNamedItem(kwsBileBombGun); + this->GiveNamedItem(kwsWebSpinner); + this->SwitchWeapon(kwsSpitGun); + break; + + case AVH_USER3_ALIEN_PLAYER3: + this->DestroyAllItems(FALSE); + this->GiveNamedItem(kwsBite2Gun); + //this->GiveNamedItem(kwsSpikeGun); + this->GiveNamedItem(kwsSporeGun); + this->GiveNamedItem(kwsUmbraGun); + this->GiveNamedItem(kwsPrimalScream); + this->SwitchWeapon(kwsBite2Gun); + break; + + case AVH_USER3_ALIEN_PLAYER4: + this->DestroyAllItems(FALSE); + this->GiveNamedItem(kwsSwipe); + this->GiveNamedItem(kwsBlinkGun); + this->GiveNamedItem(kwsAcidRocketGun); + this->GiveNamedItem(kwsMetabolize); + this->SwitchWeapon(kwsSwipe); + break; + + case AVH_USER3_ALIEN_PLAYER5: + this->DestroyAllItems(FALSE); + this->GiveNamedItem(kwsClaws); + this->GiveNamedItem(kwsDevour); + this->GiveNamedItem(kwsStomp); + this->GiveNamedItem(kwsCharge); + this->SwitchWeapon(kwsClaws); + break; + + case AVH_USER3_ALIEN_EMBRYO: + this->DestroyAllItems(FALSE); + break; + } +} + + +void AvHPlayer::SetSizeForUser3() +{ + AvHUser3 theUser3 = (AvHUser3)this->pev->iuser3; + Vector theMinSize; + Vector theMaxSize; + + // Use our previous User3 if we're back in the ready room after a game + if(this->GetPlayMode() == PLAYMODE_READYROOM) + { + if(this->mPreviousUser3 != AVH_USER3_NONE) + { + theUser3 = this->mPreviousUser3; + } + } + + this->GetSize(theMinSize, theMaxSize); + + UTIL_SetSize(this->pev, theMinSize, theMaxSize); + UTIL_SetOrigin(this->pev, this->pev->origin ); +} + +void AvHPlayer::GetViewForUser3(AvHUser3 inUser3, bool inIsDucking, float& outFOV, float& outOffset) const +{ + + switch(inUser3) + { + case AVH_USER3_NONE: + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_COMMANDER_PLAYER: + case AVH_USER3_ALIEN_PLAYER4: + default: + outFOV = 90; + outOffset = inIsDucking ? kDuckingViewHeightPercentage*HULL1_MAXZ: kStandingViewHeightPercentage*HULL0_MAXZ; + break; + + case AVH_USER3_ALIEN_PLAYER1: + outFOV = 105; + outOffset = 0; + break; + + case AVH_USER3_ALIEN_EMBRYO: + case AVH_USER3_ALIEN_PLAYER2: + outFOV = 100; + outOffset = 10; + break; + + case AVH_USER3_ALIEN_PLAYER3: + outFOV = 90; + outOffset = 10; + break; + + case AVH_USER3_ALIEN_PLAYER5: + outFOV = 90; + outOffset = inIsDucking ? kDuckingViewHeightPercentage*HULL0_MAXZ: kStandingViewHeightPercentage*HULL3_MAXZ; + break; + + } + +} + +void AvHPlayer::SetViewForUser3() +{ + + AvHUser3 theEndUser3 = this->GetUser3(true); + bool theIsDucking = FBitSet(this->pev->flags, FL_DUCKING); + + if (theEndUser3 == AVH_USER3_ALIEN_EMBRYO) + { + + bool theEndIsDucking = true; + + switch(GetEvolution(true)) + { + case ALIEN_LIFEFORM_ONE: + theEndUser3 = AVH_USER3_ALIEN_PLAYER1; + break; + case ALIEN_LIFEFORM_TWO: + theEndUser3 = AVH_USER3_ALIEN_PLAYER2; + break; + case ALIEN_LIFEFORM_THREE: + theEndUser3 = AVH_USER3_ALIEN_PLAYER3; + break; + case ALIEN_LIFEFORM_FOUR: + theEndUser3 = AVH_USER3_ALIEN_PLAYER4; + break; + case ALIEN_LIFEFORM_FIVE: + theEndUser3 = AVH_USER3_ALIEN_PLAYER5; + break; + default: + // For upgrades. + theEndUser3 = GetPreviousUser3(true); + break; + } + + // Linearly interpolate between the previous lifeform and the new lifeform. + + float theStartFOV; + float theStartOffset; + + float theEndFOV; + float theEndOffset; + + float amount = pev->fuser3 / kNormalizationNetworkFactor; + + AvHUser3 theStartUser3 = GetPreviousUser3(true); + + GetViewForUser3(theStartUser3, theIsDucking, theStartFOV, theStartOffset); + GetViewForUser3(theEndUser3, true, theEndFOV, theEndOffset); + + // Take into account that the origin will change for the offset. + + Vector theStartMinSize; + Vector theStartMaxSize; + + AvHSHUGetSizeForPlayerUser3(this->GetUser3(true), theStartMinSize, theStartMaxSize, theIsDucking); + + Vector theEndMinSize; + Vector theEndMaxSize; + + AvHSHUGetSizeForPlayerUser3(theEndUser3, theEndMinSize, theEndMaxSize, true); + theEndOffset += theStartMinSize.z - theEndMinSize.z; + + pev->fov = theStartFOV + amount * (theEndFOV - theStartFOV); + pev->view_ofs[2] = theStartOffset + amount * (theEndOffset - theStartOffset); + + } + else + { + GetViewForUser3(theEndUser3, theIsDucking, pev->fov, pev->view_ofs[2]); + } + + + +} + +bool AvHPlayer::SendMessage(const char *pMessage, SHOWMESSAGE_TYPE type) +{ + bool theSuccess = false; + + int theNumChars = strlen(pMessage); + if((theNumChars > 0) && (theNumChars < kMaxPlayerSendMessageLength)) + { + string theMessage(pMessage); + if(theMessage != this->mLastMessageSent) + { + UTIL_ShowMessage2(pMessage, this, type); + + this->mLastMessageSent = theMessage; + + theSuccess = true; + } + else + { + int a = 0; + } + } +// else +// { +// // Log error to console +// char theErrorMessage[10000]; +// sprintf(theErrorMessage, "Can't send message \"%s\" of length %d, max size is %d", pMessage, theNumChars, kMaxPlayerSendMessageLength); +// ALERT(at_logged, theErrorMessage); +// } + + return theSuccess; +} + +bool AvHPlayer::SendMessageOnce(const char *pMessage, SHOWMESSAGE_TYPE type) +{ + bool theSentMessage = false; + + string theMessage(pMessage); + + // Check if message is in sent list + StringList::iterator theIter = std::find(this->mSentMessageList.begin(), this->mSentMessageList.end(), theMessage); + if(theIter == this->mSentMessageList.end()) + { + // If not + // Call SendMessage + theSentMessage = this->SendMessage(pMessage, type); + + this->mLastMessageSent = theMessage; + + // Add message to list + this->mSentMessageList.push_back(theMessage); + } + + return theSentMessage; +} + +bool AvHPlayer::SendMessageNextThink(const char* pMessage) +{ + this->mQueuedThinkMessage = string(pMessage); + return true; +} + +void AvHPlayer::SetCurrentCommand(const struct usercmd_s* inCommand) +{ + memcpy(&this->mCurrentCommand, inCommand, sizeof(*inCommand)); +} + +void AvHPlayer::SetDebugCSP(weapon_data_t* inWeaponData) +{ + CBasePlayerWeapon* theCurrentWeapon = dynamic_cast(this->m_pActiveItem); + if(theCurrentWeapon && (theCurrentWeapon->m_iId == inWeaponData->m_iId)) + { + memcpy(&this->mDebugCSPInfo, inWeaponData, sizeof(weapon_data_t)); + } +} + +void AvHPlayer::StartObserver( Vector vecPosition, Vector vecViewAngle ) +{ + Vector theFadeColor; + theFadeColor.x = 0; + theFadeColor.y = 0; + theFadeColor.z = 0; + + UTIL_ScreenFade(this, theFadeColor, kTransitionFadeTime, 0.0f, 255, FFADE_IN); + + CBasePlayer::StartObserver(vecPosition, vecViewAngle); +} + +void AvHPlayer::ResetBehavior(bool inRemoveFromTeam) +{ + // remove observer mode if enabled + this->StopObserver(); + + // Leave top down mode if in it + this->StopTopDownMode(); + + // Stop digesting if you are + this->StopDigestion(false); + + // Stop being digested + this->SetBeingDigestedMode(false); + + // Reset room sounds + this->SetDesiredRoomType(0, true); + + // remove all equipment, but don't drop it (how to do this?) + this->DestroyAllItems(FALSE); + + if(inRemoveFromTeam) + { + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + theTeam->RemovePlayer(this->entindex()); + } + + // Clear experience + this->mExperience = 0.0f; + this->mExperienceLevelsSpent = 0; + this->mCombatNodes.Clear(); + this->mPurchasedCombatUpgrades.clear(); + this->mGiveCombatUpgrades.clear(); + } +} + +void AvHPlayer::SetPlayMode(AvHPlayMode inPlayMode, bool inForceSpawn) +{ + if(this->pev->playerclass != inPlayMode || inForceSpawn) + { + bool theGoingToReadyRoom = (inPlayMode == PLAYMODE_READYROOM); + this->ResetBehavior(theGoingToReadyRoom); + + if(!theGoingToReadyRoom) + { + // Clear player + //this->Init(); + this->ClearUserVariables(); + this->pev->rendermode = kRenderNormal; + this->pev->renderfx = kRenderFxNone; + this->pev->renderamt = 0; + } + + // Clear anim + this->m_szAnimExtention[0] = '\0'; + +// this->mUpgrades.clear(); + + AvHTeamNumber theTeamNumber = AvHTeamNumber(this->pev->team); + AvHUser3 theUser3 = AVH_USER3_NONE; + bool theSetUser3 = false; + + string theMessage; + + AvHTeam* theTeam = this->GetTeamPointer(false); + string theTeamName = kUndefinedTeam; + + switch(inPlayMode) + { + // Initialize stuff + case PLAYMODE_UNDEFINED: + this->pev->iuser3 = AVH_USER3_NONE; + this->pev->playerclass = PLAYMODE_UNDEFINED; + this->pev->team = TEAM_IND; + respawn(this->pev, FALSE); + break; + + case PLAYMODE_READYROOM: + this->pev->playerclass = PLAYMODE_READYROOM; + + if(theTeam) + { + theTeam->RemovePlayer(this->entindex()); + } + + this->pev->frags = 0; + this->mScore = 0; + this->mSavedCombatFrags = 0; + this->m_iDeaths = 0; + this->pev->team = TEAM_IND; + + // So we don't have the sideways camera because we're dead + this->pev->health = 100; + this->pev->max_health = pev->health; + this->pev->armorvalue = 0; + + respawn(this->pev, FALSE); + + if(this->pev->iuser3 == AVH_USER3_ALIEN_EMBRYO) + { + // Stop sound and allow movement, else we're stuck in the ready room + UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kGestationSound, 1.0f, .5, SND_STOP, 100); + SetUpgradeMask(&this->pev->iuser4, MASK_ALIEN_EMBRYO, false); + } + // Else "commander" draws on the scoreboard and it doesn't make sense to have a commander on the ground + else if(this->pev->iuser3 == AVH_USER3_COMMANDER_PLAYER) + { + this->pev->iuser3 = AVH_USER3_MARINE_PLAYER; + } + //else if(this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER5) + //{ + // // Set player ducking to improve chances of them not getting stuck + // SetBits( m_afPhysicsFlags, PFLAG_DUCKING ); + // SetBits( this->pev->flags, FL_DUCKING ); + // this->pev->origin.z += 25; + //} + + theMessage = kReadyRoomMessage; + theTeamName = kUndefinedTeam; + break; + + case PLAYMODE_PLAYING: + // Don't set playmode if we couldn't respawn + this->pev->playerclass = PLAYMODE_PLAYING; + + //respawn(this->pev, FALSE); + + // Account for both sides, or to let player choose it somehow + if(this->GetClassType() == AVH_CLASS_TYPE_MARINE) + { + theUser3 = AVH_USER3_MARINE_PLAYER; + } + else + { + theUser3 = AVH_USER3_ALIEN_PLAYER1; // TEMPORARY + + // In combat mode, spawn player in as most advanced lifeform so player doesn't get stuck. + if(GetGameRules()->GetIsCombatMode() && !GetGameRules()->GetIsHamboneMode()) + { + // Find the most advanced lifeform + for(MessageIDListType::const_iterator theIter = this->mGiveCombatUpgrades.begin(); theIter != this->mGiveCombatUpgrades.end(); theIter++) + { + AvHMessageID theCurrentCombatUpgrade = *theIter; + switch(theCurrentCombatUpgrade) + { + case ALIEN_LIFEFORM_TWO: + theUser3 = AVH_USER3_ALIEN_PLAYER2; + break; + case ALIEN_LIFEFORM_THREE: + theUser3 = AVH_USER3_ALIEN_PLAYER3; + break; + case ALIEN_LIFEFORM_FOUR: + theUser3 = AVH_USER3_ALIEN_PLAYER4; + break; + case ALIEN_LIFEFORM_FIVE: + theUser3 = AVH_USER3_ALIEN_PLAYER5; + break; + } + } + } + } + + this->SetUser3(theUser3, true, false); + + respawn(this->pev, FALSE); + + this->SetWeaponsForUser3(); + + // In combat mode, add all player upgrades + if(GetGameRules()->GetIsCombatMode()) + { + if(GetGameRules()->GetIsHamboneMode()) + { + // Reset tech nodes + this->mGiveCombatUpgrades.clear(); + this->mCombatNodes = this->GetTeamPointer()->GetTechNodes(); + this->mExperienceLevelsSpent = 0; + } + else if(!GetGameRules()->GetIsIronMan()) + { + // Respend upgrades + this->GiveCombatUpgradesOnSpawn(); + } + } + + theTeam = this->GetTeamPointer(); + theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); + + this->mLastTimeStartedPlaying = gpGlobals->time; + this->mHasLeftReadyRoom = true; + break; + + case PLAYMODE_AWAITINGREINFORCEMENT: + //this->mRole = AVH_USER3_NONE; + // Preserve team, we could be brought back in + this->pev->team = theTeamNumber; + this->pev->playerclass = PLAYMODE_AWAITINGREINFORCEMENT; + + theTeam = this->GetTeamPointer(); + theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); + + this->StartObservingIfNotAlready(); + + theTeam = this->GetTeamPointer(); + theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); + theMessage = kReinforcementMessage; + this->mHasLeftReadyRoom = true; + break; + + case PLAYMODE_REINFORCING: + this->pev->team = theTeamNumber; + this->pev->playerclass = PLAYMODE_REINFORCING; + + theTeam = this->GetTeamPointer(); + theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); + + this->StartObservingIfNotAlready(); + + this->SendMessage(kReinforcingMessage, TOOLTIP); + this->mHasLeftReadyRoom = true; + break; + + case PLAYMODE_OBSERVER: + + if(theTeam) + { + theTeam->RemovePlayer(this->entindex()); + } + + // Set observer mode + this->StartObservingIfNotAlready(); + + // Set current team to indeterminate; we aren't allowed to join from spectating + this->pev->team = TEAM_IND; + this->pev->playerclass = PLAYMODE_OBSERVER; + + // Remember that we have spectated + this->mHasBeenSpectator = true; + theMessage = kObserverMessage; + theTeamName = kSpectatorTeam; + this->mHasLeftReadyRoom = true; + + this->SetHasSeenTeam(GetGameRules()->GetTeamA()->GetTeamNumber()); + this->SetHasSeenTeam(GetGameRules()->GetTeamB()->GetTeamNumber()); + break; + + case PLAYMODE_REINFORCINGCOMPLETE: + this->pev->playerclass = PLAYMODE_REINFORCINGCOMPLETE; + this->SendMessage(kReinforcementComplete, NORMAL); + break; + } + + // Force reset of entities because we just respawned + //this->ResetPlayerPVS(); + + // Inform gamerules of the change + GetGameRules()->ChangePlayerTeam(this, theTeamName.c_str(), false, false); + if(theTeam) + { + this->SetHasSeenTeam(theTeam->GetTeamNumber()); + } + + // Inform scoreboard + this->EffectivePlayerClassChanged(); + + if(theMessage != "") + { + // Send instructions to player + this->SendMessageNextThink(theMessage.c_str()); + } + } +} + +void AvHPlayer::GetNewOrigin(AvHUser3 inNewUser3, bool inCheckDucking, vec3_t& outOrigin) const +{ + + vec3_t theOldMinSize; + vec3_t theOldMaxSize; + + GetSize(theOldMinSize, theOldMaxSize); + + vec3_t theNewMinSize; + vec3_t theNewMaxSize; + + AvHSHUGetSizeForPlayerUser3(inNewUser3, theNewMinSize, theNewMaxSize, inCheckDucking); + + VectorCopy(pev->origin, outOrigin); + outOrigin[2] += theOldMinSize.z - theNewMinSize.z; + +} + +void AvHPlayer::SetUser3(AvHUser3 inUser3, bool inForceChange, bool inGiveWeapons) +{ + if((inUser3 != this->pev->iuser3) || inForceChange) + { + + // Make us duck so that it's easier to gestate in small areas. + + if(AvHMUGetCanDuck(this->pev->iuser3)) + { + + SetBits(this->pev->flags, FL_DUCKING); + SetBits( m_afPhysicsFlags, PFLAG_DUCKING ); + + // Important or the animations will get screwed up! + + m_Activity = ACT_RESET; + SetAnimation( PLAYER_IDLE ); + + } + + bool theIsDucking = FBitSet(this->pev->flags, FL_DUCKING); + + // Save off the current size of the player so that we can adjust the + // origin later. + + Vector theOldMinSize; + Vector theOldMaxSize; + + this->GetSize(theOldMinSize, theOldMaxSize); + + vec3_t theNewOrigin; + GetNewOrigin(inUser3, true, theNewOrigin); + + this->mPreviousUser3 = (AvHUser3)this->pev->iuser3; + + // Leave old User3 + switch(this->mPreviousUser3) + { + case AVH_USER3_MARINE_PLAYER: + break; + case AVH_USER3_ALIEN_PLAYER5: + this->StopDigestion(false); + break; + case AVH_USER3_COMMANDER_PLAYER: + this->StopTopDownMode(); + break; + } + + string theMessage; + + this->pev->iuser3 = inUser3; + + // Drop inventory, clear abilities + //this->DestroyAllItems(FALSE); + + bool theSavedAlienSightActive = this->mAlienSightActive; + + this->ClearRoleAbilities(); + + int theSavedUser4 = this->pev->iuser4; + bool theSavedIsSpectator = this->GetIsSpectator(); + + float theJetpackEnergy = mSavedJetpackEnergy; + mSavedJetpackEnergy = this->pev->fuser3; + + this->ClearUserVariables(); + + switch(inUser3) + { + case AVH_USER3_NONE: + this->pev->team = TEAM_IND; + break; + + case AVH_USER3_MARINE_PLAYER: + this->pev->iuser3 = inUser3; + theMessage = kSoldierMessage; + this->pev->movetype = MOVETYPE_WALK; + break; + case AVH_USER3_COMMANDER_PLAYER: + this->pev->iuser3 = inUser3; + this->StartTopDownMode(); + theMessage = kCommanderMessage; + break; + + // NOTE: When moving weapons/abilities around, be sure to change AvHBasePlayerWeapon::GetAnimationExtension() also + case AVH_USER3_ALIEN_PLAYER1: + this->pev->iuser3 = inUser3; + this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; + break; + + case AVH_USER3_ALIEN_PLAYER2: + this->pev->iuser3 = inUser3; + this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; + break; + + case AVH_USER3_ALIEN_PLAYER3: + this->pev->iuser3 = inUser3; + this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; + break; + + case AVH_USER3_ALIEN_PLAYER4: + this->pev->iuser3 = inUser3; + this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; + break; + + case AVH_USER3_ALIEN_PLAYER5: + this->pev->iuser3 = inUser3; + this->pev->fuser3 = 1.0f*kNormalizationNetworkFactor; + this->mLastGallopViewDirection = gpGlobals->v_forward; + break; + + case AVH_USER3_ALIEN_EMBRYO: + this->pev->iuser3 = inUser3; + this->pev->iuser4 |= MASK_ALIEN_EMBRYO; + theMessage = kGestationMessage; + break; + } + + // Preserve upgrades on a role change + this->pev->iuser4 |= theSavedUser4; + + // All players are selectable + this->pev->iuser4 |= MASK_SELECTABLE; + + // Get team-wide upgrades + AvHTeam* theTeamPointer = this->GetTeamPointer(); + if(theTeamPointer) + { + this->pev->iuser4 |= theTeamPointer->GetTeamWideUpgrades(); + } + + if(inGiveWeapons) + { + this->SetWeaponsForUser3(); + } + + // Adjust the size for the new user3. + + this->SetSizeForUser3(); + + // Adjust the origin of the player so that they are still on the ground. + + //Vector theNewMinSize; + //Vector theNewMaxSize; + //this->GetSize(theNewMinSize, theNewMaxSize); + //this->pev->origin[2] += theOldMinSize.z - theNewMinSize.z; + + if(this->mPreviousUser3 != AVH_USER3_COMMANDER_PLAYER) + { + UTIL_SetOrigin(this->pev, theNewOrigin); + } + + this->SetViewForUser3(); + this->SetMoveTypeForUser3(); + + if(theSavedIsSpectator) + { + this->SetIsSpectator(); + } + + if(this->pev->playerclass == PLAYMODE_AWAITINGREINFORCEMENT) + { + this->pev->playerclass = PLAYMODE_PLAYING; + } + + //this->SetModelFromState(); + float theHealthPercentage = 1.0f; + float theArmorPercentage = 1.0f; + if((this->mPreviousUser3 != AVH_USER3_NONE) /*&& (inUser3 != AVH_USER3_ALIEN_EMBRYO)*/) + { + theHealthPercentage = (float)this->pev->health/AvHPlayerUpgrade::GetMaxHealth(theSavedUser4, this->mPreviousUser3, this->GetExperienceLevel()); + theArmorPercentage = (float)this->pev->armorvalue/AvHPlayerUpgrade::GetMaxArmorLevel(theSavedUser4, this->mPreviousUser3); + } + + if (mPreviousUser3 == AVH_USER3_COMMANDER_PLAYER) + { + // Restore the jetpack energy from when the player went into the CC. + this->pev->fuser3 = theJetpackEnergy; + } + + //char theInitializeMessage[128]; + //sprintf(theInitializeMessage, "Initializing to user3: %d with health/armor percentages: %f/%f\n", inUser3, theHealthPercentage, theArmorPercentage); + //ClientPrint(this->pev, HUD_PRINTTALK, theInitializeMessage); + + this->InitializeFromTeam(theHealthPercentage, theArmorPercentage); + + this->SetModelFromState(); + + if(this->GetIsAlien()) + { + this->mAlienSightActive = theSavedAlienSightActive; + } + else + { + this->mAlienSightActive = false; + } + + // Update team + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + theTeam->ProcessRankChange(this, this->mPreviousUser3, this->GetUser3()); + } + + // Update scoreboard + this->EffectivePlayerClassChanged(); + + if(theMessage != "") + { + // Send instructions to player + this->SendMessageOnce(theMessage.c_str(), TOOLTIP); + } + this->LogEmitRoleChange(); + } +} + +void AvHPlayer::SetResources(float inResources, bool inPlaySound) +{ + if(!GetGameRules()->GetIsCombatMode()) + { + if(inResources < 0) + { + inResources = 0; + } + + AvHTeam* theTeam = this->GetTeamPointer(); + ASSERT(theTeam != NULL); + + if(this->GetIsMarine()) + { + if(inPlaySound) + { + this->PlayHUDSound(HUD_SOUND_MARINE_POINTS_RECEIVED); + + AvHPlayer* theCommander = this->GetCommander(); + if(theCommander) + { + theCommander->PlayHUDSound(HUD_SOUND_MARINE_POINTS_RECEIVED); + } + } + + theTeam->SetTeamResources(inResources); + } + else if(this->GetIsAlien()) + { + if(inPlaySound) + { + this->PlayHUDSound(HUD_SOUND_ALIEN_POINTS_RECEIVED); + } + if ( this->mResources != inResources ) + this->EffectivePlayerClassChanged(); + + this->mResources = inResources; + } + + this->InternalBoundResources(); + + if(this->GetIsAlien()) + { + AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(); + if(theServerPlayerData) + { + theServerPlayerData->SetResources(this->mResources); + } + } + } +} + +void AvHPlayer::Spawn( void ) +{ + CBasePlayer::Spawn(); + //this->PrecacheAndSetPlayerModel(); + + pev->classname = MAKE_STRING(kAvHPlayerClassName); + + this->mSendSpawnScreenFade = true; + + if(this->pev->playerclass != PLAYMODE_READYROOM) + { + this->PlayRandomRoleSound(kPlayerLevelSpawnSoundList, CHAN_ITEM, this->GetAlienAdjustedEventVolume()); + } + + //SET_MODEL(ENT(pev), "models/headcrab.mdl"); + //UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); + + SetTouch(&AvHPlayer::PlayerTouch); + + // Stop spectating + this->pev->iuser1 = 0; +} + +void AvHPlayer::StartObservingIfNotAlready(void) +{ + // Prevent this is the cvar is set + if ( allow_spectators.value ) + { + //CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pev); + if(!this->IsObserver()) + //if(!pPlayer->IsObserver()) + { + //if(this->mPlayMode == PLAYMODE_AWAITINGREINFORCEMENT) + //{ + // TODO: Start observer mode in chase cam on friendlies + //} + //else + //{ + edict_t *pentSpawnSpot = GetGameRules()->SelectSpawnPoint( this /*pPlayer*/ ); + if(!FNullEnt(pentSpawnSpot)) + { + this->StartObserver( VARS(pentSpawnSpot)->origin, VARS(pentSpawnSpot)->angles); + } + else + { + this->StartObserver( this->pev->origin, this->pev->angles); + } + } + } + else + { + this->ObserverModeIllegal(); + } +} + +bool AvHPlayer::SetBeingDigestedMode(bool inBeingDigested) +{ + bool theSuccess = false; + bool theIsDigesting = this->GetIsBeingDigested(); + + SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, false); + + if(inBeingDigested && !theIsDigesting) + { + // Fade player to black +// Vector theFadeColor; +// theFadeColor.x = 0; +// theFadeColor.y = 0; +// theFadeColor.z = 0; +// UTIL_ScreenFade(this, theFadeColor, .7f, 0.0f, 255, FFADE_OUT | FFADE_STAYOUT); + + this->HolsterCurrent(); + + this->pev->solid = SOLID_NOT; + this->pev->effects |= EF_NODRAW; + this->pev->takedamage = DAMAGE_NO; + + this->pev->movetype = MOVETYPE_FLY; + this->m_afPhysicsFlags |= PFLAG_OBSERVER; + + ClearBits( m_afPhysicsFlags, PFLAG_DUCKING ); + ClearBits( this->pev->flags, FL_DUCKING ); + VectorCopy(g_vecZero, this->pev->velocity); + + theSuccess = true; + } + else if(!inBeingDigested && theIsDigesting) + { + // Fade player up from black + Vector theFadeColor; + theFadeColor.x = 0; + theFadeColor.y = 0; + theFadeColor.z = 0; + UTIL_ScreenFade(this, theFadeColor, 1.0f, 0.0f, 255, FFADE_IN); + this->SetDesiredRoomType(0); + + this->DeployCurrent(); + + // Set physics + this->pev->solid = SOLID_SLIDEBOX; + this->pev->effects &= ~EF_NODRAW; + this->pev->takedamage = DAMAGE_YES; + + this->pev->movetype = MOVETYPE_WALK; + ClearBits(this->m_afPhysicsFlags, PFLAG_OBSERVER); + + // Set player ducking to improve chances of them not getting stuck + SetBits( m_afPhysicsFlags, PFLAG_DUCKING ); + SetBits( this->pev->flags, FL_DUCKING ); + + VectorCopy(g_vecZero, this->pev->velocity); + this->pev->fixangle = TRUE; + + theSuccess = true; + } + + if(theSuccess) + { + // Set digesting flag + SetUpgradeMask(&this->pev->iuser4, MASK_DIGESTING, inBeingDigested); + this->EffectivePlayerClassChanged(); + } + + return theSuccess; +} + +void AvHPlayer::StartTopDownMode() +{ + if(!this->mInTopDownMode && !this->GetCurrentWeaponCannotHolster()) + { + Vector theFadeColor; + theFadeColor.x = 0; + theFadeColor.y = 0; + theFadeColor.z = 0; + UTIL_ScreenFade(this, theFadeColor, kTransitionFadeTime, 0.0f, 255, FFADE_IN); + + VectorCopy(this->pev->origin, this->mPositionBeforeTopDown); + VectorCopy(this->pev->angles, this->mAnglesBeforeTopDown); + VectorCopy(this->pev->v_angle, this->mViewAnglesBeforeTopDown); + VectorCopy(this->pev->view_ofs, this->mViewOfsBeforeTopDown); + this->mAnimExtensionBeforeTopDown = this->m_szAnimExtention; + + this->HolsterCurrent(); + + this->mTimeStartedTopDown = gpGlobals->time; + + this->mOverwatchEnabled = false; + SetUpgradeMask(&this->pev->iuser4, MASK_TOPDOWN); + this->m_afPhysicsFlags |= PFLAG_OBSERVER; + this->pev->effects |= EF_NODRAW; + this->pev->view_ofs = g_vecZero; + this->pev->gravity = 0; + + this->pev->solid = SOLID_NOT; + this->pev->takedamage = DAMAGE_NO; + + //this->pev->movetype = MOVETYPE_NOCLIP; + //this->pev->movetype = MOVETYPE_WALK; + this->pev->movetype = MOVETYPE_FLY; + this->m_afPhysicsFlags |= PFLAG_OBSERVER; + + ClearBits( m_afPhysicsFlags, PFLAG_DUCKING ); + ClearBits( this->pev->flags, FL_DUCKING ); + //this->pev->deadflag = DEAD_RESPAWNABLE; + //this->pev->deadflag = DEAD_RESPAWNABLE; + //this->pev->velocity[0] = 0.0f; + //this->pev->velocity[1] = 0.0f; + //this->pev->velocity[2] = -1.0f; + + +// float theMinViewHeight, theMaxViewHeight; +// float theMinX, theMaxX; +// float theMinY, theMaxY; +// bool theDrawMapBG; +// GetGameRules()->GetMapExtents(theMinViewHeight, theMaxViewHeight, theMinX, theMinY, theMaxX, theMaxY, theDrawMapBG); + + this->pev->origin.z = GetGameRules()->GetMapExtents().GetMaxViewHeight(); + + this->mInTopDownMode = true; + + // Cheesy way to make sure player class change is sent to everyone + this->EffectivePlayerClassChanged(); + } +} + +bool AvHPlayer::GetHasLeftReadyRoom() const +{ + return this->mHasLeftReadyRoom; +} + +bool AvHPlayer::GetHasJetpack() const +{ + return this->GetIsMarine() && GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_7); +} + +bool AvHPlayer::GetHasHeavyArmor() const +{ + return this->GetIsMarine() && GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_13); +} + +bool AvHPlayer::GetHasGivenOrder() const +{ + return this->mHasGivenOrder; +} + +void AvHPlayer::SetHasGivenOrder(bool inState) +{ + this->mHasGivenOrder = inState; +} + +float AvHPlayer::GetTimeLastF4() const +{ + return this->mTimeOfLastF4; +} + +void AvHPlayer::SetTimeLastF4(float inTime) +{ + this->mTimeOfLastF4=inTime; +} + +float AvHPlayer::GetTimeStartedTopDown() const +{ + return this->mTimeStartedTopDown; +} + +float AvHPlayer::GetTimeOfLastSignificantCommanderAction() const +{ + return this->mTimeOfLastSignificantCommanderAction; +} + +bool AvHPlayer::GetHasAvailableUpgrades() const +{ + bool theHasPendingUpgrades = false; + + AvHTeam* theTeamPointer = this->GetTeamPointer(); + if(theTeamPointer) + { + AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); + + for(int i = ALIEN_UPGRADE_CATEGORY_INVALID + 1; i < ALIEN_UPGRADE_CATEGORY_MAX_PLUS_ONE; i++) + { + AvHAlienUpgradeCategory theCurrentCategory = AvHAlienUpgradeCategory(i); + + // Now make sure we have an unspent upgrade available + if(AvHGetHasFreeUpgradeCategory(theCurrentCategory, theUpgrades, this->pev->iuser4)) + { + theHasPendingUpgrades = true; + break; + } + } + } + + return theHasPendingUpgrades; +} + + +bool AvHPlayer::GetHasPowerArmor() const +{ + return this->GetIsMarine() && GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_10); +} + +int AvHPlayer::GetHull() const +{ + int theHull = AvHMUGetHull(true, this->pev->iuser3); + + return theHull; +} + +bool AvHPlayer::GetIsTemporarilyInvulnerable() const +{ + bool theIsInvulnerable = false; + + if(GetGameRules()->GetIsCombatMode() && (this->GetPlayMode() == PLAYMODE_PLAYING)) + { + if(this->mLastTimeStartedPlaying != -1) + { + float theInvulnerableTimeSeconds = 0.0f; + + #ifdef DEBUG + theInvulnerableTimeSeconds = avh_spawninvulnerabletime.value; + #endif + + if(gpGlobals->time < (this->mLastTimeStartedPlaying + theInvulnerableTimeSeconds)) + { + theIsInvulnerable = true; + } + } + } + + if (mTimeOfLastRedeem != -1 && gpGlobals->time < mTimeOfLastRedeem + kRedeemInvulnerableTime) + { + theIsInvulnerable = true; + } + + return theIsInvulnerable; +} + +bool AvHPlayer::GetIsEnsnared() const +{ + return GetHasUpgrade(this->pev->iuser4, MASK_ENSNARED); +} + +bool AvHPlayer::GetIsAbleToAct() const +{ + return !GetIsInTopDownMode() && !GetIsBeingDigested() && !GetIsEnsnared() && !GetIsStunned(); +} + +bool AvHPlayer::SetEnsnareState(bool inState) +{ + bool theSuccess = true; + + if(inState) + { + // If too ensnared already, don't ensnare further + if(!this->GetIsEnsnared()) + { + this->mTimeToBeUnensnared = gpGlobals->time; + } + + if(!this->GetIsEnsnared() || ((this->mTimeToBeUnensnared + BALANCE_VAR(kEnsnareTime) - gpGlobals->time) < BALANCE_VAR(kMaxEnsnareTime))) + { + this->mLastTimeEnsnared = gpGlobals->time; + this->mTimeToBeUnensnared += BALANCE_VAR(kEnsnareTime); + + // Player is defenseless + this->HolsterCurrent(); + + SetUpgradeMask(&this->pev->iuser4, MASK_ENSNARED); + } + else + { + theSuccess = false; + } + } + else + { + SetUpgradeMask(&this->pev->iuser4, MASK_ENSNARED, false); + this->mTimeToBeUnensnared = -1; + this->mLastTimeEnsnared = -1; + + this->DeployCurrent(); + } + + return theSuccess; +} + +bool AvHPlayer::GetIsStunned() const +{ + return GetHasUpgrade(this->pev->iuser4, MASK_PLAYER_STUNNED); +} + +bool AvHPlayer::SetIsStunned(bool inState, float inTime) +{ + bool theSuccess = false; + + // Only able to stun walking players (prevents weird problems with players on ladders, who are treated as flying + if(inState && !this->GetIsStunned() && (this->pev->movetype == MOVETYPE_WALK)) + { + SetUpgradeMask(&this->pev->iuser4, MASK_PLAYER_STUNNED); + this->mTimeToBeFreeToMove = gpGlobals->time + inTime; + + Vector theFadeColor; + theFadeColor.x = 255; + theFadeColor.y = 255; + theFadeColor.z = 255; + float theFadeTime = .25f; + UTIL_ScreenFade(this, theFadeColor, theFadeTime, 0.0f, 128, FFADE_IN/* | FFADE_MODULATE*/); + + theSuccess = true; + + // Clear keys so they aren't held down + //this->ClearKeys(); + } + else if(!inState && this->GetIsStunned()) + { + SetUpgradeMask(&this->pev->iuser4, MASK_PLAYER_STUNNED, false); + this->mTimeToBeFreeToMove = -1; + } + + return theSuccess; +} + +bool AvHPlayer::GetIsCatalysted() const +{ + return this->GetIsMarine() && GetHasUpgrade(this->pev->iuser4, MASK_BUFFED); +} + +void AvHPlayer::SetIsCatalysted(bool inState, float inTime) +{ + if(this->GetIsMarine()) + { + if(inState && !this->GetIsCatalysted()) + { + SetUpgradeMask(&this->pev->iuser4, MASK_BUFFED); + this->mTimeToEndCatalyst = gpGlobals->time + inTime; + + // Trigger screen effect? + } + else + { + SetUpgradeMask(&this->pev->iuser4, MASK_BUFFED, false); + this->mTimeToEndCatalyst = -1; + } + } +} + +bool AvHPlayer::Energize(float inEnergyAmount) +{ + bool theSuccess = false; + + if(AvHMUGiveAlienEnergy(this->pev->fuser3, inEnergyAmount)) + { + theSuccess = true; + } + + return theSuccess; +} + +bool AvHPlayer::Heal(float inAmount, bool inPlaySound, bool dcHealing) +{ + bool theDidHeal = false; + if ( gpGlobals->time > this->mTimeOfLastDCRegeneration + BALANCE_VAR(kDefenseChamberThinkInterval) - 0.05f || (dcHealing == false) ) { + if ( dcHealing ) { + this->mTimeOfLastDCRegeneration = gpGlobals->time; + } + + int theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + int theMaxArmor = AvHPlayerUpgrade::GetMaxArmorLevel(this->pev->iuser4, (AvHUser3)this->pev->iuser3); + + float theAmount = inAmount; + + // If we aren't at full health, heal health + if(this->pev->health < theMaxHealth) + { + int theAmountToGive = theAmount; + theAmount -= (theMaxHealth - this->pev->health); //store relative amount compared to that necessary for complete heal + this->pev->health = min((float)theMaxHealth, this->pev->health + theAmountToGive); + theDidHeal = true; + } + else if(this->pev->armorvalue < theMaxArmor) + { + this->pev->armorvalue = min((float)theMaxArmor, this->pev->armorvalue + theAmount); + theDidHeal = true; + } + + // Play regen event + if(theDidHeal) + { + if(inPlaySound) + { + // Play regeneration event + PLAYBACK_EVENT_FULL(0, this->edict(), gRegenerationEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + } + } + } + return theDidHeal; +} + +bool AvHPlayer::Regenerate(float inRegenerationAmount, bool inPlaySound, bool dcHealing) +{ + bool theDidRegenerate = this->Heal(inRegenerationAmount, inPlaySound); + + if(theDidRegenerate) + { + this->mTimeOfLastRegeneration = gpGlobals->time; + } + + return theDidRegenerate; +} + +bool AvHPlayer::GetCanBeResupplied() const +{ + bool theCanBeResupplied = false; + + const float theResupplyTime = BALANCE_VAR(kResupplyTime); + + if((this->mTimeOfLastResupply == 0) || (gpGlobals->time > (this->mTimeOfLastResupply + theResupplyTime))) + { + if(this->m_pActiveItem) + { + AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem->GetWeaponPtr()); + if(theBaseWeapon && theBaseWeapon->CanHolster() && theBaseWeapon->GetCanBeResupplied()) + { + theCanBeResupplied = true; + } + } + + // If we don't have max health, or we need ammo + if(this->pev->health < this->pev->max_health) + { + theCanBeResupplied = true; + } + } + return theCanBeResupplied; +} + +bool AvHPlayer::Resupply(bool inGiveHealth) +{ + bool theSuccess = false; + + if(this->m_pActiveItem) + { + AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(this->m_pActiveItem->GetWeaponPtr()); + if(theBaseWeapon && theBaseWeapon->Resupply()) + { + theSuccess = true; + } + + if(inGiveHealth) + { + // : 1017 armoury gives 10 health per use + if(AvHHealth::GiveHealth(this, BALANCE_VAR(kPointsPerArmouryHealth))) + { + // Play event for each person helped + //PLAYBACK_EVENT_FULL(0, this->edict(), gPhaseInEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); + theSuccess = true; + } + } + + this->mTimeOfLastResupply = gpGlobals->time; + } + + return theSuccess; +} + +bool AvHPlayer::GetIsScreaming() +{ + return this->mIsScreaming; +} + +void AvHPlayer::StartScreaming() +{ + this->mIsScreaming = true; + this->mTimeStartedScream = gpGlobals->time; +} + +bool AvHPlayer::StopTopDownMode() +{ + bool theSuccess = false; + + if(this->mInTopDownMode) + { + Vector theFadeColor; + theFadeColor.x = 0; + theFadeColor.y = 0; + theFadeColor.z = 0; + UTIL_ScreenFade(this, theFadeColor, kTransitionFadeTime, 0.0f, 255, FFADE_IN); + + this->DeployCurrent(); + + this->mOverwatchEnabled = true; + this->pev->effects &= ~EF_NODRAW; + this->pev->view_ofs = g_vecZero; + SetUpgradeMask(&this->pev->iuser4, MASK_TOPDOWN, false); + + // TODO: Make sure original gravity is 1? + this->pev->gravity = 1; + + this->pev->solid = SOLID_SLIDEBOX; + this->pev->takedamage = DAMAGE_YES; + + //this->pev->movetype = MOVETYPE_NONE; + this->pev->movetype = MOVETYPE_WALK; + ClearBits(this->m_afPhysicsFlags, PFLAG_OBSERVER); + this->pev->deadflag = DEAD_NO; + + VectorCopy(this->mPositionBeforeTopDown, this->pev->origin); + VectorCopy(this->mAnglesBeforeTopDown, this->pev->angles); + VectorCopy(this->mViewAnglesBeforeTopDown, this->pev->v_angle); + VectorCopy(this->mViewOfsBeforeTopDown, this->pev->view_ofs); + strcpy(this->m_szAnimExtention, this->mAnimExtensionBeforeTopDown.c_str()); + + VectorCopy(g_vecZero, this->pev->velocity); + this->pev->fixangle = TRUE; + this->mInTopDownMode = false; + + AvHTeamNumber theStationTeamNumber = (AvHTeamNumber)this->pev->team; + const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationLogoutTeamOne : kTargetCommandStationLogoutTeamTwo; + FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f); + + this->mSelected.clear(); + + // Need to reset groups when logging out, so they are re-propagated when logging in after another potential commander (see AvHHud::ResetTopDownUI) + for(int i = 0; i < kNumHotkeyGroups; i++) + { + this->mClientGroupAlerts[i] = ALERT_NONE; + + this->mClientGroups[i].clear(); + } + + this->mClientSelectAllGroup.clear(); + + theSuccess = true; + } + + return theSuccess; +} + +void AvHPlayer::SetPendingCommand(char* inCommand) +{ + this->mPendingCommand = inCommand; +} + +void AvHPlayer::TriggerFog(int inFogEntity, float inFogExpireTime) +{ + this->mCurrentFogEntity = inFogEntity; + this->mFogExpireTime = inFogExpireTime; + + // Allows resetting of fog entity + if(inFogEntity > -1) + { + this->mTimeOfLastFogTrigger = gpGlobals->time; + } +} + +void AvHPlayer::TriggerProgressBar(int inEntityID, int inParam, int inPercent) +{ + ASSERT(inEntityID >= 0); + + this->mProgressBarEntityIndex = inEntityID; + this->mProgressBarParam = inParam; + this->mProgressBarCompleted = inPercent; + this->mTimeProgressBarTriggered = gpGlobals->time; + +} + +float AvHPlayer::GetTimeOfLastTeleport() const +{ + return this->mTimeOfLastTeleport; +} + +float AvHPlayer::GetTimeLastPlaying() const +{ + return this->mTimeLastPlaying; +} + + +bool AvHPlayer::HolsterWeaponToUse() +{ + bool theSuccess = false; + + if(!this->GetCurrentWeaponCannotHolster()) + { + this->HolsterCurrent(); + + this->mTimeOfLastUse = gpGlobals->time; + + theSuccess = true; + } + return theSuccess; +} + +void AvHPlayer::SetTimeOfLastTeleport(float inTime) +{ + this->mTimeOfLastTeleport = inTime; +} + +void AvHPlayer::BecomePod() +{ + //ASSERT(this->mRole != AVH_USER3_ALIEN_EMBRYO); + + this->HolsterCurrent(); + + ClearBits(this->m_afPhysicsFlags, PFLAG_DUCKING); + ClearBits(this->pev->flags, FL_DUCKING); + + //EMIT_SOUND_DYN(ENT(this->pev), CHAN_VOICE, kGestationSound, 1, ATTN_NORM, 0, 100); + + float flSilenceLevel = this->GetAlienAdjustedEventVolume(); + + if(flSilenceLevel > 0.0) + UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kGestationSound, flSilenceLevel, 2.0, 0, 100); +} + +bool AvHPlayer::SwitchWeapon(const char* inString) +{ + bool theSuccess = false; + + if(!this->GetIsEnsnared()) + { + CBasePlayerWeapon* theCurrentWeapon; + + for (int i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + theCurrentWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); + while( theCurrentWeapon ) + { + if(FClassnameIs(theCurrentWeapon->pev, inString)) + { + // this weapon is from the same category. + if ( theCurrentWeapon->CanDeploy() ) + { + theSuccess = CBasePlayer::SwitchWeapon( theCurrentWeapon ); + } + break; + } + theCurrentWeapon = dynamic_cast(theCurrentWeapon->m_pNext); + } + } + } + + return theSuccess; +} + +//BOOL AvHPlayer::SwitchWeapon( CBasePlayerItem* inWeapon ) +//{ +// CBasePlayerWeapon* theCurrentWeapon = dynamic_cast(this->m_pActiveItem); +// CBasePlayerItem* theWeapon = inWeapon; +// BOOL theSuccess = TRUE; +// +// if(!inWeapon) +// { +// if(theCurrentWeapon) +// { +// theCurrentWeapon->RetireWeapon(); +// } +// else +// { +// theSuccess = FALSE; +// } +// } +// else +// { +// CBasePlayer::SwitchWeapon(theWeapon); +// } +// +// return theSuccess; +//} + + +void AvHPlayer:: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if ( pev->takedamage && GetCanBeAffectedByEnemies()) + { + m_LastHitGroup = ptr->iHitgroup; + + // No locational damage in NS. + + /* + switch ( ptr->iHitgroup ) + { + case HITGROUP_GENERIC: + break; + case HITGROUP_HEAD: + flDamage *= gSkillData.plrHead; + break; + case HITGROUP_CHEST: + flDamage *= gSkillData.plrChest; + break; + case HITGROUP_STOMACH: + flDamage *= gSkillData.plrStomach; + break; + case HITGROUP_LEFTARM: + case HITGROUP_RIGHTARM: + flDamage *= gSkillData.plrArm; + break; + case HITGROUP_LEFTLEG: + case HITGROUP_RIGHTLEG: + flDamage *= gSkillData.plrLeg; + break; + default: + break; + } + */ + + // Player's aren't affected by structural damage, so don't create blood + // if that's the damage type. + + if (!(bitsDamageType & NS_DMG_STRUCTURAL)) + { + SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage); + TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); + } + + AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); + + } +} + + +int AvHPlayer::TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) +{ + //Even out the damage + //flDamage = ceil(flDamage); + + int theReturnValue = 0; + + if(GetGameRules()->GetGameStarted() && !this->GetIsTemporarilyInvulnerable()) + { + // Take into account handicap + if(!pevAttacker) + { + pevAttacker = pevInflictor; + } + + if(!pevInflictor) + { + pevInflictor = pevAttacker; + } + + AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(pevAttacker->team)); + if(theTeam) + { + float theHandicap = theTeam->GetHandicap(); + flDamage *= theHandicap; + } + + if(GetGameRules()->GetIsCheatEnabled(kcHighDamage)) + { + flDamage *= 10; + } + + if(bitsDamageType & NS_DMG_STRUCTURAL) + { + flDamage = 0.0f; + } + + // Do half damage to the heavy armor of HA and Onos + if(bitsDamageType & NS_DMG_LIGHT) + { + if(this->GetHasHeavyArmor() || (this->pev->iuser3 == AVH_USER3_ALIEN_PLAYER5)) + { + flDamage *= .5f; + } + } + + // If we're metabolizing, convert the damage to energy +// if(this->GetIsMetabolizing()) +// { +// const float theFactor = BALANCE_VAR(kMetabolizeDamageEnergyFactor); +// float theEnergy = (flDamage/100.f)*theFactor; +// AvHMUGiveAlienEnergy(this->pev->fuser3, theEnergy); +// +// if((this->mTimeOfLastMetabolizeEvent == -1) || (gpGlobals->time > (this->mTimeOfLastMetabolizeEvent + 1.0f))) +// { +// // Playback metabolize success event +// PLAYBACK_EVENT_FULL(0, this->edict(), gMetabolizeSuccessEventID, 0, this->pev->origin, (float *)&g_vecZero, this->GetAlienAdjustedEventVolume(), 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); +// +// this->mTimeOfLastMetabolizeEvent = gpGlobals->time;; +// } +// +// theReturnValue = 0; +// } +// else +// { + + theReturnValue = CBasePlayer::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + if(theReturnValue > 0) + { + float theSlowDownFactor = .8f; + float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(this->pev->iuser4, (AvHUser3)this->pev->iuser3, this->GetExperienceLevel()); + + if(flDamage > theMaxHealth/2.0f) + { + this->PlayRandomRoleSound(kPlayerLevelWoundSoundList, CHAN_BODY, 1.0); + theSlowDownFactor = .3f; + } + else if(flDamage >= theMaxHealth/5.0f) + { + this->PlayRandomRoleSound(kPlayerLevelPainSoundList, CHAN_BODY, .8f); + theSlowDownFactor = .5f; + } + + // Slow down when hit + //VectorScale(this->pev->velocity, theSlowDownFactor, this->pev->velocity); + + if(!pevAttacker || (this->pev->team != pevAttacker->team) && (pevAttacker->team != 0)) + { + GetGameRules()->TriggerAlert((AvHTeamNumber)this->pev->team, ALERT_PLAYER_ENGAGE, this->entindex()); + } + + if(pevAttacker) + { + CBasePlayer* inAttackingPlayer = dynamic_cast(CBaseEntity::Instance(ENT(pevAttacker))); + const char* inWeaponName = STRING(pevInflictor->classname); + if(inAttackingPlayer && inWeaponName) + { + this->LogPlayerAttackedPlayer(inAttackingPlayer, inWeaponName, flDamage); + } + } + + bool theDrawDamage = (ns_cvar_float(&avh_drawdamage) > 0); + + if(theDrawDamage) + { + this->PlaybackNumericalEvent(kNumericalInfoHealthEvent, (int)(-flDamage)); + } + + this->Uncloak(); + } +// } + } + + return theReturnValue; +} + +void AvHPlayer::PlaybackNumericalEvent(int inEventID, int inNumber) +{ + Vector theMinSize; + Vector theMaxSize; + this->GetSize(theMinSize, theMaxSize); + + Vector theStartPos = this->pev->origin; + theStartPos.z += theMaxSize.z; + + // Draw for everyone (team = 0 after flDamage parameter) + AvHSUPlayNumericEvent(inNumber, this->edict(), theStartPos, 0, inEventID, this->pev->team); +} + + +//const char* AvHPlayer::TeamID( void ) +//{ +// AvHTeam* theTeam = this->GetTeamPointer(); +// const char* theTeamName = (theTeam ? theTeam->GetTeamName() : kUndefinedTeam); +// //const char* theTeamName = this->GetPlayerModelKeyName(); +// return theTeamName; +//} + +void AvHPlayer::TurnOffOverwatch() +{ + this->mInOverwatch = false; + AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); + if(theWeapon) + { + theWeapon->SetOverwatchState(false); + } + + //VectorCopy(this->mOverwatchFacing, this->pev->angles); + //this->pev->fixangle = TRUE; + + //ASSERT(this->pev->iuser4 == AVH_USER4_OVERWATCH); + //this->pev->iuser4 &= ~MASK_MARINE_OVERWATCH; + this->pev->fuser1 = -1; + this->pev->fuser2 = -1; +} + +void AvHPlayer::TurnOnOverwatch() +{ + this->mInOverwatch = true; + + // Remember facing when we entered overwatch + VectorCopy(this->pev->angles, this->mOverwatchFacing); + + // if so, set overwatch on, make sure to set the current weapon into overwatch + AvHBasePlayerWeapon* theWeapon = dynamic_cast(this->m_pActiveItem); + ASSERT(theWeapon); + theWeapon->SetOverwatchState(true); + + // Flip on overwatch, clear target, it will be acquired in think + this->mOverwatchTarget = -1; + this->pev->fuser1 = -1; + this->pev->fuser2 = -1; + //this->pev->iuser4 |= MASK_MARINE_OVERWATCH; +} + +void AvHPlayer::TurnOverwatchTowardsTarget(CBaseEntity* theTarget) +{ + // TODO: Take gun offset into account with vecMid? + Vector vecMid = pev->origin + pev->view_ofs; + Vector vecMidEnemy = theTarget->BodyTarget( vecMid ); + + // Right now just point at enemy + Vector vecDirToEnemy = vecMidEnemy - vecMid; + Vector vec = UTIL_VecToAngles(vecDirToEnemy); + + vec.x = -vec.x; + +// if (vec.y > 360) +// vec.y -= 360; +// +// if (vec.y < 0) +// vec.y += 360; + + VectorCopy(vec, this->pev->angles); + VectorCopy(vec, this->pev->v_angle); + this->pev->fixangle = TRUE; +} + +bool AvHPlayer::RunClientScript(const string& inScriptName) +{ + // Returns false if client scripts aren't enabled + bool theSuccess = false; + + this->mPendingClientScripts.push_back(inScriptName); + theSuccess = true; + + return theSuccess; +} + +void AvHPlayer::PrintWeaponListToClient(CBaseEntity *theAvHPlayer) { + char msg[1024]; + sprintf(msg, "Weapons for %s:\n", this->GetPlayerName()); + ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, msg); + + for(int i = 0; i < MAX_ITEM_TYPES; i++) + { + int j=0; + AvHBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); + while(theActiveWeapon) + { + char msg[1024]; + sprintf(msg, "%d[%d] ", i, j); + ClientPrint(theAvHPlayer->pev, HUD_PRINTNOTIFY, msg); + theActiveWeapon->PrintWeaponToClient(theAvHPlayer); + + // Next weapon + theActiveWeapon = dynamic_cast(theActiveWeapon->m_pNext); + j++; + } + } +} +void AvHPlayer::UpdateInventoryEnabledState(int inNumActiveHives, bool inForceUpdate) +{ + // Have we not yet updated our weapons with this # of hives? + if((inNumActiveHives != this->mNumHives) || (inForceUpdate)) + { + for(int i = 0; i < MAX_ITEM_TYPES; i++) + { + AvHBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); + while(theActiveWeapon) + { + theActiveWeapon->UpdateInventoryEnabledState(inNumActiveHives); + + // Next weapon + theActiveWeapon = dynamic_cast(theActiveWeapon->m_pNext); + } + } + } + + // Save # of hives we've last updated with + this->mNumHives = inNumActiveHives; +} + +bool AvHPlayer::GetIsBeingDigested() const +{ + bool theIsBeingDigested = false; + + if(GetHasUpgrade(this->pev->iuser4, MASK_DIGESTING)) + { + if(this->pev->effects & EF_NODRAW) + { + theIsBeingDigested = true; + } + } + + return theIsBeingDigested; +} + +bool AvHPlayer::GetIsDigesting() const +{ + bool theIsDigesting = false; + + if(GetHasUpgrade(this->pev->iuser4, MASK_DIGESTING)) + { + if(! (this->pev->effects & EF_NODRAW)) + { + theIsDigesting = true; + } + } + + return theIsDigesting; +} + +void AvHPlayer::UpdateAmbientSounds() +{ + AvHClassType theClassType = this->GetClassType(); + if(theClassType == AVH_CLASS_TYPE_MARINE) + { + } + else if(theClassType == AVH_CLASS_TYPE_ALIEN) + { + // Get role + AvHUser3 theUser3 = this->GetUser3(); + int theAlienLevel = theUser3 - AVH_USER3_COMMANDER_PLAYER; + int theVelocity = this->pev->velocity.Length(); + bool theIsMoving = theVelocity > 100; + int theSilenceLevel = AvHGetAlienUpgradeLevel(this->pev->iuser4, MASK_UPGRADE_6); + +// if(RANDOM_LONG(0, 40) == 0) +// { +// char theVelocityMsg[128]; +// sprintf(theVelocityMsg, "alien moving at velocity: %d", theVelocity); +// ClientPrint(this->pev, HUD_PRINTTALK, theVelocityMsg); +// } + + // if moving, check chance for playing moving sound + if(!this->GetIsCloaked() && !this->mIsScreaming /*&& !this->GetIsBlinking()*/) + { + float theSilenceVolumeFactor = this->GetAlienAdjustedEventVolume(); + + if(theIsMoving) + { + int theBaseSpeed, theMaxSpeed; + this->GetSpeeds(theBaseSpeed, theMaxSpeed); + + float theAlienSoundFreq = 0.003f; + float theChanceOfPlayingSound = theAlienSoundFreq*(theVelocity/((float)theMaxSpeed)); + if(RANDOM_FLOAT(0, 1) < theChanceOfPlayingSound) + { + float theVolume = RANDOM_FLOAT(.5, 1.0)*theSilenceVolumeFactor; + if(theVolume > 0.01f) + { + this->PlayRandomRoleSound(kPlayerLevelMoveSoundList, CHAN_VOICE, theVolume); + } + } + } + else + { + if(GetHasUpgrade(this->pev->iuser4, MASK_BUFFED)) + { + float theSilenceVolumeFactor = this->GetAlienAdjustedEventVolume(); + + // If player is part of primal scream, scream defiance! + if(RANDOM_FLOAT(0, 1) < .02f && theSilenceVolumeFactor > 0.0) + { + EMIT_SOUND(this->edict(), CHAN_VOICE, kPrimalScreamResponseSound, theSilenceVolumeFactor, ATTN_NORM); + } + } + else + { + bool theIsGestating = (this->GetUser3() == AVH_USER3_ALIEN_EMBRYO); + + // if idle, check chance for playing idle sound + float theBaseChance = 0.0005f; + if(theIsGestating) + { + theBaseChance *= 10; + } + + if(RANDOM_FLOAT(0, 1) < theBaseChance) + { + float theVolume = RANDOM_FLOAT(.2, .4)*theSilenceVolumeFactor; + if(theVolume > 0.01f) + { + if(theIsGestating) + { + EMIT_SOUND(this->edict(), CHAN_AUTO, kEggIdleSound, theVolume, ATTN_NORM); + } + else + { + this->PlayRandomRoleSound(kPlayerLevelIdleSoundList, CHAN_VOICE, theVolume); + } + } + } + } + } + } + } + + if(this->mDesiredRoomType != this->mClientDesiredRoomType) + { + MESSAGE_BEGIN( MSG_ONE, SVC_ROOMTYPE, NULL, this->pev); + WRITE_SHORT( (short)this->mDesiredRoomType); + MESSAGE_END(); + + this->mClientDesiredRoomType = this->mDesiredRoomType; + } +} + +void AvHPlayer::UpdateAlienUI() +{ + AvHTeam* theTeamPointer = this->GetTeamPointer(); + bool theIsMarine = false; + bool theIsAlien = false; + + if(this->GetIsAlien()) + { + theIsAlien = true; + } + else if(this->GetIsMarine()) + { + theIsMarine = true; + } + + // Update when going back to ready room, so check if not-marine instead of is-alien + if(!theIsMarine) + { + bool theCanRespawn = GetGameRules()->FPlayerCanRespawn(this); + + AvHTeam* theTeamPointer = this->GetTeamPointer(); + if(theTeamPointer) + { + AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); + + int thePreSize = theUpgrades.size(); + + // Trim max upgrades in each category + AvHRemoveIrrelevantUpgrades(theUpgrades); + + if(theUpgrades != this->mClientUpgrades) + { + NetMsg_AlienInfo_Upgrades( this->pev, theUpgrades ); + this->mClientUpgrades = theUpgrades; + } + + HiveInfoListType theTeamHiveInfo = theTeamPointer->GetHiveInfoList(); + if(this->mClientHiveInfo != theTeamHiveInfo) + { + NetMsg_AlienInfo_Hives( this->pev, theTeamHiveInfo, this->mClientHiveInfo ); + this->mClientHiveInfo = theTeamHiveInfo; + } + } + + if ( theIsAlien ) { + int currentMask=0; + currentMask |= ( this->mNumSensory & 0x3 ); + currentMask <<= 2; + currentMask |= ( this->mNumDefense & 0x3 ); + currentMask <<= 2; + currentMask |= ( this->mNumMovement & 0x3 ); + currentMask |= 0x80; + + int teamMask=0; + AvHEntityHierarchy& theEntHier=GetGameRules()->GetEntityHierarchy(this->GetTeam()); + teamMask |= ( theEntHier.GetNumSensory() & 0x3 ); + teamMask <<= 2; + teamMask |= ( theEntHier.GetNumDefense() & 0x3 ); + teamMask <<= 2; + teamMask |= ( theEntHier.GetNumMovement() & 0x3 ); + teamMask |= 0x80; + + if ( currentMask != teamMask ) { + this->mNumSensory=theEntHier.GetNumSensory(); + this->mNumDefense=theEntHier.GetNumDefense(); + this->mNumMovement=theEntHier.GetNumMovement(); + NetMsg_HUDSetUpgrades(this->pev, teamMask); + } + } + } +} + +void AvHPlayer::UpdateMarineUI() +{ + AvHTeam* theTeamPointer = this->GetTeamPointer(); + bool theIsMarine = false; + bool theIsAlien = false; + + if(this->GetIsAlien()) + { + return; + } + + int tmpUpgrades=0; + for(int i = 0; i < MAX_ITEM_TYPES; i++) + { + AvHBasePlayerWeapon* theActiveWeapon = dynamic_cast(this->m_rgpPlayerItems[i]); + while(theActiveWeapon) + { + ItemInfo ii; + theActiveWeapon->GetItemInfo(&ii); + switch ( ii.iId ) { + case AVH_WEAPON_WELDER: + tmpUpgrades |= 0x1; + break; + case AVH_WEAPON_MINE: + tmpUpgrades |= 0x2; + break; + case AVH_WEAPON_GRENADE: + tmpUpgrades |= 0x4; + break; + } + // Next weapon + theActiveWeapon = dynamic_cast(theActiveWeapon->m_pNext); + } + } + if ( tmpUpgrades != this->mMarineHUDUpgrades ) { + NetMsg_HUDSetUpgrades(this->pev, tmpUpgrades&0x7); + this->mMarineHUDUpgrades=tmpUpgrades; + } +} + +// TODO: Send only changed blips, send only the changes for each blip. +void AvHPlayer::UpdateBlips() +{ + if(this->mEnemyBlips != this->mClientEnemyBlips) + { + NetMsg_BlipList( this->pev, false, this->mEnemyBlips ); + this->mClientEnemyBlips = this->mEnemyBlips; + } + + if(this->mFriendlyBlips != this->mClientFriendlyBlips) + { + NetMsg_BlipList( this->pev, true, this->mFriendlyBlips ); + this->mClientFriendlyBlips = this->mFriendlyBlips; + } +} + +void AvHPlayer::UpdateClientData( void ) +{ + if(GET_RUN_CODE(128)) + { + //UTIL_LogPrintf("UpdateClientData starting.\n"); + CBasePlayer::UpdateClientData(); + + // Update one-shot stuff + this->UpdateFirst(); + this->UpdateParticleTemplates(); + this->UpdateInfoLocations(); + this->UpdateOrders(); + this->UpdateVUser4(); + this->UpdateProgressBar(); + this->UpdateSetSelect(); + this->UpdateTechNodes(); + this->UpdateBalanceVariables(); + this->UpdateSoundNames(); + this->UpdateTopDownMode(); + this->UpdateEntityHierarchy(); + this->UpdateExperienceLevelsSpent(); + this->UpdateSpawnScreenFade(); + this->UpdateEffectiveClassAndTeam(); + //this->UpdateArmor(); + //this->UpdateOverwatch(); + this->UpdatePendingClientScripts(); + this->UpdateGamma(); + this->UpdateBlips(); + this->UpdateAlienUI(); + this->UpdateMarineUI(); + this->UpdateFog(); + + //this->UpdateDebugCSP(); + } + //UTIL_LogPrintf("UpdateClientData done.\n"); +} + +void AvHPlayer::UpdateEffectiveClassAndTeam() +{ + // Don't send too many messages when these get updated really quickly. Too many messages are being sent on a game reset, and it's not needed. We only need the most recent message. + const float kClassAndTeamUpdateRate = .6f; + if((this->mTimeOfLastClassAndTeamUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastClassAndTeamUpdate + kClassAndTeamUpdateRate))) + { + if(this->mEffectivePlayerClassChanged) + { + ScoreInfo info; + AvHSUFillScoreInfo(info, this); + NetMsg_ScoreInfo( info ); + this->mEffectivePlayerClassChanged = false; + } + + if(this->mNeedsTeamUpdate) + { + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( i ); + if ( plr && GetGameRules()->IsValidTeam( plr->TeamID() ) ) + { + NetMsg_TeamInfo( this->pev, plr->entindex(), plr->TeamID() ); + } + } + this->mNeedsTeamUpdate = false; + } + + if(this->mSendTeamUpdate) + { + // notify everyone's HUD of the team change + NetMsg_TeamInfo( this->entindex(), this->TeamID() ); + this->mSendTeamUpdate = false; + } + + this->mTimeOfLastClassAndTeamUpdate = gpGlobals->time; + } +} + +void AvHPlayer::UpdateFirst() +{ + if(this->mFirstUpdate) + { + // Tell this player to reset + int theState = (this->mNewMap ? kGameStatusResetNewMap : kGameStatusReset); + NetMsg_GameStatus_State( this->pev, theState, GetGameRules()->GetMapMode() ); + + if(this->mNewMap) + { + NetMsg_SetSoundNames( this->pev, true, string() ); + this->mClientSoundNames.clear(); + + // Send down map extents so players can start computing it + // Cache this so it isn't computed every round, only the when a player connects or a new map starts? + const char* theCStrLevelName = STRING(gpGlobals->mapname); + const AvHMapExtents& theMapExtents = GetGameRules()->GetMapExtents(); + ASSERT(theCStrLevelName); + ASSERT(!FStrEq(theCStrLevelName, "")); + + float mins[3] = { theMapExtents.GetMinMapX(), theMapExtents.GetMinMapY(), theMapExtents.GetMinViewHeight() }; + float maxs[3] = { theMapExtents.GetMaxMapX(), theMapExtents.GetMaxMapY(), theMapExtents.GetMaxViewHeight() }; + + NetMsg_SetupMap_Extents( this->pev, string( theCStrLevelName ), mins, maxs, theMapExtents.GetDrawMapBG() ); + } + + this->mFirstUpdate = false; + this->mNewMap = false; + } +} + +void AvHPlayer::UpdateFog() +{ + if(this->mClientCurrentFogEntity != this->mCurrentFogEntity) + { + bool theFogEnabled = this->mCurrentFogEntity > -1; + int theR, theG, theB; + float theStart, theEnd; + + if(theFogEnabled) + { + AvHFog* theFogEntity = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCurrentFogEntity))); + ASSERT(theFogEntity); + + theFogEntity->GetFogColor(theR, theG, theB); + theStart = theFogEntity->GetFogStart(); + theEnd = theFogEntity->GetFogEnd(); + } + + NetMsg_Fog( this->pev, theFogEnabled, theR, theG, theB, theStart, theEnd ); + this->mClientCurrentFogEntity = this->mCurrentFogEntity; + } +} + +void AvHPlayer::UpdateGamma() +{ + float theMapGamma = GetGameRules()->GetMapGamma(); + if(this->mClientGamma != theMapGamma) + { + if(!GetGameRules()->GetIsTesting()) + { + NetMsg_SetGammaRamp( this->pev, theMapGamma ); + this->mClientGamma = theMapGamma; + } + } +} + +void AvHPlayer::UpdateOrders() +{ + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + OrderListType theTeamOrders; + theTeam->GetOrders(theTeamOrders); + + for(OrderListType::iterator theIter = theTeamOrders.begin(); theIter != theTeamOrders.end(); theIter++) + { + bool theClientHasOrder = false; + AvHOrder theClientOrder; + + // Does client already have this order? + for(OrderListType::iterator theClientIter = this->mClientOrders.begin(); theClientIter != this->mClientOrders.end(); theClientIter++) + { + if(theIter->GetOrderID() == theClientIter->GetOrderID()) + { + theClientHasOrder = true; + theClientOrder = *theClientIter; + break; + } + } + + if(!theClientHasOrder || theClientOrder != *theIter) + { + NetMsg_SetOrder( this->pev, *theIter ); + } + } + + this->mClientOrders = theTeamOrders; + } +} + +void AvHPlayer::UpdateParticleTemplates() +{ + const float kParticleTemplateRate = 1.0f; + if(gParticleTemplateList.GetCreatedTemplates()) + { + // Make sure client clears out all particle systems first + int theNumberTemplates = gParticleTemplateList.GetNumberTemplates(); + if(theNumberTemplates > this->mNumParticleTemplatesSent) + { + if((this->mTimeOfLastParticleTemplateSending == -1) || (gpGlobals->time > this->mTimeOfLastParticleTemplateSending + kParticleTemplateRate)) + { + AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(this->mNumParticleTemplatesSent); + ASSERT(theTemplate); + NetMsg_SetParticleTemplate( this->pev, this->mNumParticleTemplatesSent, *theTemplate ); + this->mNumParticleTemplatesSent++; + this->mTimeOfLastParticleTemplateSending = gpGlobals->time; + } + } + } +} + +void AvHPlayer::UpdateInfoLocations() +{ + // Get map location list + const AvHBaseInfoLocationListType& theInfoLocations = GetGameRules()->GetInfoLocations(); + + // Compare with ours, send one down each tick (assumes that previous ones sent don't change) + int theNumClientInfoLocations = this->mClientInfoLocations.size(); + if((signed)theInfoLocations.size() > theNumClientInfoLocations) + { + // Only send one at a time + AvHBaseInfoLocation theInfoLocation = theInfoLocations[theNumClientInfoLocations]; + + vec3_t theMaxExtents = theInfoLocation.GetMaxExtent(); + vec3_t theMinExtents = theInfoLocation.GetMinExtent(); + float mins[3] = { theMinExtents.x, theMinExtents.y, theMinExtents.z }; + float maxs[3] = { theMaxExtents.x, theMaxExtents.y, theMinExtents.z }; + + NetMsg_SetupMap_Location( this->pev, theInfoLocation.GetLocationName(), mins, maxs ); + this->mClientInfoLocations.push_back(theInfoLocation); + } + +} + +void AvHPlayer::UpdatePendingClientScripts() +{ + if(this->mPendingClientScripts.size() > 0) + { + NetMsg_ClientScripts( this->pev, this->mPendingClientScripts ); + this->mPendingClientScripts.clear(); + } +} + +void AvHPlayer::UpdateProgressBar() +{ + // TODO: If this is the commander, send him all the progress bars of all his teammates so he can see them! + + // Assumes that progress is normalized and stored in one of the fuser variables of the entity index sent down + if(this->mClientProgressBarEntityIndex != this->mProgressBarEntityIndex) + { + NetMsg_ProgressBar( this->pev, this->mProgressBarEntityIndex, this->mProgressBarParam, this->mProgressBarCompleted ); + this->mClientProgressBarEntityIndex = this->mProgressBarEntityIndex; + } + // onos digestion uses a parameter to the network message + else if ( this->mProgressBarParam == 5 ) { + NetMsg_ProgressBar( this->pev, this->mProgressBarEntityIndex, this->mProgressBarParam, this->mProgressBarCompleted ); + } +} + +void AvHPlayer::UpdateVUser4() +{ + // Update client with resources (as int) + int theResources = (short)(this->GetResources(true)); +#ifdef DEBUG + if(ns_cvar_float(&avh_testing)) + { + theResources = g_engfuncs.pfnNumberOfEntities(); + } +#endif + + if(this->pev) + { + if(GetGameRules()->GetIsCombatMode()) + { + this->pev->vuser4.z = this->GetExperience()*kNumericNetworkConstant; + } + else + { + this->pev->vuser4.z = theResources*kNumericNetworkConstant; + } + } +} + +void AvHPlayer::UpdateSetSelect() +{ + if(this->GetIsInTopDownMode()) + { + if((this->mSelected != this->mClientSelected) || (this->mTrackingEntity != this->mClientTrackingEntity)) + { + Selection selection; + selection.group_number = 0; + selection.selected_entities = this->mSelected; + selection.tracking_entity = max( this->mTrackingEntity, 0 ); + + NetMsg_SetSelect( this->pev, selection ); + + // Synch up + this->mClientSelected = this->mSelected; + this->mClientTrackingEntity = this->mTrackingEntity; + } + + AvHTeam* theTeam = this->GetTeamPointer(); + ASSERT(theTeam); + + for(int j=0; j < kNumHotkeyGroups; j++) + { + EntityListType theGroup = theTeam->GetGroup(j); + EntityListType& theClientGroup = this->mClientGroups[j]; + AvHUser3 theGroupType = theTeam->GetGroupType(j); + AvHAlertType theGroupAlert = ALERT_NONE; + AvHAlertType& theClientGroupAlert = this->mClientGroupAlerts[j]; + + // Is group under attack or no longer under attack? + for(EntityListType::iterator theIter = theGroup.begin(); theIter != theGroup.end(); theIter++) + { + if(GetGameRules()->GetIsEntityUnderAttack(*theIter)) + { + theGroupAlert = ALERT_UNDER_ATTACK; + } + } + + if((theClientGroup != theGroup) || (theClientGroupAlert != theGroupAlert)) + { + Selection selection; + selection.group_number = j+1; + selection.selected_entities = theGroup; + selection.group_type = theGroupType; + selection.group_alert = theGroupAlert; + + NetMsg_SetSelect( this->pev, selection ); + + theClientGroup = theGroup; + theClientGroupAlert = theGroupAlert; + } + } + + // See if "selectall" hotgroup has changed and send it if needed + EntityListType theSelectAllGroup = theTeam->GetSelectAllGroup(); + if(theSelectAllGroup != this->mClientSelectAllGroup) + { + Selection selection; + selection.group_number = kSelectAllHotGroup; + selection.selected_entities = theSelectAllGroup; + + NetMsg_SetSelect( this->pev, selection ); + + this->mClientSelectAllGroup = theSelectAllGroup; + } + + // Check idle soldiers, ammo requests and health requests + AvHMessageID theRequestList[kNumRequestTypes] = {COMMANDER_NEXTIDLE, COMMANDER_NEXTAMMO, COMMANDER_NEXTHEALTH}; + for(int i = 0; i < kNumRequestTypes; i++) + { + AvHMessageID theCurrentRequestType = theRequestList[i]; + int theNumClientRequests = this->mClientRequests[i]; + int theNumTeamRequests = theTeam->GetAlerts(theCurrentRequestType).size(); + if(theNumClientRequests != theNumTeamRequests) + { + NetMsg_SetRequest( this->pev, theCurrentRequestType, theNumTeamRequests ); + this->mClientRequests[i] = theNumTeamRequests; + } + } + } +} + +void AvHPlayer::UpdateSoundNames() +{ + if(this->pev != NULL ) // Not fully connected yet + { + // Send list of special sounds + const StringList& theSoundNameList = AvHMP3Audio::GetSoundNameList(); + int theNumberOfSounds = theSoundNameList.size(); + int theNumberOfSoundsOnClient = this->mClientSoundNames.size(); + + ASSERT(theNumberOfSoundsOnClient <= theNumberOfSounds); + + // Only send one new sound name every tick, to avoid sending too much data and overflowing too quickly + if(theNumberOfSounds > theNumberOfSoundsOnClient) + { + const char* theSoundNameToSend = theSoundNameList[theNumberOfSoundsOnClient].c_str(); + if(GetGameRules()->GetIsTesting()) + { + this->SendMessage(theSoundNameToSend); + } + ASSERT( strlen(theSoundNameToSend) < 50); + NetMsg_SetSoundNames( this->pev, false, theSoundNameToSend); + + this->mClientSoundNames.push_back(theSoundNameToSend); + } + } +} + +//TODO: (KGP) there are a lot of expensive per-frame operations here that can be eliminated through careful refactoring. +// 1) make AvHTechTree an abstract interface +// 2) create base case using current AvHTechTree code +// 3) create filter around AvHTechTree that uses override of IsResearchable by MessageID and returns AvHTechNode objects +// that reflect the filter. +// 4) create AvHTechChangeListener class and use it as basis for decision to send tech nodes +// 5) create NetMsg_SetTechNodeDelta function that bundles state changes for multiple nodes into a single call +// 6) always use a personal copy of AvHTechNodes interface for each player to eliminate the per-frame copy of the team nodes +// 7) use filter class for NS mode aliens and update state of the filter when alien lifeform changes instead of using per-frame update +// Combined, these changes should reduce CPU overhead for tech node update by at least 90%. +void AvHPlayer::UpdateTechNodes() +{ + bool theIsCombatMode = GetGameRules()->GetIsCombatMode(); + bool theIsNSMode = GetGameRules()->GetIsNSMode(); + if((this->GetUser3() == AVH_USER3_COMMANDER_PLAYER) || theIsCombatMode || this->GetIsAlien()) + { + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam) + { + // Propagate and use local tech nodes in combat mode, else use team nodes in NS mode + AvHTechTree& theTechNodes = theIsCombatMode ? this->mCombatNodes : theTeam->GetTechNodes(); + + // Now customize nodes for aliens in NS + if(theIsNSMode && this->GetIsAlien()) + { + // Set current lifeform to be unavailable + AvHMessageID theLifeform = MESSAGE_NULL; + switch(this->GetUser3()) + { + case AVH_USER3_ALIEN_PLAYER1: + theLifeform = ALIEN_LIFEFORM_ONE; + break; + case AVH_USER3_ALIEN_PLAYER2: + theLifeform = ALIEN_LIFEFORM_TWO; + break; + case AVH_USER3_ALIEN_PLAYER3: + theLifeform = ALIEN_LIFEFORM_THREE; + break; + case AVH_USER3_ALIEN_PLAYER4: + theLifeform = ALIEN_LIFEFORM_FOUR; + break; + case AVH_USER3_ALIEN_PLAYER5: + theLifeform = ALIEN_LIFEFORM_FIVE; + break; + } + // 0001075 : Reset techs to researchable before flagging them otherwise + theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_ONE, true); + theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_TWO, true); + theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_THREE, true); + theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_FOUR, true); + theTechNodes.SetIsResearchable(ALIEN_LIFEFORM_FIVE, true); + theTechNodes.SetIsResearchable(theLifeform, false); + + // 0001075 : Reset techs to researchable before flagging them otherwise + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_ONE, true); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TWO, true); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_THREE, true); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_SEVEN, true); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_EIGHT, true); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_NINE, true); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TEN, true); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_ELEVEN, true); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TWELVE, true); + + // If not Gorge, set buildables to be unavailable + if(theLifeform != ALIEN_LIFEFORM_TWO) + { + theTechNodes.SetIsResearchable(ALIEN_BUILD_HIVE, false); + theTechNodes.SetIsResearchable(ALIEN_BUILD_RESOURCES, false); + theTechNodes.SetIsResearchable(ALIEN_BUILD_OFFENSE_CHAMBER, false); + theTechNodes.SetIsResearchable(ALIEN_BUILD_DEFENSE_CHAMBER, false); + theTechNodes.SetIsResearchable(ALIEN_BUILD_MOVEMENT_CHAMBER, false); + theTechNodes.SetIsResearchable(ALIEN_BUILD_SENSORY_CHAMBER, false); + } + else + { + // 0001075 : Reset techs to researchable before flagging them otherwise + theTechNodes.SetIsResearchable(ALIEN_BUILD_HIVE, true); + theTechNodes.SetIsResearchable(ALIEN_BUILD_RESOURCES, true); + theTechNodes.SetIsResearchable(ALIEN_BUILD_OFFENSE_CHAMBER, true); + theTechNodes.SetIsResearchable(ALIEN_BUILD_DEFENSE_CHAMBER, true); + theTechNodes.SetIsResearchable(ALIEN_BUILD_MOVEMENT_CHAMBER, true); + theTechNodes.SetIsResearchable(ALIEN_BUILD_SENSORY_CHAMBER, true); + + // If we have the max hives, disable hives + + bool theHasFreeUpgradeCategory = false; + MessageIDListType theUnsupportedUpgrades; + theUnsupportedUpgrades.push_back(ALIEN_BUILD_DEFENSE_CHAMBER); + theUnsupportedUpgrades.push_back(ALIEN_BUILD_MOVEMENT_CHAMBER); + theUnsupportedUpgrades.push_back(ALIEN_BUILD_SENSORY_CHAMBER); + + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if(theEntity && theEntity->GetIsActive() && (theEntity->pev->team == this->pev->team)) + { + AvHMessageID theTechnology = theEntity->GetTechnology(); + if(theTechnology == MESSAGE_NULL) + { + theHasFreeUpgradeCategory = true; + break; + } + else + { + // It's supported, so remove from unsupported list + theUnsupportedUpgrades.erase(std::find(theUnsupportedUpgrades.begin(), theUnsupportedUpgrades.end(), theTechnology)); + } + } + END_FOR_ALL_ENTITIES(kesTeamHive); + + // If we don't have a free upgrade category + if(!theHasFreeUpgradeCategory) + { + // Remove every unsupported structure + for(MessageIDListType::iterator theIter = theUnsupportedUpgrades.begin(); theIter != theUnsupportedUpgrades.end(); theIter++) + { + theTechNodes.SetIsResearchable(*theIter, false); + } + } + } + + // If there are no free upgrades available, disable upgrades + AvHTeam* theTeamPointer = this->GetTeamPointer(); + if(theTeamPointer) + { + AvHAlienUpgradeListType theUpgrades = theTeamPointer->GetAlienUpgrades(); + if(!AvHGetHasFreeUpgradeCategory(ALIEN_UPGRADE_CATEGORY_DEFENSE, theUpgrades, this->pev->iuser4)) + { + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_ONE, false); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TWO, false); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_THREE, false); + } + if(!AvHGetHasFreeUpgradeCategory(ALIEN_UPGRADE_CATEGORY_MOVEMENT, theUpgrades, this->pev->iuser4)) + { + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_SEVEN, false); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_EIGHT, false); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_NINE, false); + } + if(!AvHGetHasFreeUpgradeCategory(ALIEN_UPGRADE_CATEGORY_SENSORY, theUpgrades, this->pev->iuser4)) + { + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TEN, false); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_ELEVEN, false); + theTechNodes.SetIsResearchable(ALIEN_EVOLUTION_TWELVE, false); + } + } + } + + theTechNodes.GetDelta( this->mClientTechNodes,this->mClientTechDelta ); + if( !mClientTechDelta.empty() ) + { + const AvHTechNode* Node = NULL; + MessageIDListType::iterator current, end = mClientTechDelta.end(); + for( current = mClientTechDelta.begin(); current != end; ++current ) + { + Node = theTechNodes.GetNode(*current); + if( Node != NULL ) + { + NetMsg_SetTechNode( this->pev, Node ); + this->mClientTechNodes.InsertNode( Node ); + } + else + { + //TODO: send signal to remove the tech node from the client here... + this->mClientTechNodes.RemoveNode(*current); + } + } + mClientTechDelta.clear(); + mClientTechNodes = theTechNodes; + } + + // Propagate any tech slots that have changed + const AvHTechSlotListType& theTeamTechSlotList = theTeam->GetTechSlotManager().GetTechSlotList(); + if(this->mClientTechSlotList != theTeamTechSlotList) + { + // Send any nodes that have changed + int theCurrentSlot = 0; + for(AvHTechSlotListType::const_iterator theIter = theTeamTechSlotList.begin(); theIter != theTeamTechSlotList.end(); theIter++, theCurrentSlot++) + { + bool theHasClientTechSlot = false; + AvHTechSlots theClientTechSlot; + if((signed)this->mClientTechSlotList.size() > theCurrentSlot) + { + theClientTechSlot = this->mClientTechSlotList[theCurrentSlot]; + theHasClientTechSlot = true; + } + + AvHTechSlots theServerTechSlot = theTeamTechSlotList[theCurrentSlot]; + + if(!theHasClientTechSlot || (theClientTechSlot != theServerTechSlot)) + { + NetMsg_SetTechSlots( this->pev, theServerTechSlot ); + } + } + + this->mClientTechSlotList = theTeamTechSlotList; + } + } + } +} + +void AvHPlayer::UpdateTopDownMode() +{ + if((this->mClientInTopDownMode != this->mInTopDownMode) || (this->mSpecialPASOrigin != this->mClientSpecialPASOrigin)) + { + vec3_t& angles = this->mInTopDownMode ? this->mSpecialPASOrigin : this->mAnglesBeforeTopDown; + float position[3] = { angles.x, angles.y, angles.z }; + NetMsg_SetTopDown_Position( this->pev, this->mInTopDownMode, position ); + + this->mClientInTopDownMode = this->mInTopDownMode; + this->mClientSpecialPASOrigin = this->mSpecialPASOrigin; + } + + // Send menu tech slots to commander + AvHTeam* theTeam = this->GetTeamPointer(); + if(theTeam && this->mInTopDownMode) + { + int theMenuTechSlots = theTeam->GetMenuTechSlots(); + if(theMenuTechSlots != this->mClientMenuTechSlots) + { + NetMsg_SetTopDown_TechSlots( this->pev, theMenuTechSlots ); + this->mClientMenuTechSlots = theMenuTechSlots; + } + } +} + +void AvHPlayer::UpdateExperienceLevelsSpent() +{ + // If our spent level is different then our client's + if(this->mClientExperienceLevelsSpent != this->mExperienceLevelsSpent) + { + NetMsg_GameStatus_UnspentLevels( this->pev, kGameStatusUnspentLevels, GetGameRules()->GetMapMode(), this->mExperienceLevelsSpent ); + this->mClientExperienceLevelsSpent = this->mExperienceLevelsSpent; + } +} + +void AvHPlayer::UpdateEntityHierarchy() +{ + + AvHPlayer* player = this; + + // If we're spectating, then use the minimap data for the player we're spectating. + + AvHPlayer* spectatingPlayer = dynamic_cast(GetSpectatingEntity()); + + if (spectatingPlayer) + { + player = spectatingPlayer; + } + + AvHTeam* theTeam = player->GetTeamPointer(); + + // Removed the check for gamestart. This ensures that the entity-hierarchy is propagated before + // game start. + //if((theTeam) && GetGameRules()->GetGameStarted()) + if((theTeam)) // && GetGameRules()->GetGameStarted()) + { + if (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE || + theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + // Pass in previous version so it can optimize and only send diff + AvHEntityHierarchy& theEntityList = GetGameRules()->GetEntityHierarchy(player->GetTeam()); + + if(theEntityList.SendToNetworkStream(this->mClientEntityHierarchy, this->pev, false)) + { + this->mClientEntityHierarchy = theEntityList; + } + } + } + +} + +void AvHPlayer::UpdateSpawnScreenFade() +{ + if(this->mSendSpawnScreenFade) + { + Vector theFadeColor; + theFadeColor.x = 0; + theFadeColor.y = 0; + theFadeColor.z = 0; + UTIL_ScreenFade(this, theFadeColor, kSpawnInFadeTime, 0.0f, 255, FFADE_IN); + this->mSendSpawnScreenFade = false; + } +} + + +void AvHPlayer::UpdateDebugCSP() +{ + bool theCSPChanged = memcmp(&this->mClientDebugCSPInfo, &this->mDebugCSPInfo, sizeof(weapon_data_t)); + if(theCSPChanged || (this->mClientNextAttack != this->m_flNextAttack)) + { + NetMsg_DebugCSP( this->pev, this->mDebugCSPInfo, this->m_flNextAttack ); + memcpy(&this->mClientDebugCSPInfo, &this->mDebugCSPInfo, sizeof(weapon_data_t)); + this->mClientNextAttack = this->m_flNextAttack; + } +} + +void AvHPlayer::UpdateOverwatch() +{ + // Update overwatch indicator + if(this->mClientInOverwatch != this->mInOverwatch) + { + // We are entering overwatch + if(this->mInOverwatch) + { + PLAYBACK_EVENT_FULL(0, this->edict(), gStartOverwatchEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + } + // We are leaving overwatch + else + { + PLAYBACK_EVENT_FULL(0, this->edict(), gEndOverwatchEventID, 0, this->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + } + this->mClientInOverwatch = this->mInOverwatch; + } +} + +bool AvHPlayer::GetCanUseWeapon() const +{ + return GetIsAbleToAct() && pev->viewmodel; +} + +// : 0000953 +// allows a player to join team only once each inCoolDownTime seconds +bool AvHPlayer::JoinTeamCooledDown(float inCoolDownTime) { +// UTIL_ClientPrintAll(HUD_PRINTTALK, UTIL_VarArgs("Enter: JoinTeamCooledDown(%f), gpGlobals->time = %f, this->mTimeLastJoinTeam = %f", inCoolDownTime, gpGlobals->time, this->mTimeLastJoinTeam)); + if ((this->mTimeLastJoinTeam == -1) || (gpGlobals->time > this->mTimeLastJoinTeam + inCoolDownTime)) + { + this->mTimeLastJoinTeam = gpGlobals->time; + return true; + } + else + return false; +} +// : + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Nexus interface +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//TODO: flesh this out with admin privileges, etc. once the UPP authorization interface has been expanded +bool AvHPlayer::GetIsAuthorized(AvHAuthAction inAction, int inParameter) const +{ + switch( inAction ) + { + case AUTH_ACTION_JOIN_TEAM: + { + AvHTeamNumber theTeam = (AvHTeamNumber)inParameter; + switch( theTeam ) + { + case TEAM_IND: // ready room & spectator - game allows in all cases + case TEAM_SPECT: + return true; + default: + // check it's an active team + if( theTeam == GetGameRules()->GetTeamA()->GetTeamNumber() || theTeam == GetGameRules()->GetTeamB()->GetTeamNumber() ) + { + // : 0001042 -- allow switching of teams -- placeholder before Nexus + // if( GetGameRules()->GetCheatsEnabled() ) { return true; } // cheaters can switch + // if( !GetGameRules()->GetGameStarted() ) { return true; } // can switch teams before start + // if( this->GetHasBeenSpectator() ) { return false; } // spectators have seen everybody + // for(int counter = TEAM_ACTIVE_BEGIN; counter < TEAM_ACTIVE_END; counter++) + // { + // if( theTeam != counter && this->GetHasSeenTeam( (AvHTeamNumber)counter ) ) + // { return false; } // we've seen another active team + // } + return true; // haven't seen another team, authorized to join + } + return false; // unknown/inactive team - never grant an unknown permission! + } + } + case AUTH_ACTION_ADJUST_BALANCE: + { +#ifndef BALANCE_ENABLED + return false; +#else + return this->GetIsMember(PLAYERAUTH_DEVELOPER); +#endif + } + default: + return false; // never grant an unknown permission! + } +} + +#ifdef USE_OLDAUTH +bool AvHPlayer::GetIsMember(const AvHPlayerAuthentication inAuthGroup) +{ + return (this->GetAuthenticationMask() & inAuthGroup); +} +#else +bool AvHPlayer::GetIsMember(const string& inAuthGroup) const +{ + return false; +} +#endif + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// BalanceChangeListener implementation and balance network code +// +// Balance is checked for changes at a set rate determined by the +// BALANCE_UPDATE_MAX_FREQUENCY const below. This prevents the +// balance system logic from bogging down the server if there are 32 +// players and the entire system is reloaded. A maximum of one +// message will be sent with each check. Note that this system is +// much, much more efficient than the old check of the entire balance +// state every frame! +// +// Due to the setup of the balance system, the BalanceChanageListener +// functions will never be called for non-playtest compiles, so +// there is no need to gaurd with a playtest build #define. The +// call to UpdateBalanceVariables may benefit from an being #define'd +// out, but that function has very low overhead anyway. +// +// TODO: move this block (variables and logic) into a discrete class +// and associate that class with the player using an auto_ptr instead +// of embedding the information into the player class +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +const float BALANCE_UPDATE_MAX_FREQUENCY = 0.05; //maximum frequency at which checks occur + +bool AvHPlayer::shouldNotify(const string& name, const BalanceValueType type) const +{ + return true; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHPlayer::balanceCleared(void) const +{ + this->mBalanceRemovalList.clear(); + this->mBalanceMapInts.clear(); + this->mBalanceMapFloats.clear(); + this->mBalanceMapStrings.clear(); + NetMsg_BalanceVarClear( this->pev ); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// INTEGER +void AvHPlayer::balanceValueInserted(const string& name, const int value) const +{ + this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion + this->mBalanceMapInts.insert(BalanceIntCollection::value_type(name,value)); +} + +void AvHPlayer::balanceValueChanged(const string& name, const int old_value, const int new_value) const +{ + this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion + this->mBalanceMapInts.insert(BalanceIntCollection::value_type(name,new_value)); +} + +void AvHPlayer::balanceValueRemoved(const string& name, const int old_value) const +{ + this->mBalanceMapInts.erase(name); //in case we didn't send it yet + this->mBalanceRemovalList.insert(name); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// FLOAT +void AvHPlayer::balanceValueInserted(const string& name, const float value) const +{ + this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion + this->mBalanceMapFloats.insert(BalanceFloatCollection::value_type(name,value)); +} + +void AvHPlayer::balanceValueChanged(const string& name, const float old_value, const float new_value) const +{ + this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion + this->mBalanceMapFloats.insert(BalanceFloatCollection::value_type(name,new_value)); +} + +void AvHPlayer::balanceValueRemoved(const string& name, const float old_value) const +{ + this->mBalanceMapFloats.erase(name); //in case we didn't send it yet + this->mBalanceRemovalList.insert(name); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// STRING +void AvHPlayer::balanceValueInserted(const string& name, const string& value) const +{ + this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion + this->mBalanceMapStrings.insert(BalanceStringCollection::value_type(name,value)); +} + +void AvHPlayer::balanceValueChanged(const string& name, const string& old_value, const string& new_value) const +{ + this->mBalanceRemovalList.erase(name); //in case we had previous signal for deletion + this->mBalanceMapStrings.insert(BalanceStringCollection::value_type(name,new_value)); +} + +void AvHPlayer::balanceValueRemoved(const string& name, const string& old_value) const +{ + this->mBalanceMapStrings.erase(name); //in case we didn't send it yet + this->mBalanceRemovalList.insert(name); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHPlayer::InitBalanceVariables(void) +{ + //grab the entire current balance + BalanceValueContainer* container = BalanceValueContainerFactory::get(BalanceValueContainerFactory::getDefaultFilename()); + this->mBalanceMapStrings = *container->getStringMap(); + this->mBalanceMapInts = *container->getIntMap(); + this->mBalanceMapFloats = *container->getFloatMap(); + + //clear the client in preparation to send everything again + //pev will be null if this is called during construction + if( this->pev ) { NetMsg_BalanceVarClear( this->pev ); } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHPlayer::UpdateBalanceVariables(void) +{ + if(mNextBalanceVarUpdate < gpGlobals->time) + { + //only send it if it can be used... + //CONSIDER: second security setting for read-only transfer + if(this->GetIsAuthorized(AUTH_ACTION_ADJUST_BALANCE,0)) + { + //check number of changes we have to send + int total_changes = this->mBalanceRemovalList.size(); + total_changes += this->mBalanceMapInts.size(); + total_changes += this->mBalanceMapFloats.size(); + total_changes += this->mBalanceMapStrings.size(); + + //if we have multiple changes and need to tell client they are a set + if( total_changes > 1 && !this->mBalanceMessagePending ) //flag multiple changes incoming + { + NetMsg_BalanceVarChangesPending( this->pev, true ); + this->mBalanceMessagePending = true; + } + //else if we have no more changes -- check to see if we need to send end set signal + else if( total_changes == 0 ) + { + if( this->mBalanceMessagePending ) + { + NetMsg_BalanceVarChangesPending( this->pev, false ); + this->mBalanceMessagePending = false; + } + } + // we have at least one change to make, possibly in a set + else if(!this->mBalanceRemovalList.empty()) + { + set::iterator item = this->mBalanceRemovalList.begin(); + NetMsg_BalanceVarRemove( this->pev, *item ); + this->mBalanceRemovalList.erase(item); + } + else if(!this->mBalanceMapInts.empty()) + { + BalanceIntCollection::iterator item = this->mBalanceMapInts.begin(); + NetMsg_BalanceVarInsertInt( this->pev, item->first, item->second ); + this->mBalanceMapInts.erase(item); + } + else if(!this->mBalanceMapFloats.empty()) + { + BalanceFloatCollection::iterator item = this->mBalanceMapFloats.begin(); + NetMsg_BalanceVarInsertFloat( this->pev, item->first, item->second ); + this->mBalanceMapFloats.erase(item); + } + else if(!this->mBalanceMapStrings.empty()) + { + BalanceStringCollection::iterator item = this->mBalanceMapStrings.begin(); + NetMsg_BalanceVarInsertString( this->pev, item->first, item->second ); + this->mBalanceMapStrings.erase(item); + } + } + //update next check of balance message queue + mNextBalanceVarUpdate = gpGlobals->time + BALANCE_UPDATE_MAX_FREQUENCY; + } +} diff --git a/main/source/mod/AvHPlayer.h b/main/source/mod/AvHPlayer.h index 949ca91..b1b381e 100644 --- a/main/source/mod/AvHPlayer.h +++ b/main/source/mod/AvHPlayer.h @@ -1,869 +1,885 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHPlayer.h $ -// $Date: 2002/11/22 21:18:33 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHPlayer.h,v $ -// Revision 1.65 2002/11/22 21:18:33 Flayra -// - Potentially fixed strange Onos collision crash -// - Don't allow player to join team after he's seen another team -// - "lastinv" support -// - Fixed perma-cloak bug after losing last sensory chamber whilst cloaked -// - Started fixing commander PAS problem -// - Fixed readyroom "ghost player" exploit when F4 during REIN -// - Draw damage in debug, never otherwise -// -// Revision 1.64 2002/11/15 04:42:50 Flayra -// - Regenerate now returns true if healing was successful -// - Logging changes and fixes -// -// Revision 1.63 2002/11/13 01:49:08 Flayra -// - Proper death message logging for Psychostats -// -// Revision 1.62 2002/11/12 18:44:54 Flayra -// - Added mp_logdetail support for damage messages -// - Changed the alien ability anti-exploit code to try to co-exist with scripters -// -// Revision 1.61 2002/11/12 02:28:39 Flayra -// - Fixed problems with armor not being updated when armor upgrades completed -// - Aliens now keep same percentage of health and armor when morphing -// - Much better logging, up to standard -// - Don't add enemy buildings to hive sight unless parasited (solves blip spam) -// - Removed draw damage from public build -// - Changes to minimap to less overflows at end of big games -// -// Revision 1.60 2002/10/25 21:48:01 Flayra -// - Added more auth masks -// - Moved mSkin to pev->skin so playerclass could hold what used to be mPlayMode could be used to quickly perform culling, to try to prevent overflowage at the end of large games -// -// Revision 1.59 2002/10/24 21:40:02 Flayra -// - Reworked jetpack effect -// - Authicons update -// - Set builder for alien buildings, so turret kills can credit builder -// - Alien easter eggs -// - Network optimizations after game reset on huge (20-32) player games -// - Allow server ops to disable auth icons -// - Alien energy tweaks -// - Show unbuilt hives in hive sight -// - Move alien energy updating fully into shared code -// - Tried to fix full health ring showing for dead selected players -// - Moved help text client-side -// - Cache info_locations and gamma until map change -// - Skin fixes -// -// Revision 1.58 2002/10/20 21:10:57 Flayra -// - Optimizations -// -// Revision 1.57 2002/10/16 01:05:38 Flayra -// - Sent health as short for big aliens -// - Fixed sayings not triggering commander alerts -// - Now name changes are queued until the next match -// - Added authmask support -// - Egg idle sounds play more frequently, refactored too -// - Fixed preserving model in ready room after game end -// - Profiling of AddToFullPack -// - Fix for falling through lifts when morphing on them (untested) -// -// Revision 1.56 2002/10/03 19:32:06 Flayra -// - Reworked orders completely -// - Send max resources to players -// - Heavy armor sped up slightly -// - Kill players who illegally try to use alien abilities -// - Moved energy to a new variable, send health for aliens -// - Only freeze players during countdown -// - Removed slowdown when taking damage -// - Send blips in two messages, one friendly and one enemy (old bad hack) -// -// Revision 1.55 2002/09/25 20:50:06 Flayra -// - Added 3 new sayings -// - Frame-rate independent updating -// - Don't allow player to kill self while commanding -// -// Revision 1.54 2002/09/23 22:27:52 Flayra -// - Added skin support -// - Added client connected/disconnected hooks for particle system propagation optimizations -// - Removed power armor, added heavy armor -// - Fixed death animations -// - Added hook to see if commander has given an order and to see if he's idle -// - Bound resources for aliens -// - Soldiers asking for ammo and health trigger commander alert -// - Added gestation anims -// - Slowed down Onos movement -// - When cheats are enabled, purchases are free -// -// Revision 1.53 2002/09/09 20:04:53 Flayra -// - Added commander voting -// - Commander score is now the average of the rest of his players (reverted back when he leaves CC) -// - Fixed bug where upgrades were getting removed and then add repeatedly -// - Added multiple skins for marines -// - Play sound when aliens lose an upgrade -// - Changed fov to 90 for all aliens for software compatibility -// - Added hiveinfo drawing -// - Tweaked marine and alien speeds (to compensate for loss of drastic alien fov) -// -// Revision 1.52 2002/08/31 18:01:02 Flayra -// - Work at VALVe -// -// Revision 1.51 2002/08/16 02:42:57 Flayra -// - New damage types -// - Much more efficient blip calculation (at least it should be, I haven't measured it so who really knows) -// - New way of representing ensnare state for shared code (disabling jumping, jetpacking) -// - Removed old overwatch code -// - Store health in fuser2 for drawing health for commander -// - Swap bile bomb and umbra -// -// Revision 1.50 2002/08/09 01:10:30 Flayra -// - Keep previous model when a game is over and going back to ready room -// - Refactoring for scoreboard -// - Support for "jump" animation -// - Freeze player before game starts -// - Reset score when leaving a team -// -// Revision 1.49 2002/08/02 21:52:17 Flayra -// - Cleaned up jetpacks, made webbing useful again, help messages, added "orderself" cheat, changed gestation messages for new tooltip system, added GetHasAvailableUpgrades() for help system, removed particle template messages, slowed down particle template update rate to reduce overflows (woo!) -// -// Revision 1.48 2002/07/26 23:07:54 Flayra -// - Numerical feedback -// - New artwork for marine, with jetpack as body group (this code doesn't work) -// - Misc. fixes (alien vision mode not being preserved when it should, and staying when it shouldn't) -// -// Revision 1.47 2002/07/23 17:17:57 Flayra -// - Added power armor, added "talking" state for hive sight, changes for new resource model, removed max buildings, create buildings with random orientation, added stimpack, tweaked redemption, added new hive sight info -// -// Revision 1.46 2002/07/08 17:15:39 Flayra -// - Added validation of all impulses (assumes invalid by default, instead of valid), reset players like all other entities, ensnaring fixes, players are temporarily invulnerable when they spawn, preserve health and armor when using command station, fixed up redemption, add hooks for commander voting and going back to the ready room, models can't be changed via the console anymore -// -// Revision 1.45 2002/07/01 21:43:16 Flayra -// - Removed outdated adrenaline concept, added new alien sight mode, primal scream, removed flashlight battery message, fixed morphing problems for level 5 (and others), tweaked marine and alien speeds, fixed triple speed upgrade problem, disabled overwatch, moved player assets out of precache() (wasn't being called reliably), get full energy after a lifeform change, hive sight only draws out of sight now, added scent of fear -// -// Revision 1.44 2002/06/25 18:13:57 Flayra -// - Added death animations, added more general animation support, leaps do damage, general construct effects, new alien inventory system, added charging, info_locations, galloping, new alien building code, better morphing system (prevents getting stuck), more builder error messages, clear deaths on game end, hide health while gestating -// -// Revision 1.43 2002/06/10 20:03:13 Flayra -// - Updated for new minimap (remember killed position). Updated blood so marines aren't bloody, and aliens emit yellow blood. Removed slowing when hit (now players fly back when hit though), UpdateBlips() hack (fix when time) -// -// Revision 1.42 2002/05/28 18:03:41 Flayra -// - Refactored size for role code for movement chambers, deal with inventory properly (entity leak), increased web ensnaring effects (put weapon away!), reinforcement refactoring, tweaked speeds so marines are a little slower, and speed upgrade works properly again (now level 1 can generally outrun marines), added recycling support, play destroy egg sound when killed when morphing, new hive sight, EffectivePlayerClassChanged() refactoring -// -// Revision 1.41 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifndef AVHPLAYER_H -#define AVHPLAYER_H - -#include "dlls/extdll.h" -#include "dlls/util.h" - -#include "dlls/cbase.h" -#include "dlls/player.h" -#include "dlls/trains.h" -#include "dlls/nodes.h" -#include "dlls/weapons.h" -#include "dlls/soundent.h" -#include "dlls/monsters.h" -#include "engine/shake.h" -#include "dlls/decals.h" -#include "dlls/gamerules.h" -#include "mod/AvHConstants.h" -#include "mod/AvHTeam.h" -#include "mod/AvHMessage.h" -#include "mod/AvHSharedTypes.h" -#include "mod/AvHBasePlayerWeapon.h" -#include "mod/AvHEntityHierarchy.h" -#include "mod/AvHOrder.h" -#include "mod/AvHSpecials.h" -#include "common/usercmd.h" -#include "types.h" -#include "mod/AvHTechTree.h" -#include "common/weaponinfo.h" -#include "mod/AvHVisibleBlipList.h" -#include "mod/AvHBaseInfoLocation.h" -#include "mod/AvHCloakable.h" -#include "mod/AvHMessageList.h" -#include "util/Balance.h" - -#include //for balance information below... - -class AvHPlayer : public CBasePlayer, public AvHCloakable, public BalanceChangeListener -{ -public: - // AvHPlayer stuff - AvHPlayer(); - - void AddDebugEnemyBlip(float inX, float inY, float inZ); - void PrintWeaponListToClient(CBaseEntity *theAvHPlayer); - virtual void AddPoints( int score, BOOL bAllowNegativeScore ); - - virtual void AwardKill( entvars_t* pevTarget); - virtual int BloodColor(void); - bool BuildTech(AvHMessageID inBuildID, const Vector& inWorldPos); - void ClearBlips(); - void ClientDisconnected(); - bool DropItem(const char* inItemName = NULL); - bool GroupMessage(AvHMessageID inGroupMessage); - bool GetCenterPositionForGroup(int inGroupNumber, float& outX, float& outY); - - virtual void GetAnimationForActivity(int inActivity, char outAnimation[64], bool inGaitSequence = false); - - bool GetInReadyRoom(void) const; - AvHPlayer* GetCommander(void); - AvHPlayMode GetPlayMode(bool inIncludeSpectating = false) const; - bool GetHasLeftReadyRoom() const; - bool GetHasJetpack() const; - bool GetHasHeavyArmor() const; - bool GetHasAvailableUpgrades() const; - bool GetHasPowerArmor() const; - int GetHull() const; - virtual int GiveAmmo( int iAmount, char *szName, int iMax ); - bool GetShouldResupplyAmmo(); - float GetCurrentWeaponAmmoPercentage(); - virtual int GetMaxWalkSpeed() const; - int GetScore() const; - void SetScore(int inScore); - void PickSkin(); - void SetSkin(int inSkin); - bool GetCanCommand(string& outErrorMessage); - bool GetCanReceiveResources() const; - - void SetPlayMode(AvHPlayMode inPlayMode, bool inForceSpawn = false); - bool GetHasBeenSpectator(void) const; - void InitializeFromTeam(float inHealthPercentage = 1.0f, float inArmorPercentage = 1.0f); - bool GetIsAlien(bool inIncludeSpectating = false) const; - bool GetIsMarine(bool inIncludeSpectating = false) const; - bool GetIsInTopDownMode(bool inIncludeSpectating = false) const; - bool GetIsBeingDigested() const; - bool GetIsDigesting() const; - bool GetIsEntityInSight(CBaseEntity* inEntity); - bool GetIsValidReinforcementFor(AvHTeamNumber inTeam) const; - AvHTeamNumber GetTeam(bool inIncludeSpectating = false) const; - float GetKilledX() const; - float GetKilledY() const; - AvHTeam* GetTeamPointer(bool inIncludeSpectating = false) const; - bool GetIsAlienSightActive() const; - - AvHClassType GetClassType(void) const; - - bool GetCurrentWeaponCannotHolster() const; - //Activity GetDeathActivity(void); - bool GetEnemySighted(void) const; - bool GetIsFiring(void) const; - bool GetIsInOverwatch(void) const; - bool GetIsSpectatingTeam(AvHTeamNumber inTeamNumber) const; - bool GetIsSpectatingPlayer(int inPlayerNumber) const; - bool GiveCombatModeUpgrade(AvHMessageID inMessageID, bool inInstantaneous = false); - bool GetHasCombatModeUpgrade(AvHMessageID inMessageID) const; - - bool GetIsRelevant(bool inIncludeSpectating = false) const; - bool GetCanBeAffectedByEnemies() const; - bool GetIsSelected(int inEntityIndex) const; - bool RemoveSelection(int inEntityIndex); - void SetSelection(int inEntityIndex, bool inClearPreviousSelection = true); - bool GetIsMetabolizing() const; - void SetTimeOfMetabolizeEnd(float inTime); - virtual float GetOpacity() const; - bool GetIsSpectator() const; - void SetIsSpectator(); - float GetLastTimeInCommandStation() const; - - bool GetIsSpeaking(void) const; - AvHMessageID GetLastSaying() const; - bool GetOrdersRequested(void) const; - bool GetOrderAcknowledged(void) const; - string GetPlayerName() const; - - bool GetHasGivenOrder() const; - void SetHasGivenOrder(bool inState); - - int GetPointValue(void) const; - vec3_t GetVisualOrigin() const; - int GetRespawnCost() const; - - void AwardExperienceForObjective(float inHealthChange, AvHMessageID inMessageID); - float GetExperience() const; - void SetExperience(float inExperience); - int GetExperienceLevel() const; - - AvHServerPlayerData* GetServerPlayerData(); - - virtual bool GetHasItem(const char *szName); - virtual void GiveNamedItem(const char *szName, bool inSendMessage = false); - int GetNumberOfItems(); - void GiveResources(float inResources); - float GetTimeLastF4() const; - void SetTimeLastF4(float inTime); - float GetTimeStartedTopDown() const; - float GetTimeOfLastConstructUse() const; - void SetTimeOfLastConstructUse(float inTime); - float GetTimeOfLastSignificantCommanderAction() const; - void LogEmitRoleChange(); - void LogPlayerAction(const char* inActionDescription, const char* inActionData); - void LogPlayerActionPlayer(CBasePlayer* inAttackingPlayer, const char* inAction); - void LogPlayerAttackedPlayer(CBasePlayer* inAttackingPlayer, const char* inWeaponName, float inDamage); - void LogPlayerKilledPlayer(CBasePlayer* inAttackingPlayer, const char* inWeaponName); - - void StartLeap(); - - AvHUser3 GetPreviousUser3(bool inIncludeSpectating = false) const; - AvHUser3 GetUser3(bool inIncludeSpectating = false) const; - AvHMessageID GetEvolution(bool inIncludeSpectating = false) const; - bool GetSpecialPASOrigin(Vector& outOrigin); - void GiveTeamUpgrade(AvHMessageID inUpgrade, bool inGive = true); - bool HolsterWeaponToUse(); - void Kick(); - virtual void Killed( entvars_t *pevAttacker, int iGib ); - void NextWeapon(); - void ResetEntity(void); - bool PayPurchaseCost(int inCost); - void PlaybackNumericalEvent(int inEventID, int inNumber); - bool PlayHUDSound(AvHHUDSound inSound) const; - bool PlayHUDSound(AvHHUDSound inSound, float x, float y) const; - void PlayHUDStructureNotification(AvHMessageID inMessageID, const Vector& inLocation); - void PlayRandomRoleSound(string inSoundListName, int inChannel = CHAN_AUTO, float inVolume = 1.0f); - void PlayerConstructUse(); - void SetCurrentCommand(const struct usercmd_s* inCommand); - void SetDebugCSP(weapon_data_t* inWeaponData); - void SetPendingCommand(char* inCommand); - void TriggerProgressBar(int inEntityID, int inParam); - float GetTimeOfLastTeleport() const; - void SetTimeOfLastTeleport(float inTime); - bool SwitchWeapon(const char* inString); - virtual void StartObserver( Vector vecPosition, Vector vecViewAngle ); - virtual void StartObservingIfNotAlready(void); - void StartTopDownMode(void); - bool StopTopDownMode(void); - bool SetBeingDigestedMode(bool inBeingDigested); - - bool GetIsCloaked() const; - bool GetIsPartiallyCloaked() const; - void TriggerUncloak(); - - //Nexus interface - replaces all old auth information - bool GetIsAuthorized(AvHAuthAction inAction, int inParameter) const; -#ifdef USE_OLDAUTH - int GetAuthenticationMask(); - bool GetIsMember(const AvHPlayerAuthentication inAuthGroup); - bool GetAllowAuth() const; - void SetAllowAuth(bool inAllowAuth); - void SetAuthCheatMask(int inAuthCheatMask); -#else - bool GetIsMember(const string& inAuthGroup) const; -#endif - //END Nexus interface - - float GetTimeLastPlaying() const; - - bool GetHasSeenTeam(AvHTeamNumber inNumber) const; - void SetHasSeenTeam(AvHTeamNumber inNumber); - - float GetTimeOfLastSporeDamage() const; - void SetTimeOfLastSporeDamage(float inTime); - - // Quick ensnare system...add something better? - bool GetIsEnsnared() const; - bool GetIsAbleToAct() const; - - void DropAmmo(char *pszAmmoType, int iAmmoAmt, int iMax, int iWeaponID, Vector vecAngles); - void EffectivePlayerClassChanged(); - void NeedsTeamUpdate(); - void SendTeamUpdate(); - - // Returns true if successful. Fails if the player is too ensnared already - bool SetEnsnareState(bool inState); - - bool GetIsStunned() const; - bool SetIsStunned(bool inState, float inTime = 0.0f); - - bool GetIsCatalysted() const; - void SetIsCatalysted(bool inState, float inTime = 0.0f); - - bool Energize(float inEnergyAmount); - bool Heal(float inAmount, bool inPlaySound = true); - bool Regenerate(float inRegenerationAmount, bool inPlaySound = true); - bool GetIsScreaming(); - void StartScreaming(); - - //void UpgradeArmorLevel(void); - - // CBasePlayer stuff - virtual void ImpulseCommands( void ); - virtual void ItemPostFrame(void); - virtual void Jump( void ); - virtual void ObserverModeIllegal(); - virtual void PackDeadPlayerItems(void); - virtual void PreThink( void ); - virtual void Spawn( void ); - //virtual BOOL SwitchWeapon( CBasePlayerItem* pWeapon ); - virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); - //virtual const char* TeamID( void ); - virtual void UpdateClientData( void ); - - virtual float GetCloakTime() const; - virtual int GetExperienceLevelsSpent() const; - virtual void SetExperienceLevelsSpent(int inSpentLevels); - - virtual string GetNetworkID() const; - virtual void SetNetworkID(string& inNetworkID); - - virtual string GetNetworkAddress() const; - virtual void SetNetworkAddress(string& inNetworkAddress); - - virtual bool GetIsTemporarilyInvulnerable() const; - AvHTechTree& GetCombatNodes(); - MessageIDListType& GetPurchasedCombatUpgrades(); - bool Redeem(); - - bool GetCanBeResupplied() const; - bool Resupply(bool inGiveHealth = false); - - void SetDesiredNetName(string inDesiredNetName); - void SetDesiredRoomType(int inRoomType, bool inForceUpdate = false); - - // Gets origin of the player after changing the iuser3. - void GetNewOrigin(AvHUser3 inNewUser3, bool inDucking, vec3_t& outOrigin) const; - - float GetResources(bool inIncludeSpectating = false) const; - void SetUser3(AvHUser3 inUser3, bool inForceChange = false, bool inGiveWeapons = true); - void SetResources(float inResources, bool inPlaySound = false); - - // Send messages to player's screen - bool SendMessage(const char* pMessage, SHOWMESSAGE_TYPE type = NORMAL); - bool SendMessageOnce(const char* pMessage, SHOWMESSAGE_TYPE type = NORMAL); - bool SendMessageNextThink(const char* pMessage); - - virtual int GetEffectivePlayerClass(); - - void BecomePod(); - void SetModelFromState(); - - int GetDigestee() const; - void SetDigestee(int inPlayerID); - void StartDigestion(int inDigestee); - void StopDigestion(bool inDigested); - - void ProcessEntityBlip(CBaseEntity* inEntity); - - void SetPosition(const Vector& inNewPosition); - virtual char* GetPlayerModelKeyName(); - void GetSize(Vector& outMinSize, Vector& outMaxSize) const; - void SetSizeForUser3(); - - void GetViewForUser3(AvHUser3 inUser3, bool inIsDucking, float& outFOV, float& outOffset) const; - void SetViewForUser3(); - void SetWeaponsForUser3(); - - bool RunClientScript(const string& inScriptName); - - void TriggerFog(int inFogEntity, float inFogExpireTime); - - void UpdateInventoryEnabledState(int inNumActiveHives, bool inForceUpdate = false); - - virtual bool GetCanUseWeapon() const; - - void PropagateServerVariables(); - - bool GetUsedKilled() { return mUsedKilled; } - void SetUsedKilled(bool bKilled ) { mUsedKilled = bKilled; } - void ClearOrders() { mClientOrders.clear(); } - - // tankefugl: 0000953 - bool JoinTeamCooledDown(float inCoolDownTime); - // tankefugl -private: - void AcquireOverwatchTarget(); - bool AttemptToBuildAlienStructure(AvHMessageID inMessageID); - void ClearRoleAbilities(); - void ClearUserVariables(); - CBaseEntity* CreateAndDrop(const char* inItemName); - void DeployCurrent(); - bool ExecuteAlienMorphMessage(AvHMessageID inMessageID, bool inInstantaneous); - bool ExecuteCombatMessage(AvHMessageID inMessageID, bool& outIsAvailable, bool inForce = false); - bool ExecuteMessage(AvHMessageID inMessageID, bool inInstantaneous = false, bool inForce = false); - float GetAlienAdjustedEventVolume() const; - bool GetCanGestate(AvHMessageID inMessageID, string& outErrorMessage); - bool GetDoesCurrentStateStopOverwatch() const; - bool QueryEnemySighted(CBaseEntity* inEntity); - bool GetHasActiveAlienWeaponWithImpulse(AvHMessageID inMessageID) const; - bool GetRandomGameStartedTick(float inApproximateFrameRate); - bool GetPurchaseAllowed(AvHMessageID inUpgrade, int& outCost, string* outErrorMessage = NULL) const; - int GetRelevantWeight(void) const; - int GetRelevantWeightForWeapon(AvHBasePlayerWeapon* inWeapon) const; - void GetSpeeds(int& outBaseSpeed, int& outUnemcumberedSpeed) const; - void GiveCombatUpgradesOnSpawn(); - bool GiveOrderToSelection(AvHOrderType inOrder, Vector inNormRay); - void GiveOrderToSelection(AvHOrder& inOrder); - void GiveUpgrade(AvHMessageID inUpgrade); - void HandleOverwatch(void); - void HandleResearch(); - void HolsterCurrent(); - void InitializeTechNodes(); - void HandleTopDownInput(); - void ProcessEvolution(); - void ProcessCombatDeath(); - void ProcessResourceAdjustment(AvHMessageID inMessageID); - void ResetBehavior(bool inRemoveFromTeam); - void ResetGameNewMap(); - void ResetPlayerPVS(); - void SetCombatNodes(const AvHTechTree& inTechNodes); - void SetLifeformCombatNodesAvailable(bool inAvailable); - void ValidateClientMoveEvents(); - - void InternalAlienThink(); - void InternalAlienUpgradesThink(); - void InternalAlienUpgradesCloakingThink(); - void InternalAlienUpgradesPheromonesThink(); - void InternalAlienUpgradesRegenerationThink(); - void InternalCommanderThink(); - void InternalBoundResources(); - void InternalCommonThink(); - void InternalCombatThink(); - void InternalDigestionThink(); - void InternalEnemySightedPreThink(); - void InternalFogThink(); - void InternalHUDThink(); - void InternalMarineThink(); - void InternalPreThink(); - void InternalProgressBarThink(); - void InternalSpeakingThink(); - - void EXPORT PlayerTouch(CBaseEntity* inOther); - - bool PlaySaying(AvHMessageID inMessageID); - bool ProcessClickEvent(bool inLMB, float inWorldX, float inWorldY); - void ProcessTechUpgrade(AvHMessageID inMessageID); - void Research(AvHMessageID inMessageID, int inEntityIndex); - void Evolve(AvHMessageID inMessageID, bool inInstantaneous = false); - void PurchaseCombatUpgrade(AvHMessageID inMessageID); - void RecalculateSpeed(void); - void ReloadWeapon(void); - void RemoveCombatUpgrade(AvHMessageID inMessageID); - void RemoveCombatUpgradesPremptedBy(AvHMessageID inMessageID); - void ResetOverwatch(); - void RevertHealthArmorPercentages(); - void SaveHealthArmorPercentages(); - void SetMoveTypeForUser3(); - //bool SpawnReinforcements(void); - void TurnOverwatchTowardsTarget(CBaseEntity* theTarget); - void TurnOffOverwatch(); - void TurnOnOverwatch(); - void UpdateAmbientSounds(); - - //void UpdateArmor(); - void UpdateAlienUI(); - void UpdateBlips(); - void UpdateDebugCSP(); - void UpdateEffectiveClassAndTeam(); - void UpdateEntityHierarchy(); - void UpdateExperienceLevelsSpent(); - void UpdateFirst(); - void UpdateFog(); - void UpdateGamma(); - void UpdateInfoLocations(); - void UpdateParticleTemplates(); - void UpdateOrders(); - void UpdateOverwatch(); - void UpdatePendingClientScripts(); - void UpdateProgressBar(); - void UpdateVUser4(); - void UpdateSetSelect(); - void UpdateSpawnScreenFade(); - void UpdateSoundNames(); - void UpdateTechNodes(); - void UpdateTopDownMode(); - - void Init(); - - //BalanceChangeListener implementation - void InitBalanceVariables(void); - void UpdateBalanceVariables(void); - bool shouldNotify(const string& name, const BalanceValueType type) const; - void balanceCleared(void) const; - void balanceValueInserted(const string& name, const float value) const; - void balanceValueInserted(const string& name, const int value) const; - void balanceValueInserted(const string& name, const string& value) const; - void balanceValueChanged(const string& name, const float old_value, const float new_value) const; - void balanceValueChanged(const string& name, const int old_value, const int new_value) const; - void balanceValueChanged(const string& name, const string& old_value, const string& default_value) const; - void balanceValueRemoved(const string& name, const float old_value) const; - void balanceValueRemoved(const string& name, const int old_value) const; - void balanceValueRemoved(const string& name, const string& old_value) const; - - float mResources; - - bool mFirstUpdate; - bool mNewMap; - - bool mHasSeenTeamA; - bool mHasSeenTeamB; - - string mQueuedThinkMessage; - - bool mClientInOverwatch; - bool mInOverwatch; - bool mOverwatchEnabled; - int mOverwatchTarget; - float mTimeOfLastOverwatchPreventingAction; - float mTimeLastSeenOverwatchTarget; - Vector mOverwatchFacing; - bool mOverwatchFiredThisThink; - - // tankefugl: 0000953 - float mTimeLastJoinTeam; - // tankefugl - - // alien upgrades - float mTimeOfLastRegeneration; - float mTimeOfLastPheromone; - - float mTimeOfLastUse; - - float mTimeLeapEnd; - - Vector mPositionBeforeTopDown; - Vector mAnglesBeforeTopDown; - Vector mViewAnglesBeforeTopDown; - Vector mViewOfsBeforeTopDown; - string mAnimExtensionBeforeTopDown; - bool mClientInTopDownMode; - bool mInTopDownMode; - int mTimeStartedTopDown; - - float mTimeOfLastF4; - float mTimeOfLastSaying; - AvHMessageID mLastSaying; - bool mIsSpeaking; - bool mOrdersRequested; - bool mOrderAcknowledged; - float mTimeOfLastEnemySighting; - bool mEnemySighted; - - bool mTriggerUncloak; - - bool mHasLeftReadyRoom; - bool mHasBeenSpectator; - char* mPendingCommand; - - struct usercmd_s mCurrentCommand; - bool mAttackOneDown; - bool mAttackTwoDown; - Vector mAttackOnePressedWorldPos; - Vector mAttackTwoPressedWorldPos; - Vector mMouseWorldPos; - bool mPlacingBuilding; - - EntityListType mSelected; - EntityListType mClientSelected; - - EntityInfo mTrackingEntity; - EntityInfo mClientTrackingEntity; - - EntityListType mClientGroups[kNumHotkeyGroups]; - AvHAlertType mClientGroupAlerts[kNumHotkeyGroups]; - int mClientRequests[kNumRequestTypes]; - EntityListType mClientSelectAllGroup; - - OrderListType mClientOrders; - - AvHEntityHierarchy mClientEntityHierarchy; - - // Research nodes for commander - AvHTechTree mClientTechNodes; - MessageIDListType mClientTechDelta; - AvHTechSlotListType mClientTechSlotList; - - AvHMessageID mClientResearchingTech; - - float mClientGamma; - - StringList mSentMessageList; - - AvHVisibleBlipList mFriendlyBlips; - AvHVisibleBlipList mEnemyBlips; - - AvHVisibleBlipList mClientFriendlyBlips; - AvHVisibleBlipList mClientEnemyBlips; - - int mClientCommander; - - Vector mSpecialPASOrigin; - Vector mClientSpecialPASOrigin; - float mTimeOfLastPASUpdate; - - - bool mClientIsAlien; - - bool mAlienSightActive; - - float mTimeOfLastTeleport; - float mTimeOfLastRedeem; - - float mJetpackEnergy; - bool mJetpacking; - - weapon_data_t mClientDebugCSPInfo; - weapon_data_t mDebugCSPInfo; - float mClientNextAttack; - - float mTimeToBeUnensnared; - float mLastTimeEnsnared; - int mMaxWalkSpeed; - int mMaxGallopSpeed; - vec3_t mLastGallopViewDirection; - - float mTimeToBeFreeToMove; - float mTimeToEndCatalyst; - - float mLastTimeInCommandStation; - float mLastTimeCheckedRedemption; - float mLastTimeRedemptionTriggered; - - float mLastTimeStartedPlaying; - - float mTimeOfLastHelpText; - bool mLastHelpWasGeneral; - - float mLastPowerArmorThink; - float mLastInventoryThink; - - int mNumHives; - - float mTimeLastPlaying; - - float mTimeGestationStarted; - - AvHUser3 mPreviousUser3; - float mSavedJetpackEnergy; - - AvHMessageID mEvolution; - float mHealthPercentBefore; - float mArmorPercentBefore; - - StringList mClientSoundNames; - - bool mIsScreaming; - float mTimeStartedScream; - - float mKilledX, mKilledY; - - int mNumParticleTemplatesSent; - float mTimeOfLastParticleTemplateSending; - - int mClientProgressBarEntityIndex; - int mProgressBarEntityIndex; - int mProgressBarParam; - float mTimeProgressBarTriggered; - - float mTimeOfLastFogTrigger; - float mFogExpireTime; - int mCurrentFogEntity; - int mClientCurrentFogEntity; - - AvHBaseInfoLocationListType mClientInfoLocations; - - HiveInfoListType mClientHiveInfo; - - AvHAlienUpgradeListType mClientUpgrades; - - StringList mPendingClientScripts; - - bool mHasGivenOrder; - float mTimeOfLastSignificantCommanderAction; - - int mPreThinkTicks; - float mPreThinkFrameRate; - - string mDesiredNetName; - - int mClientMenuTechSlots; - - float mTimeOfLastClassAndTeamUpdate; - bool mEffectivePlayerClassChanged; - bool mNeedsTeamUpdate; - bool mSendTeamUpdate; - bool mSendSpawnScreenFade; - - int mDigestee; - float mTimeOfLastDigestDamage; - float mTimeOfLastCombatThink; - - int mDesiredRoomType; - int mClientDesiredRoomType; - - float mTimeOfLastConstructUseAnimation; - float mTimeOfLastConstructUse; - - float mTimeOfLastResupply; - - float mTimeOfMetabolizeEnd; - - AvHMessageID mLastSelectEvent; - Vector mPositionBeforeLastGotoGroup; - - float mTimeOfLastSporeDamage; - float mTimeOfLastTouchDamage; - - string mLastMessageSent; - - float mExperience; - float mClientPercentToNextLevel; - int mExperienceLevelsSpent; - int mClientExperienceLevelsSpent; - - MessageIDListType mPurchasedCombatUpgrades; - MessageIDListType mGiveCombatUpgrades; - - AvHTechTree mCombatNodes; - - int mScore; - int mSavedCombatFrags; - float mLastUpdateTime; - - string mNetworkAddress; - int mLastModelIndex; - - string mNetworkID; - - struct ServerVariable - { - std::string mName; - std::string mLastValueSent; - }; - - std::vector mServerVariableList; - - bool mUsedKilled; - - //TODO: remove this system from AvHPlayer and create an - // explicit balance forwarding class registered to each - // client instead. This functionality is tangential to - // AvHPlayer's role as a game entity and AvHPlayer has - // far too much responsibility (included in over 60 source - // files?!?). Other functionality should also be examined - // and refactored if appropriate. - mutable bool mBalanceMessagePending; //are we in the middle of a set of changes? - mutable std::set mBalanceRemovalList; - mutable BalanceFloatCollection mBalanceMapFloats; - mutable BalanceIntCollection mBalanceMapInts; - mutable BalanceStringCollection mBalanceMapStrings; - float mNextBalanceVarUpdate; -#ifdef USE_OLDAUTH - bool mAllowAuth; - int mAuthCheatMask; - int mCachedAuthenticationMask; -#endif - -}; - -typedef vector PlayerListType; - -#endif - +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHPlayer.h $ +// $Date: 2002/11/22 21:18:33 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHPlayer.h,v $ +// Revision 1.65 2002/11/22 21:18:33 Flayra +// - Potentially fixed strange Onos collision crash +// - Don't allow player to join team after he's seen another team +// - "lastinv" support +// - Fixed perma-cloak bug after losing last sensory chamber whilst cloaked +// - Started fixing commander PAS problem +// - Fixed readyroom "ghost player" exploit when F4 during REIN +// - Draw damage in debug, never otherwise +// +// Revision 1.64 2002/11/15 04:42:50 Flayra +// - Regenerate now returns true if healing was successful +// - Logging changes and fixes +// +// Revision 1.63 2002/11/13 01:49:08 Flayra +// - Proper death message logging for Psychostats +// +// Revision 1.62 2002/11/12 18:44:54 Flayra +// - Added mp_logdetail support for damage messages +// - Changed the alien ability anti-exploit code to try to co-exist with scripters +// +// Revision 1.61 2002/11/12 02:28:39 Flayra +// - Fixed problems with armor not being updated when armor upgrades completed +// - Aliens now keep same percentage of health and armor when morphing +// - Much better logging, up to standard +// - Don't add enemy buildings to hive sight unless parasited (solves blip spam) +// - Removed draw damage from public build +// - Changes to minimap to less overflows at end of big games +// +// Revision 1.60 2002/10/25 21:48:01 Flayra +// - Added more auth masks +// - Moved mSkin to pev->skin so playerclass could hold what used to be mPlayMode could be used to quickly perform culling, to try to prevent overflowage at the end of large games +// +// Revision 1.59 2002/10/24 21:40:02 Flayra +// - Reworked jetpack effect +// - Authicons update +// - Set builder for alien buildings, so turret kills can credit builder +// - Alien easter eggs +// - Network optimizations after game reset on huge (20-32) player games +// - Allow server ops to disable auth icons +// - Alien energy tweaks +// - Show unbuilt hives in hive sight +// - Move alien energy updating fully into shared code +// - Tried to fix full health ring showing for dead selected players +// - Moved help text client-side +// - Cache info_locations and gamma until map change +// - Skin fixes +// +// Revision 1.58 2002/10/20 21:10:57 Flayra +// - Optimizations +// +// Revision 1.57 2002/10/16 01:05:38 Flayra +// - Sent health as short for big aliens +// - Fixed sayings not triggering commander alerts +// - Now name changes are queued until the next match +// - Added authmask support +// - Egg idle sounds play more frequently, refactored too +// - Fixed preserving model in ready room after game end +// - Profiling of AddToFullPack +// - Fix for falling through lifts when morphing on them (untested) +// +// Revision 1.56 2002/10/03 19:32:06 Flayra +// - Reworked orders completely +// - Send max resources to players +// - Heavy armor sped up slightly +// - Kill players who illegally try to use alien abilities +// - Moved energy to a new variable, send health for aliens +// - Only freeze players during countdown +// - Removed slowdown when taking damage +// - Send blips in two messages, one friendly and one enemy (old bad hack) +// +// Revision 1.55 2002/09/25 20:50:06 Flayra +// - Added 3 new sayings +// - Frame-rate independent updating +// - Don't allow player to kill self while commanding +// +// Revision 1.54 2002/09/23 22:27:52 Flayra +// - Added skin support +// - Added client connected/disconnected hooks for particle system propagation optimizations +// - Removed power armor, added heavy armor +// - Fixed death animations +// - Added hook to see if commander has given an order and to see if he's idle +// - Bound resources for aliens +// - Soldiers asking for ammo and health trigger commander alert +// - Added gestation anims +// - Slowed down Onos movement +// - When cheats are enabled, purchases are free +// +// Revision 1.53 2002/09/09 20:04:53 Flayra +// - Added commander voting +// - Commander score is now the average of the rest of his players (reverted back when he leaves CC) +// - Fixed bug where upgrades were getting removed and then add repeatedly +// - Added multiple skins for marines +// - Play sound when aliens lose an upgrade +// - Changed fov to 90 for all aliens for software compatibility +// - Added hiveinfo drawing +// - Tweaked marine and alien speeds (to compensate for loss of drastic alien fov) +// +// Revision 1.52 2002/08/31 18:01:02 Flayra +// - Work at VALVe +// +// Revision 1.51 2002/08/16 02:42:57 Flayra +// - New damage types +// - Much more efficient blip calculation (at least it should be, I haven't measured it so who really knows) +// - New way of representing ensnare state for shared code (disabling jumping, jetpacking) +// - Removed old overwatch code +// - Store health in fuser2 for drawing health for commander +// - Swap bile bomb and umbra +// +// Revision 1.50 2002/08/09 01:10:30 Flayra +// - Keep previous model when a game is over and going back to ready room +// - Refactoring for scoreboard +// - Support for "jump" animation +// - Freeze player before game starts +// - Reset score when leaving a team +// +// Revision 1.49 2002/08/02 21:52:17 Flayra +// - Cleaned up jetpacks, made webbing useful again, help messages, added "orderself" cheat, changed gestation messages for new tooltip system, added GetHasAvailableUpgrades() for help system, removed particle template messages, slowed down particle template update rate to reduce overflows (woo!) +// +// Revision 1.48 2002/07/26 23:07:54 Flayra +// - Numerical feedback +// - New artwork for marine, with jetpack as body group (this code doesn't work) +// - Misc. fixes (alien vision mode not being preserved when it should, and staying when it shouldn't) +// +// Revision 1.47 2002/07/23 17:17:57 Flayra +// - Added power armor, added "talking" state for hive sight, changes for new resource model, removed max buildings, create buildings with random orientation, added stimpack, tweaked redemption, added new hive sight info +// +// Revision 1.46 2002/07/08 17:15:39 Flayra +// - Added validation of all impulses (assumes invalid by default, instead of valid), reset players like all other entities, ensnaring fixes, players are temporarily invulnerable when they spawn, preserve health and armor when using command station, fixed up redemption, add hooks for commander voting and going back to the ready room, models can't be changed via the console anymore +// +// Revision 1.45 2002/07/01 21:43:16 Flayra +// - Removed outdated adrenaline concept, added new alien sight mode, primal scream, removed flashlight battery message, fixed morphing problems for level 5 (and others), tweaked marine and alien speeds, fixed triple speed upgrade problem, disabled overwatch, moved player assets out of precache() (wasn't being called reliably), get full energy after a lifeform change, hive sight only draws out of sight now, added scent of fear +// +// Revision 1.44 2002/06/25 18:13:57 Flayra +// - Added death animations, added more general animation support, leaps do damage, general construct effects, new alien inventory system, added charging, info_locations, galloping, new alien building code, better morphing system (prevents getting stuck), more builder error messages, clear deaths on game end, hide health while gestating +// +// Revision 1.43 2002/06/10 20:03:13 Flayra +// - Updated for new minimap (remember killed position). Updated blood so marines aren't bloody, and aliens emit yellow blood. Removed slowing when hit (now players fly back when hit though), UpdateBlips() hack (fix when time) +// +// Revision 1.42 2002/05/28 18:03:41 Flayra +// - Refactored size for role code for movement chambers, deal with inventory properly (entity leak), increased web ensnaring effects (put weapon away!), reinforcement refactoring, tweaked speeds so marines are a little slower, and speed upgrade works properly again (now level 1 can generally outrun marines), added recycling support, play destroy egg sound when killed when morphing, new hive sight, EffectivePlayerClassChanged() refactoring +// +// Revision 1.41 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifndef AVHPLAYER_H +#define AVHPLAYER_H + +#include "dlls/extdll.h" +#include "dlls/util.h" + +#include "dlls/cbase.h" +#include "dlls/player.h" +#include "dlls/trains.h" +#include "dlls/nodes.h" +#include "dlls/weapons.h" +#include "dlls/soundent.h" +#include "dlls/monsters.h" +#include "engine/shake.h" +#include "dlls/decals.h" +#include "dlls/gamerules.h" +#include "mod/AvHConstants.h" +#include "mod/AvHTeam.h" +#include "mod/AvHMessage.h" +#include "mod/AvHSharedTypes.h" +#include "mod/AvHBasePlayerWeapon.h" +#include "mod/AvHEntityHierarchy.h" +#include "mod/AvHOrder.h" +#include "mod/AvHSpecials.h" +#include "common/usercmd.h" +#include "types.h" +#include "mod/AvHTechTree.h" +#include "common/weaponinfo.h" +#include "mod/AvHVisibleBlipList.h" +#include "mod/AvHBaseInfoLocation.h" +#include "mod/AvHCloakable.h" +#include "mod/AvHMessageList.h" +#include "util/Balance.h" + +#include //for balance information below... + +class AvHPlayer : public CBasePlayer, public AvHCloakable, public BalanceChangeListener +{ +public: + // AvHPlayer stuff + AvHPlayer(); + + void AddDebugEnemyBlip(float inX, float inY, float inZ); + void PrintWeaponListToClient(CBaseEntity *theAvHPlayer); + virtual void AddPoints( int score, BOOL bAllowNegativeScore ); + + virtual void AwardKill( entvars_t* pevTarget); + virtual int BloodColor(void); + bool BuildTech(AvHMessageID inBuildID, const Vector& inWorldPos); + void ClearBlips(); + void ClientDisconnected(); + bool DropItem(const char* inItemName = NULL); + bool GroupMessage(AvHMessageID inGroupMessage); + bool GetCenterPositionForGroup(int inGroupNumber, float& outX, float& outY); + + virtual void GetAnimationForActivity(int inActivity, char outAnimation[64], bool inGaitSequence = false); + + bool GetInReadyRoom(void) const; + AvHPlayer* GetCommander(void); + AvHPlayMode GetPlayMode(bool inIncludeSpectating = false) const; + bool GetHasLeftReadyRoom() const; + bool GetHasJetpack() const; + bool GetHasHeavyArmor() const; + bool GetHasAvailableUpgrades() const; + bool GetHasPowerArmor() const; + int GetHull() const; + virtual int GiveAmmo( int iAmount, char *szName, int iMax ); + bool GetShouldResupplyAmmo(); + float GetCurrentWeaponAmmoPercentage(); + virtual int GetMaxWalkSpeed() const; + int GetScore() const; + void SetScore(int inScore); + void PickSkin(); + void SetSkin(int inSkin); + bool GetCanCommand(string& outErrorMessage); + bool GetCanReceiveResources() const; + bool GetCanUseHive() const; + void SetTimeOfLastHiveUse(float time); + + void SetPlayMode(AvHPlayMode inPlayMode, bool inForceSpawn = false); + bool GetHasBeenSpectator(void) const; + void InitializeFromTeam(float inHealthPercentage = 1.0f, float inArmorPercentage = 1.0f); + bool GetIsAlien(bool inIncludeSpectating = false) const; + bool GetIsMarine(bool inIncludeSpectating = false) const; + bool GetIsInTopDownMode(bool inIncludeSpectating = false) const; + bool GetIsBeingDigested() const; + bool GetIsDigesting() const; + bool GetIsEntityInSight(CBaseEntity* inEntity); + bool GetIsValidReinforcementFor(AvHTeamNumber inTeam) const; + AvHTeamNumber GetTeam(bool inIncludeSpectating = false) const; + float GetKilledX() const; + float GetKilledY() const; + AvHTeam* GetTeamPointer(bool inIncludeSpectating = false) const; + bool GetIsAlienSightActive() const; + + AvHClassType GetClassType(void) const; + + bool GetCurrentWeaponCannotHolster() const; + //Activity GetDeathActivity(void); + bool GetEnemySighted(void) const; + bool GetIsFiring(void) const; + bool GetIsInOverwatch(void) const; + bool GetIsSpectatingTeam(AvHTeamNumber inTeamNumber) const; + bool GetIsSpectatingPlayer(int inPlayerNumber) const; + bool GiveCombatModeUpgrade(AvHMessageID inMessageID, bool inInstantaneous = false); + bool GetHasCombatModeUpgrade(AvHMessageID inMessageID) const; + + bool GetIsRelevant(bool inIncludeSpectating = false) const; + bool GetCanBeAffectedByEnemies() const; + bool GetIsSelected(int inEntityIndex) const; + bool RemoveSelection(int inEntityIndex); + void SetSelection(int inEntityIndex, bool inClearPreviousSelection = true); + bool GetIsMetabolizing() const; + void SetTimeOfMetabolizeEnd(float inTime); + virtual float GetOpacity() const; + bool GetIsSpectator() const; + void SetIsSpectator(); + float GetLastTimeInCommandStation() const; + + bool GetIsSpeaking(void) const; + AvHMessageID GetLastSaying() const; + bool GetOrdersRequested(void) const; + bool GetOrderAcknowledged(void) const; + string GetPlayerName() const; + + bool GetHasGivenOrder() const; + void SetHasGivenOrder(bool inState); + + int GetPointValue(void) const; + vec3_t GetVisualOrigin() const; + int GetRespawnCost() const; + + void AwardExperienceForObjective(float inHealthChange, AvHMessageID inMessageID); + float GetExperience() const; + void SetExperience(float inExperience); + int GetExperienceLevel() const; + + AvHServerPlayerData* GetServerPlayerData(); + + virtual bool GetHasItem(const char *szName); + virtual void GiveNamedItem(const char *szName, bool inSendMessage = false); + int GetNumberOfItems(); + void GiveResources(float inResources); + float GetTimeLastF4() const; + void SetTimeLastF4(float inTime); + float GetTimeStartedTopDown() const; + float GetTimeOfLastConstructUse() const; + void SetTimeOfLastConstructUse(float inTime); + float GetTimeOfLastSignificantCommanderAction() const; + void LogEmitRoleChange(); + void LogPlayerAction(const char* inActionDescription, const char* inActionData); + void LogPlayerActionPlayer(CBasePlayer* inAttackingPlayer, const char* inAction); + void LogPlayerAttackedPlayer(CBasePlayer* inAttackingPlayer, const char* inWeaponName, float inDamage); + void LogPlayerKilledPlayer(CBasePlayer* inAttackingPlayer, const char* inWeaponName); + + void StartLeap(); + + AvHUser3 GetPreviousUser3(bool inIncludeSpectating = false) const; + AvHUser3 GetUser3(bool inIncludeSpectating = false) const; + AvHMessageID GetEvolution(bool inIncludeSpectating = false) const; + bool GetSpecialPASOrigin(Vector& outOrigin); + void GiveTeamUpgrade(AvHMessageID inUpgrade, bool inGive = true); + bool HolsterWeaponToUse(); + void Kick(); + virtual void Killed( entvars_t *pevAttacker, int iGib ); + void NextWeapon(); + void ResetEntity(void); + bool PayPurchaseCost(int inCost); + void PlaybackNumericalEvent(int inEventID, int inNumber); + bool PlayHUDSound(AvHHUDSound inSound) const; + bool PlayHUDSound(AvHHUDSound inSound, float x, float y) const; + void PlayHUDStructureNotification(AvHMessageID inMessageID, const Vector& inLocation); + void PlayRandomRoleSound(string inSoundListName, int inChannel = CHAN_AUTO, float inVolume = 1.0f); + void PlayerConstructUse(); + void SetCurrentCommand(const struct usercmd_s* inCommand); + void SetDebugCSP(weapon_data_t* inWeaponData); + void SetPendingCommand(char* inCommand); + void TriggerProgressBar(int inEntityID, int inParam, int inPercent=0); + float GetTimeOfLastTeleport() const; + void SetTimeOfLastTeleport(float inTime); + bool SwitchWeapon(const char* inString); + virtual void StartObserver( Vector vecPosition, Vector vecViewAngle ); + virtual void StartObservingIfNotAlready(void); + void StartTopDownMode(void); + bool StopTopDownMode(void); + bool SetBeingDigestedMode(bool inBeingDigested); + + bool GetIsCloaked() const; + bool GetIsPartiallyCloaked() const; + void TriggerUncloak(); + + //Nexus interface - replaces all old auth information + bool GetIsAuthorized(AvHAuthAction inAction, int inParameter) const; +#ifdef USE_OLDAUTH + int GetAuthenticationMask(); + bool GetIsMember(const AvHPlayerAuthentication inAuthGroup); + bool GetAllowAuth() const; + void SetAllowAuth(bool inAllowAuth); + void SetAuthCheatMask(int inAuthCheatMask); +#else + bool GetIsMember(const string& inAuthGroup) const; +#endif + //END Nexus interface + + float GetTimeLastPlaying() const; + + bool GetHasSeenTeam(AvHTeamNumber inNumber) const; + void SetHasSeenTeam(AvHTeamNumber inNumber); + + float GetTimeOfLastSporeDamage() const; + void SetTimeOfLastSporeDamage(float inTime); + + // Quick ensnare system...add something better? + bool GetIsEnsnared() const; + bool GetIsAbleToAct() const; + + void DropAmmo(char *pszAmmoType, int iAmmoAmt, int iMax, int iWeaponID, Vector vecAngles); + void EffectivePlayerClassChanged(); + void NeedsTeamUpdate(); + void SendTeamUpdate(); + + // Returns true if successful. Fails if the player is too ensnared already + bool SetEnsnareState(bool inState); + + bool GetIsStunned() const; + bool SetIsStunned(bool inState, float inTime = 0.0f); + + bool GetIsCatalysted() const; + void SetIsCatalysted(bool inState, float inTime = 0.0f); + + bool Energize(float inEnergyAmount); + bool Heal(float inAmount, bool inPlaySound = true, bool dcHealing = false); + bool Regenerate(float inRegenerationAmount, bool inPlaySound = true, bool dcHealing = false); + bool GetIsScreaming(); + void StartScreaming(); + + //void UpgradeArmorLevel(void); + + // CBasePlayer stuff + virtual void ImpulseCommands( void ); + virtual void ItemPostFrame(void); + virtual void Jump( void ); + virtual void ObserverModeIllegal(); + virtual void PackDeadPlayerItems(void); + virtual void PreThink( void ); + virtual void Spawn( void ); + //virtual BOOL SwitchWeapon( CBasePlayerItem* pWeapon ); + virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + //virtual const char* TeamID( void ); + virtual void UpdateClientData( void ); + + virtual float GetCloakTime() const; + virtual int GetExperienceLevelsSpent() const; + virtual void SetExperienceLevelsSpent(int inSpentLevels); + + virtual string GetNetworkID() const; + virtual void SetNetworkID(string& inNetworkID); + + virtual string GetNetworkAddress() const; + virtual void SetNetworkAddress(string& inNetworkAddress); + + virtual bool GetIsTemporarilyInvulnerable() const; + AvHTechTree& GetCombatNodes(); + MessageIDListType& GetPurchasedCombatUpgrades(); + bool Redeem(); + + bool GetCanBeResupplied() const; + bool Resupply(bool inGiveHealth = false); + + void SetDesiredNetName(string inDesiredNetName); + void SetDesiredRoomType(int inRoomType, bool inForceUpdate = false); + + // Gets origin of the player after changing the iuser3. + void GetNewOrigin(AvHUser3 inNewUser3, bool inDucking, vec3_t& outOrigin) const; + + float GetResources(bool inIncludeSpectating = false) const; + void SetUser3(AvHUser3 inUser3, bool inForceChange = false, bool inGiveWeapons = true); + void SetResources(float inResources, bool inPlaySound = false); + + // Send messages to player's screen + bool SendMessage(const char* pMessage, SHOWMESSAGE_TYPE type = NORMAL); + bool SendMessageOnce(const char* pMessage, SHOWMESSAGE_TYPE type = NORMAL); + bool SendMessageNextThink(const char* pMessage); + + virtual int GetEffectivePlayerClass(); + + void BecomePod(); + void SetModelFromState(); + + int GetDigestee() const; + void SetDigestee(int inPlayerID); + void StartDigestion(int inDigestee); + void StopDigestion(bool inDigested); + + void ProcessEntityBlip(CBaseEntity* inEntity); + + void SetPosition(const Vector& inNewPosition); + virtual char* GetPlayerModelKeyName(); + void GetSize(Vector& outMinSize, Vector& outMaxSize) const; + void SetSizeForUser3(); + + void GetViewForUser3(AvHUser3 inUser3, bool inIsDucking, float& outFOV, float& outOffset) const; + void SetViewForUser3(); + void SetWeaponsForUser3(); + + bool RunClientScript(const string& inScriptName); + + void TriggerFog(int inFogEntity, float inFogExpireTime); + + void UpdateInventoryEnabledState(int inNumActiveHives, bool inForceUpdate = false); + + virtual bool GetCanUseWeapon() const; + + void PropagateServerVariables(); + + bool GetUsedKilled() { return mUsedKilled; } + void SetUsedKilled(bool bKilled ) { mUsedKilled = bKilled; } + void ClearOrders() { mClientOrders.clear(); } + + // : 0000953 + bool JoinTeamCooledDown(float inCoolDownTime); + // + + bool GetHasSeenATeam(); +private: + void AcquireOverwatchTarget(); + bool AttemptToBuildAlienStructure(AvHMessageID inMessageID); + void ClearRoleAbilities(); + void ClearUserVariables(); + CBaseEntity* CreateAndDrop(const char* inItemName); + void DeployCurrent(); + bool ExecuteAlienMorphMessage(AvHMessageID inMessageID, bool inInstantaneous); + bool ExecuteCombatMessage(AvHMessageID inMessageID, bool& outIsAvailable, bool inForce = false); + bool ExecuteMessage(AvHMessageID inMessageID, bool inInstantaneous = false, bool inForce = false); + float GetAlienAdjustedEventVolume() const; + bool GetCanGestate(AvHMessageID inMessageID, string& outErrorMessage); + bool GetDoesCurrentStateStopOverwatch() const; + bool QueryEnemySighted(CBaseEntity* inEntity); + bool GetHasActiveAlienWeaponWithImpulse(AvHMessageID inMessageID) const; + bool GetRandomGameStartedTick(float inApproximateFrameRate); + bool GetPurchaseAllowed(AvHMessageID inUpgrade, int& outCost, string* outErrorMessage = NULL) const; + int GetRelevantWeight(void) const; + int GetRelevantWeightForWeapon(AvHBasePlayerWeapon* inWeapon) const; + void GetSpeeds(int& outBaseSpeed, int& outUnemcumberedSpeed) const; + void GiveCombatUpgradesOnSpawn(); + bool GiveOrderToSelection(AvHOrderType inOrder, Vector inNormRay); + void GiveOrderToSelection(AvHOrder& inOrder); + void GiveUpgrade(AvHMessageID inUpgrade); + void HandleOverwatch(void); + void HandleResearch(); + void HolsterCurrent(); + void InitializeTechNodes(); + void HandleTopDownInput(); + void ProcessEvolution(); + void ProcessCombatDeath(); + void ProcessResourceAdjustment(AvHMessageID inMessageID); + void ResetBehavior(bool inRemoveFromTeam); + void ResetGameNewMap(); + void ResetPlayerPVS(); + void SetCombatNodes(const AvHTechTree& inTechNodes); + void SetLifeformCombatNodesAvailable(bool inAvailable); + void ValidateClientMoveEvents(); + + void InternalAlienThink(); + void InternalAlienUpgradesThink(); + void InternalAlienUpgradesCloakingThink(); + void InternalAlienUpgradesPheromonesThink(); + void InternalAlienUpgradesRegenerationThink(); + void InternalCommanderThink(); + void InternalBoundResources(); + void InternalCommonThink(); + void InternalCombatThink(); + void InternalDigestionThink(); + void InternalEnemySightedPreThink(); + void InternalFogThink(); + void InternalHUDThink(); + void InternalMarineThink(); + void InternalPreThink(); + void InternalProgressBarThink(); + void InternalSpeakingThink(); + void InternalMovementThink(); + + void EXPORT PlayerTouch(CBaseEntity* inOther); + + bool PlaySaying(AvHMessageID inMessageID); + bool ProcessClickEvent(bool inLMB, float inWorldX, float inWorldY); + void ProcessTechUpgrade(AvHMessageID inMessageID); + void Research(AvHMessageID inMessageID, int inEntityIndex); + void Evolve(AvHMessageID inMessageID, bool inInstantaneous = false); + void PurchaseCombatUpgrade(AvHMessageID inMessageID); + void RecalculateSpeed(void); + void ReloadWeapon(void); + void RemoveCombatUpgrade(AvHMessageID inMessageID); + void RemoveCombatUpgradesPremptedBy(AvHMessageID inMessageID); + void ResetOverwatch(); + void RevertHealthArmorPercentages(); + void SaveHealthArmorPercentages(); + void SetMoveTypeForUser3(); + //bool SpawnReinforcements(void); + void TurnOverwatchTowardsTarget(CBaseEntity* theTarget); + void TurnOffOverwatch(); + void TurnOnOverwatch(); + void UpdateAmbientSounds(); + + //void UpdateArmor(); + void UpdateAlienUI(); + void UpdateMarineUI(); + void UpdateBlips(); + void UpdateDebugCSP(); + void UpdateEffectiveClassAndTeam(); + void UpdateEntityHierarchy(); + void UpdateExperienceLevelsSpent(); + void UpdateFirst(); + void UpdateFog(); + void UpdateGamma(); + void UpdateInfoLocations(); + void UpdateParticleTemplates(); + void UpdateOrders(); + void UpdateOverwatch(); + void UpdatePendingClientScripts(); + void UpdateProgressBar(); + void UpdateVUser4(); + void UpdateSetSelect(); + void UpdateSpawnScreenFade(); + void UpdateSoundNames(); + void UpdateTechNodes(); + void UpdateTopDownMode(); + + void Init(); + + //BalanceChangeListener implementation + void InitBalanceVariables(void); + void UpdateBalanceVariables(void); + bool shouldNotify(const string& name, const BalanceValueType type) const; + void balanceCleared(void) const; + void balanceValueInserted(const string& name, const float value) const; + void balanceValueInserted(const string& name, const int value) const; + void balanceValueInserted(const string& name, const string& value) const; + void balanceValueChanged(const string& name, const float old_value, const float new_value) const; + void balanceValueChanged(const string& name, const int old_value, const int new_value) const; + void balanceValueChanged(const string& name, const string& old_value, const string& default_value) const; + void balanceValueRemoved(const string& name, const float old_value) const; + void balanceValueRemoved(const string& name, const int old_value) const; + void balanceValueRemoved(const string& name, const string& old_value) const; + + float mResources; + + bool mFirstUpdate; + bool mNewMap; + + bool mHasSeenTeamA; + bool mHasSeenTeamB; + + string mQueuedThinkMessage; + + bool mClientInOverwatch; + bool mInOverwatch; + bool mOverwatchEnabled; + int mOverwatchTarget; + float mTimeOfLastOverwatchPreventingAction; + float mTimeLastSeenOverwatchTarget; + Vector mOverwatchFacing; + bool mOverwatchFiredThisThink; + + // : 0000953 + float mTimeLastJoinTeam; + // + + // alien upgrades + float mTimeOfLastHiveUse; + float mTimeOfLastRegeneration; + float mTimeOfLastDCRegeneration; + float mTimeOfLastPheromone; + + float mTimeOfLastUse; + + float mTimeLeapEnd; + + Vector mPositionBeforeTopDown; + Vector mAnglesBeforeTopDown; + Vector mViewAnglesBeforeTopDown; + Vector mViewOfsBeforeTopDown; + string mAnimExtensionBeforeTopDown; + bool mClientInTopDownMode; + bool mInTopDownMode; + int mTimeStartedTopDown; + + float mTimeOfLastF4; + float mTimeOfLastSaying; + AvHMessageID mLastSaying; + bool mIsSpeaking; + bool mOrdersRequested; + bool mOrderAcknowledged; + float mTimeOfLastEnemySighting; + bool mEnemySighted; + + float mTimeOfLastMovementSound; + + bool mTriggerUncloak; + + bool mHasLeftReadyRoom; + bool mHasBeenSpectator; + char* mPendingCommand; + + struct usercmd_s mCurrentCommand; + bool mAttackOneDown; + bool mAttackTwoDown; + Vector mAttackOnePressedWorldPos; + Vector mAttackTwoPressedWorldPos; + Vector mMouseWorldPos; + bool mPlacingBuilding; + + EntityListType mSelected; + EntityListType mClientSelected; + + EntityInfo mTrackingEntity; + EntityInfo mClientTrackingEntity; + + EntityListType mClientGroups[kNumHotkeyGroups]; + AvHAlertType mClientGroupAlerts[kNumHotkeyGroups]; + int mClientRequests[kNumRequestTypes]; + EntityListType mClientSelectAllGroup; + + OrderListType mClientOrders; + + AvHEntityHierarchy mClientEntityHierarchy; + + // Research nodes for commander + AvHTechTree mClientTechNodes; + MessageIDListType mClientTechDelta; + AvHTechSlotListType mClientTechSlotList; + + AvHMessageID mClientResearchingTech; + + float mClientGamma; + + StringList mSentMessageList; + + AvHVisibleBlipList mFriendlyBlips; + AvHVisibleBlipList mEnemyBlips; + + AvHVisibleBlipList mClientFriendlyBlips; + AvHVisibleBlipList mClientEnemyBlips; + + int mClientCommander; + + Vector mSpecialPASOrigin; + Vector mClientSpecialPASOrigin; + float mTimeOfLastPASUpdate; + + + bool mClientIsAlien; + + bool mAlienSightActive; + + float mTimeOfLastTeleport; + float mTimeOfLastRedeem; + + float mJetpackEnergy; + bool mJetpacking; + + weapon_data_t mClientDebugCSPInfo; + weapon_data_t mDebugCSPInfo; + float mClientNextAttack; + + float mTimeToBeUnensnared; + float mLastTimeEnsnared; + int mMaxWalkSpeed; + int mMaxGallopSpeed; + vec3_t mLastGallopViewDirection; + + float mTimeToBeFreeToMove; + float mTimeToEndCatalyst; + + float mLastTimeInCommandStation; + float mLastTimeCheckedRedemption; + float mLastTimeRedemptionTriggered; + + float mLastTimeStartedPlaying; + + float mTimeOfLastHelpText; + bool mLastHelpWasGeneral; + + float mLastPowerArmorThink; + float mLastInventoryThink; + + int mNumHives; + + float mTimeLastPlaying; + + float mTimeGestationStarted; + + AvHUser3 mPreviousUser3; + float mSavedJetpackEnergy; + + AvHMessageID mEvolution; + float mHealthPercentBefore; + float mArmorPercentBefore; + + StringList mClientSoundNames; + + bool mIsScreaming; + float mTimeStartedScream; + + float mKilledX, mKilledY; + + int mNumParticleTemplatesSent; + float mTimeOfLastParticleTemplateSending; + + int mClientProgressBarEntityIndex; + int mProgressBarEntityIndex; + int mProgressBarParam; + int mProgressBarCompleted; + float mTimeProgressBarTriggered; + + float mTimeOfLastFogTrigger; + float mFogExpireTime; + int mCurrentFogEntity; + int mClientCurrentFogEntity; + + AvHBaseInfoLocationListType mClientInfoLocations; + + HiveInfoListType mClientHiveInfo; + + AvHAlienUpgradeListType mClientUpgrades; + + StringList mPendingClientScripts; + + bool mHasGivenOrder; + float mTimeOfLastSignificantCommanderAction; + + int mPreThinkTicks; + float mPreThinkFrameRate; + + string mDesiredNetName; + + int mClientMenuTechSlots; + + float mTimeOfLastClassAndTeamUpdate; + bool mEffectivePlayerClassChanged; + bool mNeedsTeamUpdate; + bool mSendTeamUpdate; + bool mSendSpawnScreenFade; + + int mDigestee; + float mTimeOfLastDigestDamage; + float mTimeOfLastCombatThink; + + int mDesiredRoomType; + int mClientDesiredRoomType; + + float mTimeOfLastConstructUseAnimation; + float mTimeOfLastConstructUse; + + float mTimeOfLastResupply; + + float mTimeOfMetabolizeEnd; + + AvHMessageID mLastSelectEvent; + Vector mPositionBeforeLastGotoGroup; + + float mTimeOfLastSporeDamage; + float mTimeOfLastTouchDamage; + + string mLastMessageSent; + + float mExperience; + float mClientPercentToNextLevel; + int mExperienceLevelsSpent; + int mClientExperienceLevelsSpent; + + MessageIDListType mPurchasedCombatUpgrades; + MessageIDListType mGiveCombatUpgrades; + + AvHTechTree mCombatNodes; + + int mScore; + int mSavedCombatFrags; + float mLastUpdateTime; + + string mNetworkAddress; + int mLastModelIndex; + + string mNetworkID; + int mMarineHUDUpgrades; + int mNumMovement; + int mNumSensory; + int mNumDefense; + + struct ServerVariable + { + const cvar_t* mCvar; + int mLastValueSent; + bool mForceResend; + }; + + std::vector mServerVariableList; + + bool mUsedKilled; + + //TODO: remove this system from AvHPlayer and create an + // explicit balance forwarding class registered to each + // client instead. This functionality is tangential to + // AvHPlayer's role as a game entity and AvHPlayer has + // far too much responsibility (included in over 60 source + // files?!?). Other functionality should also be examined + // and refactored if appropriate. + mutable bool mBalanceMessagePending; //are we in the middle of a set of changes? + mutable std::set mBalanceRemovalList; + mutable BalanceFloatCollection mBalanceMapFloats; + mutable BalanceIntCollection mBalanceMapInts; + mutable BalanceStringCollection mBalanceMapStrings; + float mNextBalanceVarUpdate; +#ifdef USE_OLDAUTH + bool mAllowAuth; + int mAuthCheatMask; + int mCachedAuthenticationMask; +#endif + +}; + +typedef vector PlayerListType; + +#endif + diff --git a/main/source/mod/AvHPlayerUpgrade.cpp b/main/source/mod/AvHPlayerUpgrade.cpp index 6683616..462588b 100644 --- a/main/source/mod/AvHPlayerUpgrade.cpp +++ b/main/source/mod/AvHPlayerUpgrade.cpp @@ -1,550 +1,549 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHPlayerUpgrade.cpp $ -// $Date: 2002/11/05 06:17:26 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHPlayerUpgrade.cpp,v $ -// Revision 1.25 2002/11/05 06:17:26 Flayra -// - Balance changes -// -// Revision 1.24 2002/10/20 02:36:14 Flayra -// - Regular update -// -// Revision 1.23 2002/10/18 22:22:04 Flayra -// - Toned down fade armor -// -// Revision 1.22 2002/10/04 18:03:43 Flayra -// - Soldier armor reduced back to 50 + 20 per upgrade (instead of 100 + 20). Means two skulk bites to kill. -// -// Revision 1.21 2002/10/03 19:02:19 Flayra -// - Toned down primal scream -// -// Revision 1.20 2002/09/25 20:50:23 Flayra -// - Increased armor for soldiers to 100 -// - Heavy armor now takes 100% of damage -// -// Revision 1.19 2002/09/23 22:02:56 Flayra -// - Fixed bug where damage upgrades were applying when they shouldn't (for aliens) -// - Added heavy armor -// -// Revision 1.18 2002/09/09 20:05:20 Flayra -// - More heavily armored cocooned aliens to allow them to morph in combat a bit more and for tension when encountered -// -// Revision 1.17 2002/08/31 18:01:02 Flayra -// - Work at VALVe -// -// Revision 1.16 2002/08/16 02:43:16 Flayra -// - Adjusted power armor slightly so it's not worse then regular armor at any point -// -// Revision 1.15 2002/07/28 19:21:28 Flayra -// - Balance changes after/during RC4a -// -// Revision 1.14 2002/07/23 17:18:47 Flayra -// - Level 1 armor toned down, power armor changes -// -// Revision 1.13 2002/07/01 21:43:56 Flayra -// - Added offensive upgrade back, when under the influence...of primal scream! -// -// Revision 1.12 2002/06/25 18:14:40 Flayra -// - Removed old offensive upgrades -// -// Revision 1.11 2002/06/03 16:55:49 Flayra -// - Toned down carapace and marine upgrades -// -// Revision 1.10 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHSpecials.h" -#include "util/Balance.h" -#include "common/damagetypes.h" - -const int kWeaponTracerDefault = 0; -const int kWeaponTracerLevelOne = 6; -const int kWeaponTracerLevelTwo = 4; -const int kWeaponTracerLevelThree = 2; - -float AvHPlayerUpgrade::GetAlienMeleeDamageUpgrade(int inUpgrade, bool inIncludeFocus) -{ - float theMultiplier = 1.0f; - - if(GetHasUpgrade(inUpgrade, MASK_BUFFED)) - { - theMultiplier = 1.0f + (float)BALANCE_VAR(kPrimalScreamDamageModifier); - } - - if(inIncludeFocus) - { - theMultiplier *= AvHPlayerUpgrade::GetFocusDamageUpgrade(inUpgrade); - } - - return theMultiplier; -} - -float AvHPlayerUpgrade::GetAlienRangedDamageUpgrade(int inUpgrade) -{ - float theMultiplier = 1.0f; - - if(GetHasUpgrade(inUpgrade, MASK_BUFFED)) - { - theMultiplier = 1.0f + (float)BALANCE_VAR(kPrimalScreamDamageModifier); - } - - return theMultiplier; -} - -float AvHPlayerUpgrade::GetFocusDamageUpgrade(int inUpgrade) -{ - float theFocusDamageUpgrade = 1.0f; - - int theFocusLevel = AvHGetAlienUpgradeLevel(inUpgrade, MASK_UPGRADE_8); - if(theFocusLevel > 0) - { - // Increase damage for each level of focus - const float theFocusDamageUpgradePercentPerLevel = (float)BALANCE_VAR(kFocusDamageUpgradePercentPerLevel); - theFocusDamageUpgrade += theFocusLevel*theFocusDamageUpgradePercentPerLevel; - } - - return theFocusDamageUpgrade; -} - -float AvHPlayerUpgrade::GetArmorValue(int inNumHives) -{ - // Each point of armor is work 1/x points of health - float theArmorValueNonAlien = BALANCE_VAR(kArmorValueNonAlien); - float theArmorBonus = theArmorValueNonAlien; - - if(inNumHives > 0) - { - float theArmorValueBase = 1.0f + (float)BALANCE_VAR(kArmorValueBase); - float theArmorValuePerHive = (float)BALANCE_VAR(kArmorValuePerHive); - inNumHives = min(inNumHives, kMaxHives); - - theArmorBonus = (theArmorValueBase + inNumHives*theArmorValuePerHive); - } - - // Smaller is better - theArmorBonus = 1.0f/theArmorBonus; - - return theArmorBonus; -} - -float AvHPlayerUpgrade::GetArmorAbsorption(AvHUser3 inUser3, int inUpgrade, int inNumHives) -{ - // A value of .2 means the armor Takes 80% of the damage, so the value gets smaller as it improves - float theAbsorption = (float)BALANCE_VAR(kArmorAbsorptionBase); - inNumHives = min(inNumHives, kMaxHives);//voogru: prevent aliens taking negative damage if some mapper goon (or me :o) ) decides to put more than 3 hives on the map. - - // Heavy armor is the shiznit - if(inUser3 == AVH_USER3_MARINE_PLAYER && GetHasUpgrade(inUpgrade, MASK_UPGRADE_13)) - { - float theHeavyArmorAbsorbPercent = BALANCE_VAR(kHeavyArmorAbsorbPercent)/100.0f; - ASSERT(theHeavyArmorAbsorbPercent >= 0.0f); - ASSERT(theHeavyArmorAbsorbPercent <= 1.0f); - - theAbsorption = 1.0f - theHeavyArmorAbsorbPercent; - } - - // Increase absorption at higher hive-levels to make sure armor is always used before player dies - if(inNumHives) - { - switch(inUser3) - { - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: - case AVH_USER3_ALIEN_EMBRYO: - theAbsorption -= (inNumHives - 1)*(float)BALANCE_VAR(kArmorAbsorptionPerExtraHive); - break; - } - } - - ASSERT(theAbsorption >= 0.0f); - ASSERT(theAbsorption <= 1.0f); - - return theAbsorption; -} - -int AvHPlayerUpgrade::GetMaxHealth(int inUpgrade, AvHUser3 inUser3, int inLevel) -{ - int theMaxHealth = 0; - int theHealthLevelIncrementPercent = BALANCE_VAR(kCombatLevelHealthIncrease); - - // TODO: Take into account upgrade if added - - switch(inUser3) - { - default: - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_COMMANDER_PLAYER: - theMaxHealth = BALANCE_VAR(kMarineHealth); - break; - - case AVH_USER3_ALIEN_PLAYER1: - theMaxHealth = BALANCE_VAR(kSkulkHealth); - break; - - case AVH_USER3_ALIEN_PLAYER2: - theMaxHealth = BALANCE_VAR(kGorgeHealth); - break; - - case AVH_USER3_ALIEN_PLAYER3: - theMaxHealth = BALANCE_VAR(kLerkHealth); - break; - - case AVH_USER3_ALIEN_PLAYER4: - theMaxHealth = BALANCE_VAR(kFadeHealth); - break; - - case AVH_USER3_ALIEN_PLAYER5: - theMaxHealth = BALANCE_VAR(kOnosHealth); - break; - - case AVH_USER3_ALIEN_EMBRYO: - theMaxHealth = BALANCE_VAR(kGestateHealth); - break; - } - - //return (1.0f + (inLevel - 1)*theHealthLevelIncrementPercent)*theMaxHealth; - return theMaxHealth + (inLevel - 1)*theHealthLevelIncrementPercent; -} - - -int AvHPlayerUpgrade::GetMaxArmorLevel(int inUpgrade, AvHUser3 inUser3) -{ - int theMaxArmorLevel = 0; - - int theArmorLevel = AvHPlayerUpgrade::GetArmorUpgrade(inUser3, inUpgrade); - //bool theHasAlienCarapace = GetHasUpgrade(inUpgrade, MASK_UPGRADE_1); - bool theHasPowerArmor = GetHasUpgrade(inUpgrade, MASK_UPGRADE_10); - bool theHasHeavyArmor = GetHasUpgrade(inUpgrade, MASK_UPGRADE_13); - - // Upgrade TODO: Take different levels into account? - - switch(inUser3) - { - default: - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_COMMANDER_PLAYER: - //theMaxArmorLevel = 100 + theArmorLevel*20; - theMaxArmorLevel = BALANCE_VAR(kMarineBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kMarineArmorUpgrade)); - if(theHasHeavyArmor) - { - theMaxArmorLevel = BALANCE_VAR(kMarineBaseHeavyArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kMarineHeavyArmorUpgrade)); - } - //if(theHasPowerArmor) - //{ - // theMaxArmorLevel *= 2; - //} - break; - - case AVH_USER3_ALIEN_PLAYER1: - theMaxArmorLevel = BALANCE_VAR(kSkulkBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kSkulkArmorUpgrade));//(theHasAlienCarapace ? 30 : 10); - break; - - case AVH_USER3_ALIEN_PLAYER2: - theMaxArmorLevel = BALANCE_VAR(kGorgeBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kGorgeArmorUpgrade));//(theHasAlienCarapace ? 75 : 50); - break; - - case AVH_USER3_ALIEN_PLAYER3: - theMaxArmorLevel = BALANCE_VAR(kLerkBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kLerkArmorUpgrade));//(theHasAlienCarapace ? 75 : 50); - break; - - case AVH_USER3_ALIEN_PLAYER4: - theMaxArmorLevel = BALANCE_VAR(kFadeBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kFadeArmorUpgrade));//(theHasAlienCarapace ? 150 : 125); - break; - - case AVH_USER3_ALIEN_PLAYER5: - theMaxArmorLevel = BALANCE_VAR(kOnosBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kOnosArmorUpgrade));//(theHasAlienCarapace ? 200 : 150); - break; - - case AVH_USER3_ALIEN_EMBRYO: - theMaxArmorLevel = BALANCE_VAR(kGestateBaseArmor); - break; - - } - - return theMaxArmorLevel; -} - -// Returns the level: 0, 1, 2 or 3 -int AvHPlayerUpgrade::GetArmorUpgrade(AvHUser3 inUser3, int inUpgrade, float* theArmorMultiplier) -{ - int theUpgradeLevel = 0; - - if(theArmorMultiplier) - *theArmorMultiplier = 1.0f; - - bool theIsMarine = false; - switch(inUser3) - { - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_COMMANDER_PLAYER: - theIsMarine = true; - break; - } - - if(theIsMarine) - { - if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_6)) - { - if(theArmorMultiplier) - *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kMarineArmorLevelThree); - theUpgradeLevel = 3; - } - else if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_5)) - { - if(theArmorMultiplier) - *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kMarineArmorLevelTwo); - - theUpgradeLevel = 2; - } - else if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_4)) - { - if(theArmorMultiplier) - *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kMarineArmorLevelOne); - - theUpgradeLevel = 1; - } - } - else - { - if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_1)) - { - if(theArmorMultiplier) - { - *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kAlienArmorLevelOne); - } - theUpgradeLevel = 1; - - if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_10)) - { - if(theArmorMultiplier) - { - *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kAlienArmorLevelTwo); - } - - theUpgradeLevel = 2; - } - if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_11)) - { - if(theArmorMultiplier) - { - *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kAlienArmorLevelThree); - } - - theUpgradeLevel = 3; - } - } - } - - return theUpgradeLevel; -} - -// Returns the level: 0, 1, 2 or 3 -int AvHPlayerUpgrade::GetWeaponUpgrade(int inUser3, int inUpgrade, float* outDamageMultiplier, int* outTracerFreq, float* outSpread) -{ - int theUpgradeLevel = 0; - - if(outDamageMultiplier) - *outDamageMultiplier = 1.0f; - - if(outTracerFreq) - *outTracerFreq = kWeaponTracerDefault; - - // Only marines and marine items can get damage upgrades - switch(inUser3) - { - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_TURRET: - case AVH_USER3_SIEGETURRET: - case AVH_USER3_MARINEITEM: - case AVH_USER3_MINE: - case AVH_USER3_NUKE: - // Apply extra damage for upgrade - if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_3)) - { - if(outDamageMultiplier) - *outDamageMultiplier *= (1.0f + (float)BALANCE_VAR(kWeaponDamageLevelThree)); - if(outTracerFreq) - *outTracerFreq = kWeaponTracerLevelThree; - - theUpgradeLevel = 3; - } - else if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_2)) - { - if(outDamageMultiplier) - *outDamageMultiplier *= (1.0f + (float)BALANCE_VAR(kWeaponDamageLevelTwo)); - if(outTracerFreq) - *outTracerFreq = kWeaponTracerLevelTwo; - theUpgradeLevel = 2; - } - else if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_1)) - { - if(outDamageMultiplier) - *outDamageMultiplier *= (1.0f + (float)BALANCE_VAR(kWeaponDamageLevelOne)); - - if(outTracerFreq) - *outTracerFreq = kWeaponTracerLevelOne; - - theUpgradeLevel = 1; - } - break; - } - - return theUpgradeLevel; -} - -float AvHPlayerUpgrade::GetSilenceVolumeLevel(AvHUser3 inUser3, int inUpgrade) -{ - int theSilenceLevel = 0; - - switch(inUser3) - { - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_PLAYER4: - case AVH_USER3_ALIEN_PLAYER5: - case AVH_USER3_ALIEN_EMBRYO: - theSilenceLevel = AvHGetAlienUpgradeLevel(inUpgrade, MASK_UPGRADE_6); - break; - } - - //float theSilenceVolumeFactor = (1 - (theSilenceLevel/3.0f)); - - float theSilenceVolumeFactor = 1.0f; - switch(theSilenceLevel) - { - case 1: - theSilenceVolumeFactor = (float)BALANCE_VAR(kSilenceLevel1Volume); - break; - case 2: - theSilenceVolumeFactor = (float)BALANCE_VAR(kSilenceLevel2Volume); - break; - case 3: - theSilenceVolumeFactor = (float)BALANCE_VAR(kSilenceLevel3Volume); - break; - } - - return theSilenceVolumeFactor; -} - -float AvHPlayerUpgrade::CalculateDamageLessArmor(AvHUser3 inUser3, int inUser4, float flDamage, float& ioArmorValue, int bitsDamageType, int inNumHives) -{ - // if we're alien, if we have the armor upgrade, we take less damage off the top -// if(AvHGetIsAlien(inUser3)) -// { -// int theArmorUpgradeLevel = AvHGetAlienUpgradeLevel(inUser4, MASK_UPGRADE_1); -// if((theArmorUpgradeLevel > 0) && ((int)(ioArmorValue) > 0)) -// { -// float thePercentageOffTop = .1f*theArmorUpgradeLevel; -// flDamage -= flDamage*thePercentageOffTop; -// } -// } - - float flRatio = AvHPlayerUpgrade::GetArmorAbsorption(inUser3, inUser4, inNumHives); - float flBonus = AvHPlayerUpgrade::GetArmorValue(inNumHives); - - // Level 1 aliens don't take falling damage, ever - if((inUser3 == AVH_USER3_ALIEN_PLAYER1) && (bitsDamageType & DMG_FALL)) - { - flDamage = 0.0f; - } - - // Armor. - if (ioArmorValue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage! - { - float flNew = flDamage*flRatio; - float flArmor = (flDamage - flNew)*flBonus; - - // Does this use more armor than we have? - if (flArmor > ioArmorValue) - { - flArmor = ioArmorValue; - flArmor *= (1/flBonus); - flNew = flDamage - flArmor; - ioArmorValue = 0; - } - else - { - ioArmorValue -= flArmor; - if ( bitsDamageType & (NS_DMG_ACID) ) - { - ioArmorValue -= flArmor; - } - } - - flDamage = flNew; - } - - return flDamage; -} - -const int kCombatMinLevel = 1; -const int kCombatMaxLevel = 10; - -float AvHPlayerUpgrade::GetExperienceForLevel(int inLevel) -{ - float theExperienceForLevel = 0.0f; - int theLevel = 1; - - int theCombatBaseExperience = BALANCE_VAR(kCombatBaseExperience); - float theCombatLevelExperienceModifier = (float)BALANCE_VAR(kCombatLevelExperienceModifier); - - while((theLevel < inLevel) && (theCombatLevelExperienceModifier > 0)) - { - theExperienceForLevel += (1.0f + (theLevel-1)*theCombatLevelExperienceModifier)*theCombatBaseExperience; - theLevel++; - } - - return theExperienceForLevel; -} - -int AvHPlayerUpgrade::GetPlayerLevel(float inExperience) -{ - int thePlayerLevel = 1; - - int theCombatBaseExperience = BALANCE_VAR(kCombatBaseExperience); - float theCombatLevelExperienceModifier = (float)BALANCE_VAR(kCombatLevelExperienceModifier); - - while((inExperience > 0) && (theCombatLevelExperienceModifier > 0)) - { - inExperience -= (1.0f + (thePlayerLevel - 1)*theCombatLevelExperienceModifier)*theCombatBaseExperience; - - if(inExperience > 0) - { - thePlayerLevel++; - } - } - - thePlayerLevel = max(min(thePlayerLevel, kCombatMaxLevel), kCombatMinLevel); - - return thePlayerLevel; -} - -float AvHPlayerUpgrade::GetPercentToNextLevel(float inExperience) -{ - int theCurrentLevel = AvHPlayerUpgrade::GetPlayerLevel(inExperience); - int theNextLevel = min(max(theCurrentLevel + 1, kCombatMinLevel), kCombatMaxLevel); - - float theExperienceForCurrentLevel = AvHPlayerUpgrade::GetExperienceForLevel(theCurrentLevel); - float theExperienceForNextLevel = AvHPlayerUpgrade::GetExperienceForLevel(theNextLevel); - - float thePercent = (inExperience - theExperienceForCurrentLevel)/(theExperienceForNextLevel - theExperienceForCurrentLevel); - - thePercent = min(max(0.0f, thePercent), 1.0f); - - return thePercent; +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHPlayerUpgrade.cpp $ +// $Date: 2002/11/05 06:17:26 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHPlayerUpgrade.cpp,v $ +// Revision 1.25 2002/11/05 06:17:26 Flayra +// - Balance changes +// +// Revision 1.24 2002/10/20 02:36:14 Flayra +// - Regular update +// +// Revision 1.23 2002/10/18 22:22:04 Flayra +// - Toned down fade armor +// +// Revision 1.22 2002/10/04 18:03:43 Flayra +// - Soldier armor reduced back to 50 + 20 per upgrade (instead of 100 + 20). Means two skulk bites to kill. +// +// Revision 1.21 2002/10/03 19:02:19 Flayra +// - Toned down primal scream +// +// Revision 1.20 2002/09/25 20:50:23 Flayra +// - Increased armor for soldiers to 100 +// - Heavy armor now takes 100% of damage +// +// Revision 1.19 2002/09/23 22:02:56 Flayra +// - Fixed bug where damage upgrades were applying when they shouldn't (for aliens) +// - Added heavy armor +// +// Revision 1.18 2002/09/09 20:05:20 Flayra +// - More heavily armored cocooned aliens to allow them to morph in combat a bit more and for tension when encountered +// +// Revision 1.17 2002/08/31 18:01:02 Flayra +// - Work at VALVe +// +// Revision 1.16 2002/08/16 02:43:16 Flayra +// - Adjusted power armor slightly so it's not worse then regular armor at any point +// +// Revision 1.15 2002/07/28 19:21:28 Flayra +// - Balance changes after/during RC4a +// +// Revision 1.14 2002/07/23 17:18:47 Flayra +// - Level 1 armor toned down, power armor changes +// +// Revision 1.13 2002/07/01 21:43:56 Flayra +// - Added offensive upgrade back, when under the influence...of primal scream! +// +// Revision 1.12 2002/06/25 18:14:40 Flayra +// - Removed old offensive upgrades +// +// Revision 1.11 2002/06/03 16:55:49 Flayra +// - Toned down carapace and marine upgrades +// +// Revision 1.10 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHSpecials.h" +#include "util/Balance.h" +#include "common/damagetypes.h" + +const int kWeaponTracerDefault = 0; +const int kWeaponTracerLevelOne = 6; +const int kWeaponTracerLevelTwo = 4; +const int kWeaponTracerLevelThree = 2; + +float AvHPlayerUpgrade::GetAlienMeleeDamageUpgrade(int inUpgrade, bool inIncludeFocus) +{ + float theMultiplier = 1.0f; + + if(GetHasUpgrade(inUpgrade, MASK_BUFFED)) + { + theMultiplier = 1.0f + (float)BALANCE_VAR(kPrimalScreamDamageModifier); + } + + if(inIncludeFocus) + { + theMultiplier *= AvHPlayerUpgrade::GetFocusDamageUpgrade(inUpgrade); + } + + return theMultiplier; +} + +float AvHPlayerUpgrade::GetAlienRangedDamageUpgrade(int inUpgrade) +{ + float theMultiplier = 1.0f; + + if(GetHasUpgrade(inUpgrade, MASK_BUFFED)) + { + theMultiplier = 1.0f + (float)BALANCE_VAR(kPrimalScreamDamageModifier); + } + + return theMultiplier; +} + +float AvHPlayerUpgrade::GetFocusDamageUpgrade(int inUpgrade) +{ + float theFocusDamageUpgrade = 1.0f; + + int theFocusLevel = AvHGetAlienUpgradeLevel(inUpgrade, MASK_UPGRADE_8); + if(theFocusLevel > 0) + { + // Increase damage for each level of focus + const float theFocusDamageUpgradePercentPerLevel = (float)BALANCE_VAR(kFocusDamageUpgradePercentPerLevel); + theFocusDamageUpgrade += theFocusLevel*theFocusDamageUpgradePercentPerLevel; + } + + return theFocusDamageUpgrade; +} + +float AvHPlayerUpgrade::GetArmorValue(int inNumHives) +{ + // Each point of armor is work 1/x points of health + float theArmorBonus = BALANCE_VAR(kArmorValueNonAlien); + if(inNumHives == 3) + { + //float theArmorValueBase = 1.0f + (float)BALANCE_VAR(kArmorValueBase); + //float theArmorValuePerHive = (float)BALANCE_VAR(kArmorValuePerHive); + //inNumHives = min(inNumHives, kMaxHives); + + //theArmorBonus = (theArmorValueBase + inNumHives*theArmorValuePerHive); + theArmorBonus+=kArmorValuePerHive; + } + + // Smaller is better + theArmorBonus = 1.0f/theArmorBonus; + + return theArmorBonus; +} + +float AvHPlayerUpgrade::GetArmorAbsorption(AvHUser3 inUser3, int inUpgrade, int inNumHives) +{ + // A value of .2 means the armor Takes 80% of the damage, so the value gets smaller as it improves + float theAbsorption = (float)BALANCE_VAR(kArmorAbsorptionBase); + inNumHives = min(inNumHives, kMaxHives);//: prevent aliens taking negative damage if some mapper goon (or me :o) ) decides to put more than 3 hives on the map. + + // Heavy armor is the shiznit + if(inUser3 == AVH_USER3_MARINE_PLAYER && GetHasUpgrade(inUpgrade, MASK_UPGRADE_13)) + { + float theHeavyArmorAbsorbPercent = BALANCE_VAR(kHeavyArmorAbsorbPercent)/100.0f; + ASSERT(theHeavyArmorAbsorbPercent >= 0.0f); + ASSERT(theHeavyArmorAbsorbPercent <= 1.0f); + + theAbsorption = 1.0f - theHeavyArmorAbsorbPercent; + } + + // Increase absorption at higher hive-levels to make sure armor is always used before player dies + if(inNumHives==3) + { + switch(inUser3) + { + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: + case AVH_USER3_ALIEN_EMBRYO: + theAbsorption -= (float)BALANCE_VAR(kArmorAbsorptionPerExtraHive); + break; + } + } + + ASSERT(theAbsorption >= 0.0f); + ASSERT(theAbsorption <= 1.0f); + + return theAbsorption; +} + +int AvHPlayerUpgrade::GetMaxHealth(int inUpgrade, AvHUser3 inUser3, int inLevel) +{ + int theMaxHealth = 0; + int theHealthLevelIncrementPercent = BALANCE_VAR(kCombatLevelHealthIncrease); + + // TODO: Take into account upgrade if added + + switch(inUser3) + { + default: + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_COMMANDER_PLAYER: + theMaxHealth = BALANCE_VAR(kMarineHealth); + break; + + case AVH_USER3_ALIEN_PLAYER1: + theMaxHealth = BALANCE_VAR(kSkulkHealth); + break; + + case AVH_USER3_ALIEN_PLAYER2: + theMaxHealth = BALANCE_VAR(kGorgeHealth); + break; + + case AVH_USER3_ALIEN_PLAYER3: + theMaxHealth = BALANCE_VAR(kLerkHealth); + break; + + case AVH_USER3_ALIEN_PLAYER4: + theMaxHealth = BALANCE_VAR(kFadeHealth); + break; + + case AVH_USER3_ALIEN_PLAYER5: + theMaxHealth = BALANCE_VAR(kOnosHealth); + break; + + case AVH_USER3_ALIEN_EMBRYO: + theMaxHealth = BALANCE_VAR(kGestateHealth); + break; + } + + //return (1.0f + (inLevel - 1)*theHealthLevelIncrementPercent)*theMaxHealth; + return theMaxHealth + (inLevel - 1)*theHealthLevelIncrementPercent; +} + + +int AvHPlayerUpgrade::GetMaxArmorLevel(int inUpgrade, AvHUser3 inUser3) +{ + int theMaxArmorLevel = 0; + + int theArmorLevel = AvHPlayerUpgrade::GetArmorUpgrade(inUser3, inUpgrade); + //bool theHasAlienCarapace = GetHasUpgrade(inUpgrade, MASK_UPGRADE_1); + bool theHasPowerArmor = GetHasUpgrade(inUpgrade, MASK_UPGRADE_10); + bool theHasHeavyArmor = GetHasUpgrade(inUpgrade, MASK_UPGRADE_13); + + // Upgrade TODO: Take different levels into account? + + switch(inUser3) + { + default: + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_COMMANDER_PLAYER: + //theMaxArmorLevel = 100 + theArmorLevel*20; + theMaxArmorLevel = BALANCE_VAR(kMarineBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kMarineArmorUpgrade)); + if(theHasHeavyArmor) + { + theMaxArmorLevel = BALANCE_VAR(kMarineBaseHeavyArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kMarineHeavyArmorUpgrade)); + } + //if(theHasPowerArmor) + //{ + // theMaxArmorLevel *= 2; + //} + break; + + case AVH_USER3_ALIEN_PLAYER1: + theMaxArmorLevel = BALANCE_VAR(kSkulkBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kSkulkArmorUpgrade));//(theHasAlienCarapace ? 30 : 10); + break; + + case AVH_USER3_ALIEN_PLAYER2: + theMaxArmorLevel = BALANCE_VAR(kGorgeBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kGorgeArmorUpgrade));//(theHasAlienCarapace ? 75 : 50); + break; + + case AVH_USER3_ALIEN_PLAYER3: + theMaxArmorLevel = BALANCE_VAR(kLerkBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kLerkArmorUpgrade));//(theHasAlienCarapace ? 75 : 50); + break; + + case AVH_USER3_ALIEN_PLAYER4: + theMaxArmorLevel = BALANCE_VAR(kFadeBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kFadeArmorUpgrade));//(theHasAlienCarapace ? 150 : 125); + break; + + case AVH_USER3_ALIEN_PLAYER5: + theMaxArmorLevel = BALANCE_VAR(kOnosBaseArmor) + (int)((theArmorLevel/3.0f)*BALANCE_VAR(kOnosArmorUpgrade));//(theHasAlienCarapace ? 200 : 150); + break; + + case AVH_USER3_ALIEN_EMBRYO: + theMaxArmorLevel = BALANCE_VAR(kGestateBaseArmor); + break; + + } + + return theMaxArmorLevel; +} + +// Returns the level: 0, 1, 2 or 3 +int AvHPlayerUpgrade::GetArmorUpgrade(AvHUser3 inUser3, int inUpgrade, float* theArmorMultiplier) +{ + int theUpgradeLevel = 0; + + if(theArmorMultiplier) + *theArmorMultiplier = 1.0f; + + bool theIsMarine = false; + switch(inUser3) + { + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_COMMANDER_PLAYER: + theIsMarine = true; + break; + } + + if(theIsMarine) + { + if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_6)) + { + if(theArmorMultiplier) + *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kMarineArmorLevelThree); + theUpgradeLevel = 3; + } + else if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_5)) + { + if(theArmorMultiplier) + *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kMarineArmorLevelTwo); + + theUpgradeLevel = 2; + } + else if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_4)) + { + if(theArmorMultiplier) + *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kMarineArmorLevelOne); + + theUpgradeLevel = 1; + } + } + else + { + if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_1)) + { + if(theArmorMultiplier) + { + *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kAlienArmorLevelOne); + } + theUpgradeLevel = 1; + + if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_10)) + { + if(theArmorMultiplier) + { + *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kAlienArmorLevelTwo); + } + + theUpgradeLevel = 2; + } + if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_11)) + { + if(theArmorMultiplier) + { + *theArmorMultiplier = 1.0f + (float)BALANCE_VAR(kAlienArmorLevelThree); + } + + theUpgradeLevel = 3; + } + } + } + + return theUpgradeLevel; +} + +// Returns the level: 0, 1, 2 or 3 +int AvHPlayerUpgrade::GetWeaponUpgrade(int inUser3, int inUpgrade, float* outDamageMultiplier, int* outTracerFreq, float* outSpread) +{ + int theUpgradeLevel = 0; + + if(outDamageMultiplier) + *outDamageMultiplier = 1.0f; + + if(outTracerFreq) + *outTracerFreq = kWeaponTracerDefault; + + // Only marines and marine items can get damage upgrades + switch(inUser3) + { + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_TURRET: + case AVH_USER3_SIEGETURRET: + case AVH_USER3_MARINEITEM: + case AVH_USER3_MINE: + case AVH_USER3_NUKE: + // Apply extra damage for upgrade + if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_3)) + { + if(outDamageMultiplier) + *outDamageMultiplier *= (1.0f + (float)BALANCE_VAR(kWeaponDamageLevelThree)); + if(outTracerFreq) + *outTracerFreq = kWeaponTracerLevelThree; + + theUpgradeLevel = 3; + } + else if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_2)) + { + if(outDamageMultiplier) + *outDamageMultiplier *= (1.0f + (float)BALANCE_VAR(kWeaponDamageLevelTwo)); + if(outTracerFreq) + *outTracerFreq = kWeaponTracerLevelTwo; + theUpgradeLevel = 2; + } + else if(GetHasUpgrade(inUpgrade, MASK_UPGRADE_1)) + { + if(outDamageMultiplier) + *outDamageMultiplier *= (1.0f + (float)BALANCE_VAR(kWeaponDamageLevelOne)); + + if(outTracerFreq) + *outTracerFreq = kWeaponTracerLevelOne; + + theUpgradeLevel = 1; + } + break; + } + + return theUpgradeLevel; +} + +float AvHPlayerUpgrade::GetSilenceVolumeLevel(AvHUser3 inUser3, int inUpgrade) +{ + int theSilenceLevel = 0; + + switch(inUser3) + { + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_PLAYER4: + case AVH_USER3_ALIEN_PLAYER5: + case AVH_USER3_ALIEN_EMBRYO: + theSilenceLevel = AvHGetAlienUpgradeLevel(inUpgrade, MASK_UPGRADE_6); + break; + } + + //float theSilenceVolumeFactor = (1 - (theSilenceLevel/3.0f)); + + float theSilenceVolumeFactor = 1.0f; + switch(theSilenceLevel) + { + case 1: + theSilenceVolumeFactor = (float)BALANCE_VAR(kSilenceLevel1Volume); + break; + case 2: + theSilenceVolumeFactor = (float)BALANCE_VAR(kSilenceLevel2Volume); + break; + case 3: + theSilenceVolumeFactor = (float)BALANCE_VAR(kSilenceLevel3Volume); + break; + } + + return theSilenceVolumeFactor; +} + +float AvHPlayerUpgrade::CalculateDamageLessArmor(AvHUser3 inUser3, int inUser4, float flDamage, float& ioArmorValue, int bitsDamageType, int inNumHives) +{ + // if we're alien, if we have the armor upgrade, we take less damage off the top +// if(AvHGetIsAlien(inUser3)) +// { +// int theArmorUpgradeLevel = AvHGetAlienUpgradeLevel(inUser4, MASK_UPGRADE_1); +// if((theArmorUpgradeLevel > 0) && ((int)(ioArmorValue) > 0)) +// { +// float thePercentageOffTop = .1f*theArmorUpgradeLevel; +// flDamage -= flDamage*thePercentageOffTop; +// } +// } + + float flRatio = AvHPlayerUpgrade::GetArmorAbsorption(inUser3, inUser4, inNumHives); + float flBonus = AvHPlayerUpgrade::GetArmorValue(inNumHives); + + // Level 1 aliens don't take falling damage, ever + if((inUser3 == AVH_USER3_ALIEN_PLAYER1) && (bitsDamageType & DMG_FALL)) + { + flDamage = 0.0f; + } + + // Armor. + if (ioArmorValue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage! + { + float flNew = flDamage*flRatio; + float flArmor = (flDamage - flNew)*flBonus; + + // Does this use more armor than we have? + if (flArmor > ioArmorValue) + { + flArmor = ioArmorValue; + flArmor *= (1/flBonus); + flNew = flDamage - flArmor; + ioArmorValue = 0; + } + else + { + ioArmorValue -= flArmor; + if ( bitsDamageType & (NS_DMG_ACID) ) + { + ioArmorValue -= flArmor; + } + } + + flDamage = flNew; + } + + return flDamage; +} + +const int kCombatMinLevel = 1; +const int kCombatMaxLevel = 10; + +float AvHPlayerUpgrade::GetExperienceForLevel(int inLevel) +{ + float theExperienceForLevel = 0.0f; + int theLevel = 1; + + int theCombatBaseExperience = BALANCE_VAR(kCombatBaseExperience); + float theCombatLevelExperienceModifier = (float)BALANCE_VAR(kCombatLevelExperienceModifier); + + while((theLevel < inLevel) && (theCombatLevelExperienceModifier > 0)) + { + theExperienceForLevel += (1.0f + (theLevel-1)*theCombatLevelExperienceModifier)*theCombatBaseExperience; + theLevel++; + } + + return theExperienceForLevel; +} + +int AvHPlayerUpgrade::GetPlayerLevel(float inExperience) +{ + int thePlayerLevel = 1; + + int theCombatBaseExperience = BALANCE_VAR(kCombatBaseExperience); + float theCombatLevelExperienceModifier = (float)BALANCE_VAR(kCombatLevelExperienceModifier); + + while((inExperience > 0) && (theCombatLevelExperienceModifier > 0.0f)) + { + inExperience -= (1.0f + (thePlayerLevel - 1)*theCombatLevelExperienceModifier)*theCombatBaseExperience; + + if(inExperience > 0) + { + thePlayerLevel++; + } + } + + thePlayerLevel = max(min(thePlayerLevel, kCombatMaxLevel), kCombatMinLevel); + + return thePlayerLevel; +} + +float AvHPlayerUpgrade::GetPercentToNextLevel(float inExperience) +{ + int theCurrentLevel = AvHPlayerUpgrade::GetPlayerLevel(inExperience); + int theNextLevel = min(max(theCurrentLevel + 1, kCombatMinLevel), kCombatMaxLevel); + + float theExperienceForCurrentLevel = AvHPlayerUpgrade::GetExperienceForLevel(theCurrentLevel); + float theExperienceForNextLevel = AvHPlayerUpgrade::GetExperienceForLevel(theNextLevel); + + float thePercent = (inExperience - theExperienceForCurrentLevel)/(theExperienceForNextLevel - theExperienceForCurrentLevel); + + thePercent = min(max(0.0f, thePercent), 1.0f); + + return thePercent; } \ No newline at end of file diff --git a/main/source/mod/AvHResearchManager.cpp b/main/source/mod/AvHResearchManager.cpp index ffbe9e1..8ba950a 100644 --- a/main/source/mod/AvHResearchManager.cpp +++ b/main/source/mod/AvHResearchManager.cpp @@ -1,421 +1,421 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHResearchManager.cpp $ -// $Date: 2002/10/24 21:41:15 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHResearchManager.cpp,v $ -// Revision 1.11 2002/10/24 21:41:15 Flayra -// - Attempt to fix progress bars not showing up in release, or when playing online (can't quite figure out when). Never safe to compare floats anyways. -// -// Revision 1.10 2002/09/23 22:28:33 Flayra -// - Added GetIsResearching method, so automatic armory resupply could be added -// -// Revision 1.9 2002/08/02 21:50:37 Flayra -// - New research system -// -// Revision 1.8 2002/07/24 19:09:17 Flayra -// - Linux issues -// -// Revision 1.7 2002/07/24 18:45:42 Flayra -// - Linux and scripting changes -// -// Revision 1.6 2002/07/23 17:20:13 Flayra -// - Added hooks for research starting, for distress beacon sound effects -// -// Revision 1.5 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHResearchManager.h" - -#include "mod/AvHAlienWeapons.h" -#include "mod/AvHPlayer.h" - -#ifdef AVH_CLIENT -#include "cl_dll/eventscripts.h" -#include "cl_dll/in_defs.h" -#include "cl_dll/wrect.h" -#include "cl_dll/cl_dll.h" -#endif - -#include "common/hldm.h" -#include "common/event_api.h" -#include "common/event_args.h" -#include "common/vector_util.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHPlayerUpgrade.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHGamerules.h" - -AvHResearchNode::AvHResearchNode(AvHMessageID inMessageID, int inEntityIndex) -{ - this->mResearch = inMessageID; - this->mEntityIndex = inEntityIndex; - this->mTimeResearchDone = -1; - this->mTimeResearchStarted = -1; -} - -bool AvHResearchNode::GetCanEntityContinueResearch() const -{ - bool theCanContinueResearch = true; - - CBaseEntity* theResearchEntity = AvHSUGetEntityFromIndex(this->mEntityIndex); - if(!theResearchEntity || !theResearchEntity->IsAlive()) - { - theCanContinueResearch = false; - } - - return theCanContinueResearch; -} - -int AvHResearchNode::GetEntityIndex() const -{ - return this->mEntityIndex; -} - -AvHMessageID AvHResearchNode::GetResearching() const -{ - return this->mResearch; -} - -float AvHResearchNode::GetTimeResearchDone() const -{ - return this->mTimeResearchDone; -} - -float AvHResearchNode::GetTimeResearchStarted() const -{ - return this->mTimeResearchStarted; -} - -bool AvHResearchNode::UpdateResearch() -{ - bool theResearchDone = false; - - AvHMessageID theResearchingTech = this->GetResearching(); - if(theResearchingTech != MESSAGE_NULL) - { - CBaseEntity* theResearchEntity = AvHSUGetEntityFromIndex(this->mEntityIndex); - ASSERT(theResearchEntity); - - float theTimeResearchDone = this->GetTimeResearchDone(); - - // Set time during the first update - if(theTimeResearchDone < 0) - { - this->mTimeResearchStarted = gpGlobals->time; - theTimeResearchDone = this->mTimeResearchStarted + GetGameRules()->GetBuildTimeForMessageID(theResearchingTech); - this->mTimeResearchDone = theTimeResearchDone; - theResearchEntity->pev->iuser2 = (int)this->mResearch; - } - - if((gpGlobals->time >= theTimeResearchDone) || GetGameRules()->GetIsTesting()) - { - theResearchDone = true; - //AvHSHUSetBuildResearchState(theResearchEntity->pev->iuser3, theResearchEntity->pev->iuser4, theResearchEntity->pev->fuser1, false, 0.0f); - // theResearchEntity->pev->fuser1 = 0.0f; - // theResearchEntity->pev->iuser2 = 0; - } - else - { - float theNormalizedResearchFactor = (gpGlobals->time - this->mTimeResearchStarted)/(this->mTimeResearchDone - this->mTimeResearchStarted); - theNormalizedResearchFactor = min(max(theNormalizedResearchFactor, 0.0f), 1.0f); - - //theResearchEntity->pev->fuser1 = (kResearchFuser1Base + theNormalizedResearchFactor)*kNormalizationNetworkFactor; - AvHSHUSetBuildResearchState(theResearchEntity->pev->iuser3, theResearchEntity->pev->iuser4, theResearchEntity->pev->fuser1, false, theNormalizedResearchFactor); - } - } - - return theResearchDone; -} - - - - -// Research manager -void AvHResearchManager::AddTechNode(AvHMessageID inMessageID, AvHTechID inTechID, AvHTechID inPrereq1, AvHTechID inPrereq2, int inPointCost, int inBuildTime, bool inResearched, bool inAllowMultiples) -{ - AvHTechNode theTechNode(inMessageID, inTechID, inPrereq1, inPrereq2, inPointCost, inBuildTime, inResearched); - if(inAllowMultiples) - { - theTechNode.setAllowMultiples(); - } - - this->mTechNodes.InsertNode(&theTechNode); -} - -bool AvHResearchManager::GetResearchInfo(AvHMessageID inTech, bool& outIsResearchable, int& outCost, float& outTime) const -{ - bool theFoundIt = false; - - // Check each tech nodes - if(this->mTechNodes.GetResearchInfo(inTech, outIsResearchable, outCost, outTime)) - { - theFoundIt = true; - } - - return theFoundIt; -} - -const AvHTechTree& AvHResearchManager::GetTechNodes() const -{ - return this->mTechNodes; -} - -AvHTechTree& AvHResearchManager::GetTechNodes() -{ - return this->mTechNodes; -} - -void AvHResearchManager::TriggerAddTech(AvHTechID inTechID) -{ - this->mTechNodes.TriggerAddTech(inTechID); -} - -void AvHResearchManager::TriggerRemoveTech(AvHTechID inTechID) -{ - this->mTechNodes.TriggerRemoveTech(inTechID); -} - -void AvHResearchManager::SetTeamNumber(AvHTeamNumber inTeamNumber) -{ - this->mTeamNumber = inTeamNumber; -} - -bool AvHResearchManager::SetResearchDone(AvHMessageID inTech, int inEntityIndex) -{ - bool theFoundIt = false; - - if(this->mTechNodes.SetResearchDone(inTech)) - { - edict_t* theEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex); - ASSERT(!theEdict->free); - CBaseEntity* theEntity = CBaseEntity::Instance(theEdict); - ASSERT(theEntity); - - // Potentially inform all entities and team of upgrade - GetGameRules()->ProcessTeamUpgrade(inTech, this->mTeamNumber, inEntityIndex, true); - - // Hook research complete - AvHSUResearchComplete(theEntity, inTech); - - // No longer researching - //theEntity->pev->fuser1 = kResearchFuser1Base*kNormalizationNetworkFactor; - AvHSHUSetBuildResearchState(theEntity->pev->iuser3, theEntity->pev->iuser4, theEntity->pev->fuser1, true, 0.0f); - - // Tell entity that it's no longer researching - AvHBuildable* theBuildable = dynamic_cast(theEntity); - if(theBuildable) - { - theBuildable->SetResearching(false); - } - - // Send message indicating research is done - GetGameRules()->TriggerAlert(this->mTeamNumber, ALERT_RESEARCH_COMPLETE, inEntityIndex); - - theFoundIt = true; - } - - return theFoundIt; -} - -void AvHResearchManager::Reset() -{ - // Clear out everything - this->mResearchingTech.clear(); - this->mTechNodes.Clear(); -} - -bool AvHResearchManager::CancelResearch(int inEntityIndex, float& outResearchPercentage, AvHMessageID& outMessageID) -{ - bool theSuccess = false; - - // Search through researching tech and make sure we're researching this - for(ResearchListType::iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); theIter++) - { - if(theIter->GetEntityIndex() == inEntityIndex) - { - float theEndTime = theIter->GetTimeResearchDone(); - float theStartTime = theIter->GetTimeResearchStarted(); - outResearchPercentage = (gpGlobals->time - theStartTime)/(theEndTime - theStartTime); - - outMessageID = theIter->GetResearching(); - - this->mResearchingTech.erase(theIter); - - // Look up entity and reset research progress - CBaseEntity* theResearchEntity = AvHSUGetEntityFromIndex(inEntityIndex); - ASSERT(theResearchEntity); - //theResearchEntity->pev->fuser1 = kResearchFuser1Base*kNormalizationNetworkFactor; - AvHSHUSetBuildResearchState(theResearchEntity->pev->iuser3, theResearchEntity->pev->iuser4, theResearchEntity->pev->fuser1, false, 0.0f); - - AvHBuildable* theBuildable = dynamic_cast(theResearchEntity); - if(theBuildable) - { - theBuildable->SetResearching(false); - } - - theSuccess = true; - break; - } - } - - return theSuccess; -} - -bool AvHResearchManager::SetResearching(AvHMessageID inMessageID, int inEntityIndex) -{ - bool theCouldStart = true; - - // Search through researching tech and make sure this entity isn't already researching something - for(ResearchListType::iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); theIter++) - { - if(theIter->GetEntityIndex() == inEntityIndex) - { - theCouldStart = false; - break; - } - } - - if(theCouldStart) - { - bool theIsResearchable = false; - int theCost; - float theTime; - // joev: 0000199 - // Look up entity and check that research is valid for this entity. - CBaseEntity* theResearchEntity = AvHSUGetEntityFromIndex(inEntityIndex); - ASSERT(theResearchEntity); - - if (!AvHSUGetIsResearchApplicable(theResearchEntity,inMessageID)) { - theCouldStart = false; - } - // :joev - - if(!this->GetResearchInfo(inMessageID, theIsResearchable, theCost, theTime) || !theIsResearchable) - { - theCouldStart = false; - } - } - - if(theCouldStart) - { - this->mResearchingTech.push_back(AvHResearchNode(inMessageID, inEntityIndex)); - - // Tell entity that it's researching - edict_t* theEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex); - ASSERT(!theEdict->free); - CBaseEntity* theEntity = CBaseEntity::Instance(theEdict); - ASSERT(theEntity); - - AvHBuildable* theBuildable = dynamic_cast(theEntity); - if(theBuildable) - { - theBuildable->SetResearching(true); - AvHSUResearchStarted(theEntity, inMessageID); - } - } - - return theCouldStart; -} - -bool AvHResearchManager::GetIsMessageAvailable(AvHMessageID& inMessageID) const -{ - bool theIsAvailable = false; - - if(this->mTechNodes.GetIsMessageAvailable(inMessageID)) - { - // Make sure it's not a one time research that we're currently researching - if(this->GetIsResearchingTech(inMessageID) && !this->mTechNodes.GetAllowMultiples(inMessageID)) - { - theIsAvailable = false; - } - else - { - theIsAvailable = true; - } - } - - return theIsAvailable; -} - -TechNodeMap AvHResearchManager::GetResearchNodesDependentOn(AvHTechID inTechID) const -{ - TechNodeMap theTechNodes; - - this->mTechNodes.GetResearchNodesDependentOn(inTechID, theTechNodes); - - return theTechNodes; -} - -bool AvHResearchManager::GetIsResearchingTech(AvHMessageID inMessageID) const -{ - bool theIsResearchingTech = false; - - for(ResearchListType::const_iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); theIter++) - { - if(theIter->GetResearching() == inMessageID) - { - theIsResearchingTech = true; - break; - } - } - - return theIsResearchingTech; -} - - -bool AvHResearchManager::GetIsResearching(int inEntityIndex) const -{ - bool theIsResearching = false; - - // Run through every item in the list and update research, marking any done - for(ResearchListType::const_iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); theIter++) - { - if(theIter->GetEntityIndex() == inEntityIndex) - { - theIsResearching = true; - break; - } - } - - return theIsResearching; -} - -void AvHResearchManager::UpdateResearch() -{ - // Run through every item in the list and update research, marking any done - for(ResearchListType::iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); ) - { - if(theIter->GetCanEntityContinueResearch()) - { - bool theHighTechCheat = GetGameRules()->GetIsCheatEnabled(kcHighTech); - if(theIter->UpdateResearch() || theHighTechCheat) - { - AvHMessageID theResearchingTech = theIter->GetResearching(); - int theEntityIndex = theIter->GetEntityIndex(); - - this->SetResearchDone(theResearchingTech, theEntityIndex); - - theIter = this->mResearchingTech.erase(theIter); - } - else - { - theIter++; - } - } - else - { - theIter = this->mResearchingTech.erase(theIter); - } - } -} +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHResearchManager.cpp $ +// $Date: 2002/10/24 21:41:15 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHResearchManager.cpp,v $ +// Revision 1.11 2002/10/24 21:41:15 Flayra +// - Attempt to fix progress bars not showing up in release, or when playing online (can't quite figure out when). Never safe to compare floats anyways. +// +// Revision 1.10 2002/09/23 22:28:33 Flayra +// - Added GetIsResearching method, so automatic armory resupply could be added +// +// Revision 1.9 2002/08/02 21:50:37 Flayra +// - New research system +// +// Revision 1.8 2002/07/24 19:09:17 Flayra +// - Linux issues +// +// Revision 1.7 2002/07/24 18:45:42 Flayra +// - Linux and scripting changes +// +// Revision 1.6 2002/07/23 17:20:13 Flayra +// - Added hooks for research starting, for distress beacon sound effects +// +// Revision 1.5 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHResearchManager.h" + +#include "mod/AvHAlienWeapons.h" +#include "mod/AvHPlayer.h" + +#ifdef AVH_CLIENT +#include "cl_dll/eventscripts.h" +#include "cl_dll/in_defs.h" +#include "cl_dll/wrect.h" +#include "cl_dll/cl_dll.h" +#endif + +#include "common/hldm.h" +#include "common/event_api.h" +#include "common/event_args.h" +#include "common/vector_util.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHGamerules.h" + +AvHResearchNode::AvHResearchNode(AvHMessageID inMessageID, int inEntityIndex) +{ + this->mResearch = inMessageID; + this->mEntityIndex = inEntityIndex; + this->mTimeResearchDone = -1; + this->mTimeResearchStarted = -1; +} + +bool AvHResearchNode::GetCanEntityContinueResearch() const +{ + bool theCanContinueResearch = true; + + CBaseEntity* theResearchEntity = AvHSUGetEntityFromIndex(this->mEntityIndex); + if(!theResearchEntity || !theResearchEntity->IsAlive()) + { + theCanContinueResearch = false; + } + + return theCanContinueResearch; +} + +int AvHResearchNode::GetEntityIndex() const +{ + return this->mEntityIndex; +} + +AvHMessageID AvHResearchNode::GetResearching() const +{ + return this->mResearch; +} + +float AvHResearchNode::GetTimeResearchDone() const +{ + return this->mTimeResearchDone; +} + +float AvHResearchNode::GetTimeResearchStarted() const +{ + return this->mTimeResearchStarted; +} + +bool AvHResearchNode::UpdateResearch() +{ + bool theResearchDone = false; + + AvHMessageID theResearchingTech = this->GetResearching(); + if(theResearchingTech != MESSAGE_NULL) + { + CBaseEntity* theResearchEntity = AvHSUGetEntityFromIndex(this->mEntityIndex); + ASSERT(theResearchEntity); + + float theTimeResearchDone = this->GetTimeResearchDone(); + + // Set time during the first update + if(theTimeResearchDone < 0) + { + this->mTimeResearchStarted = gpGlobals->time; + theTimeResearchDone = this->mTimeResearchStarted + GetGameRules()->GetBuildTimeForMessageID(theResearchingTech); + this->mTimeResearchDone = theTimeResearchDone; + theResearchEntity->pev->iuser2 = (int)this->mResearch; + } + + if((gpGlobals->time >= theTimeResearchDone) || GetGameRules()->GetIsTesting()) + { + theResearchDone = true; + //AvHSHUSetBuildResearchState(theResearchEntity->pev->iuser3, theResearchEntity->pev->iuser4, theResearchEntity->pev->fuser1, false, 0.0f); + // theResearchEntity->pev->fuser1 = 0.0f; + // theResearchEntity->pev->iuser2 = 0; + } + else + { + float theNormalizedResearchFactor = (gpGlobals->time - this->mTimeResearchStarted)/(this->mTimeResearchDone - this->mTimeResearchStarted); + theNormalizedResearchFactor = min(max(theNormalizedResearchFactor, 0.0f), 1.0f); + + //theResearchEntity->pev->fuser1 = (kResearchFuser1Base + theNormalizedResearchFactor)*kNormalizationNetworkFactor; + AvHSHUSetBuildResearchState(theResearchEntity->pev->iuser3, theResearchEntity->pev->iuser4, theResearchEntity->pev->fuser1, false, theNormalizedResearchFactor); + } + } + + return theResearchDone; +} + + + + +// Research manager +void AvHResearchManager::AddTechNode(AvHMessageID inMessageID, AvHTechID inTechID, AvHTechID inPrereq1, AvHTechID inPrereq2, int inPointCost, int inBuildTime, bool inResearched, bool inAllowMultiples) +{ + AvHTechNode theTechNode(inMessageID, inTechID, inPrereq1, inPrereq2, inPointCost, inBuildTime, inResearched); + if(inAllowMultiples) + { + theTechNode.setAllowMultiples(); + } + + this->mTechNodes.InsertNode(&theTechNode); +} + +bool AvHResearchManager::GetResearchInfo(AvHMessageID inTech, bool& outIsResearchable, int& outCost, float& outTime) const +{ + bool theFoundIt = false; + + // Check each tech nodes + if(this->mTechNodes.GetResearchInfo(inTech, outIsResearchable, outCost, outTime)) + { + theFoundIt = true; + } + + return theFoundIt; +} + +const AvHTechTree& AvHResearchManager::GetTechNodes() const +{ + return this->mTechNodes; +} + +AvHTechTree& AvHResearchManager::GetTechNodes() +{ + return this->mTechNodes; +} + +void AvHResearchManager::TriggerAddTech(AvHTechID inTechID) +{ + this->mTechNodes.TriggerAddTech(inTechID); +} + +void AvHResearchManager::TriggerRemoveTech(AvHTechID inTechID) +{ + this->mTechNodes.TriggerRemoveTech(inTechID); +} + +void AvHResearchManager::SetTeamNumber(AvHTeamNumber inTeamNumber) +{ + this->mTeamNumber = inTeamNumber; +} + +bool AvHResearchManager::SetResearchDone(AvHMessageID inTech, int inEntityIndex) +{ + bool theFoundIt = false; + + if(this->mTechNodes.SetResearchDone(inTech)) + { + edict_t* theEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex); + ASSERT(!theEdict->free); + CBaseEntity* theEntity = CBaseEntity::Instance(theEdict); + ASSERT(theEntity); + + // Potentially inform all entities and team of upgrade + GetGameRules()->ProcessTeamUpgrade(inTech, this->mTeamNumber, inEntityIndex, true); + + // Hook research complete + AvHSUResearchComplete(theEntity, inTech); + + // No longer researching + //theEntity->pev->fuser1 = kResearchFuser1Base*kNormalizationNetworkFactor; + AvHSHUSetBuildResearchState(theEntity->pev->iuser3, theEntity->pev->iuser4, theEntity->pev->fuser1, true, 0.0f); + + // Tell entity that it's no longer researching + AvHBuildable* theBuildable = dynamic_cast(theEntity); + if(theBuildable) + { + theBuildable->SetResearching(false); + } + + // Send message indicating research is done + GetGameRules()->TriggerAlert(this->mTeamNumber, ALERT_RESEARCH_COMPLETE, inEntityIndex); + + theFoundIt = true; + } + + return theFoundIt; +} + +void AvHResearchManager::Reset() +{ + // Clear out everything + this->mResearchingTech.clear(); + this->mTechNodes.Clear(); +} + +bool AvHResearchManager::CancelResearch(int inEntityIndex, float& outResearchPercentage, AvHMessageID& outMessageID) +{ + bool theSuccess = false; + + // Search through researching tech and make sure we're researching this + for(ResearchListType::iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); theIter++) + { + if(theIter->GetEntityIndex() == inEntityIndex) + { + float theEndTime = theIter->GetTimeResearchDone(); + float theStartTime = theIter->GetTimeResearchStarted(); + outResearchPercentage = (gpGlobals->time - theStartTime)/(theEndTime - theStartTime); + + outMessageID = theIter->GetResearching(); + + this->mResearchingTech.erase(theIter); + + // Look up entity and reset research progress + CBaseEntity* theResearchEntity = AvHSUGetEntityFromIndex(inEntityIndex); + ASSERT(theResearchEntity); + //theResearchEntity->pev->fuser1 = kResearchFuser1Base*kNormalizationNetworkFactor; + AvHSHUSetBuildResearchState(theResearchEntity->pev->iuser3, theResearchEntity->pev->iuser4, theResearchEntity->pev->fuser1, false, 0.0f); + + AvHBuildable* theBuildable = dynamic_cast(theResearchEntity); + if(theBuildable) + { + theBuildable->SetResearching(false); + } + + theSuccess = true; + break; + } + } + + return theSuccess; +} + +bool AvHResearchManager::SetResearching(AvHMessageID inMessageID, int inEntityIndex) +{ + bool theCouldStart = true; + + // Search through researching tech and make sure this entity isn't already researching something + for(ResearchListType::iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); theIter++) + { + if(theIter->GetEntityIndex() == inEntityIndex) + { + theCouldStart = false; + break; + } + } + + if(theCouldStart) + { + bool theIsResearchable = false; + int theCost; + float theTime; + // : 0000199 + // Look up entity and check that research is valid for this entity. + CBaseEntity* theResearchEntity = AvHSUGetEntityFromIndex(inEntityIndex); + ASSERT(theResearchEntity); + + if (!AvHSUGetIsResearchApplicable(theResearchEntity,inMessageID)) { + theCouldStart = false; + } + // : + + if(!this->GetResearchInfo(inMessageID, theIsResearchable, theCost, theTime) || !theIsResearchable) + { + theCouldStart = false; + } + } + + if(theCouldStart) + { + this->mResearchingTech.push_back(AvHResearchNode(inMessageID, inEntityIndex)); + + // Tell entity that it's researching + edict_t* theEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex); + ASSERT(!theEdict->free); + CBaseEntity* theEntity = CBaseEntity::Instance(theEdict); + ASSERT(theEntity); + + AvHBuildable* theBuildable = dynamic_cast(theEntity); + if(theBuildable) + { + theBuildable->SetResearching(true); + AvHSUResearchStarted(theEntity, inMessageID); + } + } + + return theCouldStart; +} + +bool AvHResearchManager::GetIsMessageAvailable(AvHMessageID& inMessageID) const +{ + bool theIsAvailable = false; + + if(this->mTechNodes.GetIsMessageAvailable(inMessageID)) + { + // Make sure it's not a one time research that we're currently researching + if(this->GetIsResearchingTech(inMessageID) && !this->mTechNodes.GetAllowMultiples(inMessageID)) + { + theIsAvailable = false; + } + else + { + theIsAvailable = true; + } + } + + return theIsAvailable; +} + +TechNodeMap AvHResearchManager::GetResearchNodesDependentOn(AvHTechID inTechID) const +{ + TechNodeMap theTechNodes; + + this->mTechNodes.GetResearchNodesDependentOn(inTechID, theTechNodes); + + return theTechNodes; +} + +bool AvHResearchManager::GetIsResearchingTech(AvHMessageID inMessageID) const +{ + bool theIsResearchingTech = false; + + for(ResearchListType::const_iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); theIter++) + { + if(theIter->GetResearching() == inMessageID) + { + theIsResearchingTech = true; + break; + } + } + + return theIsResearchingTech; +} + + +bool AvHResearchManager::GetIsResearching(int inEntityIndex) const +{ + bool theIsResearching = false; + + // Run through every item in the list and update research, marking any done + for(ResearchListType::const_iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); theIter++) + { + if(theIter->GetEntityIndex() == inEntityIndex) + { + theIsResearching = true; + break; + } + } + + return theIsResearching; +} + +void AvHResearchManager::UpdateResearch() +{ + // Run through every item in the list and update research, marking any done + for(ResearchListType::iterator theIter = this->mResearchingTech.begin(); theIter != this->mResearchingTech.end(); ) + { + if(theIter->GetCanEntityContinueResearch()) + { + bool theHighTechCheat = GetGameRules()->GetIsCheatEnabled(kcHighTech); + if(theIter->UpdateResearch() || theHighTechCheat) + { + AvHMessageID theResearchingTech = theIter->GetResearching(); + int theEntityIndex = theIter->GetEntityIndex(); + + this->SetResearchDone(theResearchingTech, theEntityIndex); + + theIter = this->mResearchingTech.erase(theIter); + } + else + { + theIter++; + } + } + else + { + theIter = this->mResearchingTech.erase(theIter); + } + } +} diff --git a/main/source/mod/AvHScrollHandler.cpp b/main/source/mod/AvHScrollHandler.cpp index b107e9a..3923de8 100644 --- a/main/source/mod/AvHScrollHandler.cpp +++ b/main/source/mod/AvHScrollHandler.cpp @@ -7,6 +7,7 @@ #include "mod/AvHActionButtons.h" #include "ui/UIUtil.h" + int AvHScrollHandler::sScrollX = 0; int AvHScrollHandler::sScrollY = 0; int AvHScrollHandler::sScrollZ = 0; @@ -18,9 +19,11 @@ int AvHScrollHandler::sLastMouseUpX = 0; int AvHScrollHandler::sLastMouseUpY = 0; bool AvHScrollHandler::sMouseOneDown = false; bool AvHScrollHandler::sMouseTwoDown = false; +int AvHScrollHandler::sKeyDown = 0; AvHScrollHandler::AvHScrollHandler() { + sKeyDown = 0; } bool AvHScrollHandler::GetMouseOneDown() const @@ -63,6 +66,63 @@ void AvHScrollHandler::ClearScrollHeight() sScrollZ = 0; } +void AvHScrollHandler::KeyScrollLeft() +{ + if ( sKeyDown < 0 ) sKeyDown=0; + sKeyDown++; + ScrollLeft(); +} + +void AvHScrollHandler::KeyScrollRight() +{ + if ( sKeyDown < 0 ) sKeyDown=0; + sKeyDown++; + ScrollRight(); +} + +void AvHScrollHandler::KeyScrollUp() +{ + if ( sKeyDown < 0 ) sKeyDown=0; + sKeyDown++; + ScrollUp(); +} + +void AvHScrollHandler::KeyScrollDown() +{ + if ( sKeyDown < 0 ) sKeyDown=0; + sKeyDown++; + ScrollDown(); +} + +void AvHScrollHandler::KeyScrollUpStop() +{ + sKeyDown--; + if ( sKeyDown < 0 ) sKeyDown=0; + sScrollY=0; +} + +void AvHScrollHandler::KeyScrollDownStop() +{ + sKeyDown--; + if ( sKeyDown < 0 ) sKeyDown=0; + sScrollY=0; +} + +void AvHScrollHandler::KeyScrollLeftStop() +{ + sKeyDown--; + if ( sKeyDown < 0 ) sKeyDown=0; + sScrollX=0; +} + +void AvHScrollHandler::KeyScrollRightStop() +{ + sKeyDown--; + if ( sKeyDown < 0 ) sKeyDown=0; + sScrollX=0; +} + + void AvHScrollHandler::ScrollLeft() { sScrollX = -1; @@ -102,6 +162,9 @@ void AvHScrollHandler::StopScroll() void AvHScrollHandler::cursorMoved(int x, int y, Panel* inPanel) { + if ( sKeyDown > 0 ) + return; + char theMessage[256]; int theRandNumber = rand() % 10; sprintf(theMessage, "Cursor moved, %d, %d, rand = %d", x, y, theRandNumber); diff --git a/main/source/mod/AvHScrollHandler.h b/main/source/mod/AvHScrollHandler.h index 08555a0..21fb128 100644 --- a/main/source/mod/AvHScrollHandler.h +++ b/main/source/mod/AvHScrollHandler.h @@ -19,6 +19,14 @@ public: bool GetMouseTwoDown() const; static void ClearScrollHeight(); + static void KeyScrollLeft(); + static void KeyScrollRight(); + static void KeyScrollUp(); + static void KeyScrollDown(); + static void KeyScrollLeftStop(); + static void KeyScrollRightStop(); + static void KeyScrollUpStop(); + static void KeyScrollDownStop(); static void ScrollLeft(); static void ScrollRight(); static void ScrollUp(); @@ -40,6 +48,7 @@ public: virtual void keyFocusTicked(Panel* panel) {} private: + static int sScrollX; static int sScrollY; static int sScrollZ; @@ -51,6 +60,7 @@ private: static int sLastMouseUpY; static bool sMouseOneDown; static bool sMouseTwoDown; + static int sKeyDown; }; #endif diff --git a/main/source/mod/AvHServerUtil.cpp b/main/source/mod/AvHServerUtil.cpp index 37a4dc0..35bbe09 100644 --- a/main/source/mod/AvHServerUtil.cpp +++ b/main/source/mod/AvHServerUtil.cpp @@ -1,1700 +1,1886 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHServerUtil.cpp $ -// $Date: 2002/11/22 21:25:46 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHServerUtil.cpp,v $ -// Revision 1.42 2002/11/22 21:25:46 Flayra -// - Adminmod fixes -// -// Revision 1.41 2002/11/15 04:42:13 Flayra -// - Moved utility function into here from client.cpp -// -// Revision 1.40 2002/11/12 22:39:25 Flayra -// - Logging changes for Psychostats compatibility -// -// Revision 1.39 2002/11/12 02:28:53 Flayra -// - Don't reset adminmod_ entities -// -// Revision 1.38 2002/11/03 04:51:55 Flayra -// - Refactoring for AddToFullPack changes -// -// Revision 1.37 2002/10/24 21:42:12 Flayra -// - Utility function for telefragging -// - Hive technology fixes -// -// Revision 1.36 2002/10/20 21:11:34 Flayra -// - Optimizations -// -// Revision 1.35 2002/10/19 21:09:56 Flayra -// - Debugging info for linux -// -// Revision 1.34 2002/10/19 20:58:31 Flayra -// - Debugging info for linux -// -// Revision 1.33 2002/10/18 22:22:19 Flayra -// - Sensory chamber triggers vocal alert -// -// Revision 1.32 2002/10/16 01:06:33 Flayra -// - Added generic particle event -// - Visibility tweak: enemies aren't "detected" unless they're moving. This means the commander can only see nearby moving blips, and marines with motion-tracking won't see still aliens that are detected because they're nearby. This also means webs won't show up when nearby. -// - Distress beacon event -// -// Revision 1.31 2002/10/07 17:49:23 Flayra -// - Umbra balance change -// -// Revision 1.30 2002/10/03 19:06:50 Flayra -// - Profiling info for Linux build -// -// Revision 1.29 2002/09/23 22:30:05 Flayra -// - Commander dropped weapons live forever -// - Observatory and sensory chambers check range in 2D for commander regions -// - Added turret factory upgrading (for siege) -// -// Revision 1.28 2002/09/09 20:05:59 Flayra -// - Sensory chambers now detect enemies in range -// -// Revision 1.27 2002/08/31 18:01:03 Flayra -// - Work at VALVe -// -// Revision 1.26 2002/08/16 02:44:11 Flayra -// - New damage types -// -// Revision 1.25 2002/08/09 00:52:14 Flayra -// - Removed old #ifdef -// -// Revision 1.24 2002/08/02 21:50:24 Flayra -// - Removed hives-are-visible code, I think it's safe again -// -// Revision 1.23 2002/07/25 16:58:00 flayra -// - Linux changes -// -// Revision 1.22 2002/07/23 17:24:27 Flayra -// - Added random building angles for diversity, added research started hooks for distress beacon effects, added versatile alien tech tree, hives are always visible, observatories detect nearby aliens, umbra blocks most but not all bullets -// -// Revision 1.21 2002/07/08 17:16:43 Flayra -// - Tried to cut down on sound list spam by defaulting to CHAN_BODY, added debugging code for tracking down solidity issues -// -// Revision 1.20 2002/07/01 21:44:58 Flayra -// - Added primal scream and umbra support -// -// Revision 1.19 2002/06/25 18:16:56 Flayra -// - Quieted construction effects (normalized now), temporarily removed sensory chamber sight, added upgrading of armory, wrapped bullet tracing for umbra -// -// Revision 1.18 2002/06/03 16:57:44 Flayra -// - Toned down carapace and marine upgrades, removed redundant hive class name, all buildables are subject to visibility rules -// -// Revision 1.17 2002/05/28 18:13:50 Flayra -// - Sensory chambers contribute to hive sight -// -// Revision 1.16 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "util/nowarnings.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHPlayer.h" -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "dlls/cbase.h" -#include "mod/AvHPlayerUpgrade.h" -#include "common/damagetypes.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHSharedUtil.h" -#include "util/MathUtil.h" -#include "engine/studio.h" -#include "mod/AvHSoundListManager.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "mod/AvHAlienEquipment.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHParticleTemplateServer.h" -#include "mod/AvHAlienWeapons.h" -#include "common/com_model.h" -#include "mod/AvHHulls.h" -#include "mod/AnimationUtil.h" - -int NS_PointContents(const hull_t *hull, int num, float p[3]); -float NS_TraceLineAgainstEntity(int inEntityIndex, float inTime, const float inRayOrigin[3], const float inRayDirection[3]); - -extern const float AVH_INFINITY; - -extern playermove_t* pmove; - -#ifdef WIN32 -#include "mmsystem.h" -#endif - -extern int gTeleportEventID; -extern int gParticleEventID; -extern int gNumericalInfoEventID; -extern int gDistressBeaconEventID; -extern int gUmbraCloudEventID; -extern AvHParticleTemplateListServer gParticleTemplateList; - -extern int gPhaseInEventID; -extern AvHSoundListManager gSoundListManager; - -unsigned int AvHSUTimeGetTime() -{ - unsigned int theTime = 0; - -#ifdef WIN32 - theTime = timeGetTime(); -#else - FILE* theFilePointer; - double theIdleTime; - double theUpTime; - - /* Read the system uptime and accumulated idle time from /proc/uptime. We're disregarding theIdleTime */ - theFilePointer = fopen ("/proc/uptime", "r"); - - if(fscanf(theFilePointer, "%lf %lf\n", &theUpTime, &theIdleTime) == 2) - { - /* uptime is in seconds... we want milliseconds */ - theTime = (unsigned int)(theUpTime*1000); - } - - fclose (theFilePointer); - #endif - - return theTime; -} - -int AvHSUCalcCombatSpawnWaveSize(int inNumPlayersOnTeam, int inNumDeadPlayers) -{ - int theSpawnWaveSize = min(inNumDeadPlayers, BALANCE_VAR(kCombatMaxPlayersPerWave)); - return theSpawnWaveSize; -} - -float AvHSUCalcCombatSpawnTime(AvHClassType inClassType, int inNumPlayersOnTeam, int inNumDeadPlayers, int inPlayersSpentLevel) -{ - int theWaveSize = AvHSUCalcCombatSpawnWaveSize(inNumPlayersOnTeam, inNumDeadPlayers); - float theSpawnTime = BALANCE_VAR(kCombatBaseRespawnTime) + max((theWaveSize-1), 0)*BALANCE_VAR(kCombatAdditiveRespawnTime); - return theSpawnTime; -} - -char* AvHSUGetGameVersionString() -{ - static char theGameVersion[1024]; - - string theGameVersionString; - - theGameVersionString = "v" + MakeStringFromInt(BALANCE_VAR(kGameVersionMajor)) + "." + MakeStringFromInt(BALANCE_VAR(kGameVersionMinor)) + "." + MakeStringFromInt(BALANCE_VAR(kGameVersionRevision)); - - //memset(theGameVersion, 0, 1024); - strcpy(theGameVersion, theGameVersionString.c_str()); - - return theGameVersion; -} - -bool AvHSUGetIsRelevantForTopDownPlayer(const vec3_t& inTopDownPosition, const vec3_t& inEntityPosition, float inScalar) -{ - bool theIsRelevant = false; - - //if(inEntityPosition.z <= inTopDownPosition.z) - //{ - float theXDist = fabs(inTopDownPosition.x - inEntityPosition.x); - float theYDist = fabs(inTopDownPosition.y - inEntityPosition.y); - float theXYDistance = sqrt(theXDist*theXDist + theYDist*theYDist); - - float theCullDistance = GetGameRules()->GetMapExtents().GetTopDownCullDistance()*inScalar; - if(theXYDistance <= theCullDistance) - { - theIsRelevant = true; - } - //} - - return theIsRelevant; -} - -Vector AvHSUGetRandomBuildingAngles() -{ - int theX = 0;//g_engfuncs.pfnRandomLong(0, 360); - int theY = g_engfuncs.pfnRandomLong(0, 360); - int theZ = 0; - - Vector theRandomAngles(theX, theY, theZ); - - return theRandomAngles; -} - -const char* AvHSUGetTeamName(int inTeamNumber) -{ - const char* theTeamName = "none"; - - const AvHTeam* theTeamPointer = GetGameRules()->GetTeam(AvHTeamNumber(inTeamNumber)); - if(theTeamPointer) - { - theTeamName = theTeamPointer->GetTeamName(); - } - - return theTeamName; -} - -#ifdef USE_OLDAUTH -// Steam IDs -const char* kSteamIDPending = "STEAM_ID_PENDING"; -const char* kSteamIDLocal = "STEAM_ID_LOOPBACK"; -const char* kSteamIDBot = "BOT"; -const char* kSteamIDInvalidID = "-1"; -const char* kSteamIDDefault = "STEAM_0:0:0"; -const char* kSteamIDPrefix = "STEAM_"; -bool AvHSUGetIsValidAuthID(const string& inAuthID) -{ - bool theIsValid = true; - - // "0" is WONid that hasn't been entered - if((inAuthID == "") || (inAuthID == " ") || (inAuthID == "0") || (inAuthID == kSteamIDDefault) || (inAuthID == kSteamIDInvalidID) || (inAuthID == kSteamIDBot) || (inAuthID == kSteamIDLocal)) - { - theIsValid = false; - } - - return theIsValid; -} -// Function that is backwards-compatible with WON ids -string AvHSUGetPlayerAuthIDString(edict_t* inPlayer) -{ - string thePlayerAuthID; - - // Try to get SteamID - const char* theSteamID = g_engfuncs.pfnGetPlayerAuthId(inPlayer); - if(strcmp(theSteamID, kSteamIDInvalidID)) - { - thePlayerAuthID = theSteamID; - } - // If that fails, get WonID and put it into a string - else - { - int theWonID = g_engfuncs.pfnGetPlayerWONId(inPlayer); - thePlayerAuthID = MakeStringFromInt(theWonID); - } - - return thePlayerAuthID; -} -#endif - -void AvHSUKillPlayersTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor) -{ - // If new player is stuck inside another player, kill old player - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if((theEntity != inPlayer) && (theEntity->GetIsRelevant())) - { - // tankefugl: 0000892 -- fixed to allow spawnkilling of crouching players on IP - float theDistanceToPlayer = VectorDistance(inPlayer->pev->origin, theEntity->pev->origin); - float zDistance = inPlayer->pev->origin[2] - theEntity->pev->origin[2]; - if(theDistanceToPlayer < 30 || (theDistanceToPlayer < 40 && zDistance > 0 && zDistance < 40)) - { - theEntity->TakeDamage(inInflictor, theEntity->pev, 10000, DMG_GENERIC); - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) -} - -void AvHSUBuildingJustCreated(AvHMessageID inBuildID, CBaseEntity* theBuilding, AvHPlayer* inPlayer) -{ - if((inBuildID == BUILD_RESOURCES) || (inBuildID == ALIEN_BUILD_RESOURCES)) - { - // Add it to team for performance reasons (so world doesn't have to be polled during AvHTeam::UpdateResources) - AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)theBuilding->pev->team); - ASSERT(theTeam); - theTeam->AddResourceTower(theBuilding->entindex()); - } - - // Don't expire weapons the commander drops - AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(theBuilding); - if(theBaseWeapon && (inPlayer->GetUser3() == AVH_USER3_COMMANDER_PLAYER)) - { - theBaseWeapon->SetGroundLifetime(-1); - } - - AvHBuildable* theBuildable = dynamic_cast(theBuilding); - if(theBuildable && inPlayer) - { - theBuildable->SetBuilder(inPlayer->entindex()); - } - - // If it was an alien tech building, look for a hive that doesn't support this tech and make it support it - if((inBuildID == ALIEN_BUILD_DEFENSE_CHAMBER) || (inBuildID == ALIEN_BUILD_MOVEMENT_CHAMBER) || (inBuildID == ALIEN_BUILD_SENSORY_CHAMBER)) - { - if(inPlayer) - { - AvHSUUpdateHiveTechology(inPlayer->GetTeam(), inBuildID); - } - } - - const char* theClassName = STRING(theBuilding->pev->classname); - if(inPlayer && theClassName) - { - inPlayer->LogPlayerAction("structure_built", theClassName); - } - - // Notify player and his teammates - if(!GetGameRules()->GetIsCombatMode()) - { - inPlayer->PlayHUDStructureNotification(inBuildID, theBuilding->pev->origin); - } -} - -CBaseEntity* AvHSUBuildTechForPlayer(AvHMessageID inBuildID, const Vector& inLocation, AvHPlayer* inPlayer) -{ - CBaseEntity* theEntity = NULL; - - if(!GetGameRules()->GetIsCombatMode() || AvHSHUGetIsCombatModeTech(inBuildID)) - { - char* theClassName; - - if(AvHSHUGetBuildTechClassName(inBuildID, theClassName)) - { - // Create without owner to fix solidity problems? - //theEntity = CBaseEntity::Create(theClassName, inLocation, theAngles, inPlayer->edict()); - theEntity = CBaseEntity::Create(theClassName, inLocation, AvHSUGetRandomBuildingAngles()); - if(theEntity) - { - // Set team - theEntity->pev->team = inPlayer->pev->team; - - // Set any team-wide upgrades - AvHTeam* theTeam = inPlayer->GetTeamPointer(); - ASSERT(theTeam); - theEntity->pev->iuser4 |= theTeam->GetTeamWideUpgrades(); - - AvHSUBuildingJustCreated(inBuildID, theEntity, inPlayer); - - if(!theEntity->IsInWorld() && GetGameRules()->GetIsCombatMode()) - { - //voogru: okay, if for WHATEVER reason this isnt touched by a player, set it to remove in 2.5 seconds. - theEntity->SetThink(&CBaseEntity::SUB_Remove); - theEntity->pev->nextthink = gpGlobals->time + 2.5f; - - //voogru: force them to touch it. - DispatchTouch(ENT(theEntity->pev), ENT(inPlayer->pev)); - } - - // Do special stuff for some buildings (special case scan so it doesn't "teleport in") - if(inBuildID != BUILD_SCAN) - { - //voogru: play effect at player origin in combat, cause the item is in the middle of nowhere. - Vector vecOrigin = (theEntity->IsInWorld() && !GetGameRules()->GetIsCombatMode()) ? inLocation : inPlayer->pev->origin; - PLAYBACK_EVENT_FULL(0, inPlayer->edict(), gPhaseInEventID, 0, vecOrigin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); - } - } - } - } - - return theEntity; -} - -void AvHSUExplodeEntity(CBaseEntity* inEntity, Materials inMaterial) -{ - CBreakable* theDebris = NULL; - theDebris = GetClassPtr((CBreakable*)NULL); - - UTIL_SetOrigin(VARS(theDebris->pev), inEntity->pev->origin); - - theDebris->pev->model = inEntity->pev->model; - - theDebris->SetMaterial(inMaterial); - - theDebris->Spawn(); - - theDebris->Die(); - - UTIL_Remove(theDebris); -} - -Vector AvHSUEyeToBodyVector(entvars_t* inEye, CBaseEntity* inTarget) -{ - Vector vecMid = inEye->origin + inEye->view_ofs; - Vector vecMidEnemy = inTarget->BodyTarget(vecMid); - Vector vecDirToEnemy = vecMidEnemy - vecMid; - return vecDirToEnemy; -} - -float AvHSUEyeToBodyDistance(entvars_t* inEye, CBaseEntity* inTarget) -{ - return AvHSUEyeToBodyVector(inEye, inTarget).Length(); -} - -float AvHSUEyeToBodyXYDistance(entvars_t* inEye, CBaseEntity* inTarget) -{ - return AvHSUEyeToBodyVector(inEye, inTarget).Length2D(); -} - -bool AvHSSUGetIsClassNameFadeable(const char* inClassName) -{ - bool theSuccess = false; - - if(/*FStrEq(inClassName, kesFuncDoor) || FStrEq(inClassName, "func_door_rotating") || FStrEq(inClassName, "momentary_door") ||*/ FStrEq(inClassName, kesSeethrough) || FStrEq(inClassName, kesSeethroughDoor)) - { - theSuccess = true; - } - - return theSuccess; -} - -void AvHSUPlayPhaseInEffect(int inFlags, CBaseEntity* inStartEntity, CBaseEntity* inEndEntity) -{ - // Play sound and implosion effect - EMIT_SOUND(ENT(inStartEntity->pev), CHAN_AUTO, kPhaseGateTransportSound, .8, ATTN_NORM); - - MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, inStartEntity->pev->origin); - WRITE_BYTE(TE_IMPLOSION); - WRITE_COORD(inStartEntity->pev->origin.x); - WRITE_COORD(inStartEntity->pev->origin.y); - WRITE_COORD(inStartEntity->pev->origin.z + 16); - WRITE_BYTE(255); - WRITE_BYTE(15); - WRITE_BYTE(4); - MESSAGE_END(); - - - // Play sound and particles - EMIT_SOUND(ENT(inEndEntity->pev), CHAN_AUTO, kPhaseGateTransportSound, .8, ATTN_NORM); - - PLAYBACK_EVENT_FULL(inFlags, inEndEntity->edict(), gTeleportEventID, 0, inEndEntity->pev->origin, inEndEntity->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); -} - -void AvHSUPlayRandomConstructionEffect(AvHPlayer* inPlayer, CBaseEntity* inConstructee) -{ - bool theIsMarine = inPlayer->GetIsMarine(); - float theVolume = .3f*AvHPlayerUpgrade::GetSilenceVolumeLevel(inPlayer->GetUser3(), inPlayer->pev->iuser4); - if(!theIsMarine) - { - gSoundListManager.PlaySoundInList(kAlienConstructionSoundList, inConstructee, CHAN_BODY, theVolume); - - // TODO: Play cool particle puff or something - } - else - { - gSoundListManager.PlaySoundInList(kMarineConstructionSoundList, inConstructee, CHAN_BODY, theVolume); - - // Play sparks every other time - if(RANDOM_LONG(0, 1) == 1) - { - // Emit sparks from most of the way up the turret - Vector theVector(inConstructee->pev->origin); - float theRandomFloat = RANDOM_FLOAT(0.5F, 1.0f); - float theHeight = theRandomFloat*(inConstructee->pev->absmax.z - inConstructee->pev->absmin.z); - theVector.z += theHeight; - UTIL_Sparks(theVector); - } - - } -} - -bool AvHSUPlayerCanBuild(entvars_t* inPev) -{ - bool thePlayerCanBuild = false; - - // If player is a marine or a builder-alien, he can build - if((inPev->iuser3 == AVH_USER3_MARINE_PLAYER) || (inPev->iuser3 == AVH_USER3_ALIEN_PLAYER2)) - { - thePlayerCanBuild = true; - } - - return thePlayerCanBuild; -} - -bool AvHSUPlayParticleEvent(const char* inParticleSystemName, const edict_t* inEdict, const Vector& inOrigin, int inEventFlags) -{ - bool theSuccess = false; - - // Look up particle system template by name - uint32 theTemplateIndex = 0; - if(gParticleTemplateList.GetTemplateIndexWithName(inParticleSystemName, theTemplateIndex)) - { - PLAYBACK_EVENT_FULL(inEventFlags, inEdict, gParticleEventID, 0, (float*)&inOrigin, (float*)&g_vecZero, 0.0f, 0.0, theTemplateIndex, 0, 0, 0); - - theSuccess = true; - } - - return theSuccess; -} - -void AvHSUPlayNumericEvent(float inNumber, const edict_t* inEdict, Vector& inOrigin, int inEventFlags, int inNumericEventType, int inTeamNumber) -{ - PLAYBACK_EVENT_FULL(0, inEdict, gNumericalInfoEventID, 0, inOrigin, (float *)&g_vecZero, inNumber, 0.0, inNumericEventType, inTeamNumber, 0, 0); -} - -void AvHSUPlayNumericEventAboveStructure(float inNumber, AvHBaseBuildable* inBuildable, int inNumericEventType) -{ - // Generate visual resource event - Vector theMinSize; - Vector theMaxSize; - AvHSHUGetSizeForTech(inBuildable->GetMessageID(), theMinSize, theMaxSize); - - Vector theStartPos = inBuildable->pev->origin; - theStartPos.z += theMaxSize.z; - - AvHSUPlayNumericEvent(inNumber, inBuildable->edict(), theStartPos, 0, inNumericEventType, inBuildable->pev->team); -} - -void AvHSURemoveAllEntities(const char* inClassName) -{ - FOR_ALL_ENTITIES(inClassName, CBaseEntity*); - UTIL_Remove(theEntity); - END_FOR_ALL_ENTITIES(inClassName); -} - -// Works for ammo and health only, for use in Combat mode. -void AvHSUResupplyFriendliesInRange(int inNumEntitiesToCreate, AvHPlayer* inPlayer, int inRange) -{ - // Create list of nearby eligible players - PlayerListType thePlayerList; - - thePlayerList.push_back(inPlayer); - - if(inRange > 0) - { - CBaseEntity* theEntity = NULL; - while ((theEntity = UTIL_FindEntityInSphere(theEntity, inPlayer->pev->origin, inRange)) != NULL) - { - const char* theClassName = STRING(theEntity->pev->classname); - if(!AvHSUGetIsExternalClassName(theClassName)) - { - AvHPlayer* thePlayer = dynamic_cast(theEntity); - if(thePlayer && thePlayer->GetIsRelevant() && (thePlayer->GetTeam() == inPlayer->GetTeam()) && (thePlayer != inPlayer)) - { - thePlayerList.push_back(thePlayer); - } - } - } - } - - // While there are more to supply - while(inNumEntitiesToCreate > 0) - { - // Pick the most eligible player, giving preference to creating player - int theRandomOffset = RANDOM_LONG(0, thePlayerList.size() - 1); - AvHPlayer* thePlayer = thePlayerList[theRandomOffset]; - - // Give player entity - //AvHSUBuildTechForPlayer(inMessageID, thePlayer->pev->origin, inPlayer); - - // puzl: 1017 combat resupply amount - BOOL theHelpedPlayer = AvHHealth::GiveHealth(thePlayer, BALANCE_VAR(kPointsPerHealth)); - - if(!theHelpedPlayer) - { - theHelpedPlayer = AvHGenericAmmo::GiveAmmo(thePlayer); - } - - if(theHelpedPlayer) - { - // Play event for each person helped - PLAYBACK_EVENT_FULL(0, thePlayer->edict(), gPhaseInEventID, 0, thePlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); - } - - // Decrement - inNumEntitiesToCreate--; - } -} - -bool AvHSUGetIsOftRepeatedAlert(AvHAlertType inAlertType) -{ - bool theIsOftRepeated = false; - - switch(inAlertType) - { - case ALERT_UNDER_ATTACK: - case ALERT_HIVE_DYING: - case ALERT_PLAYER_ENGAGE: - case ALERT_SENTRY_FIRING: - case ALERT_SENTRY_DAMAGED: - case ALERT_ORDER_NEEDED: - case ALERT_SOLDIER_NEEDS_AMMO: - case ALERT_SOLDIER_NEEDS_HEALTH: - theIsOftRepeated = true; - break; - } - - return theIsOftRepeated; -} - -bool AvHSUGetIsUrgentAlert(AvHAlertType inAlertType) -{ - bool theIsUrgent = false; - - switch(inAlertType) - { - // Very important and relatively rare - case ALERT_HIVE_COMPLETE: - case ALERT_NEW_TRAIT: - case ALERT_LOW_RESOURCES: - case ALERT_UNDER_ATTACK: - case ALERT_HIVE_DYING: - - // These must always be played because they don't have any notification on the commander UI - case ALERT_RESEARCH_COMPLETE: - case ALERT_UPGRADE_COMPLETE: - theIsUrgent = true; - break; - } - - return theIsUrgent; -} - -// Used to screen for non-NS entities (they can't use RTTI among others) -bool AvHSUGetIsExternalClassName(const char* inClassName) -{ - bool theIsExternal = false; - - if(inClassName && (!strncmp(inClassName, "adminmod_", 9))) - { - theIsExternal = true; - } - - return theIsExternal; -} - -bool AvHSUGetIsSubjectToVisibilityRules(CBaseEntity* inEntity) -{ - bool theIsSubjectToVis = false; - - //char theErrorString[256]; - const char* theEntityName = "unknown"; - if(inEntity && inEntity->pev) - { - const char* theTentativeEntityName = NULL; - theTentativeEntityName = STRING(inEntity->pev->classname); - if(theTentativeEntityName) - { - theEntityName = theTentativeEntityName; - } - } - - // Players - if(!strcmp(theEntityName, kAvHPlayerClassName)) - { - theIsSubjectToVis = true; - } - // All buildables - else if(GetHasUpgrade(inEntity->pev->iuser4, MASK_BUILDABLE)) - { - theIsSubjectToVis = true; - } - // Hives - else if(!strcmp(theEntityName, kesTeamHive)) - { - theIsSubjectToVis = true; - } - // Webs - else if(!strcmp(theEntityName, kesTeamWebStrand)) - { - theIsSubjectToVis = true; - } - // Visibility for particle systems - else if(!strcmp(theEntityName, kesParticles)) - { - theIsSubjectToVis = true; - } - else if(!strcmp(theEntityName, kesParticlesCustom)) - { - theIsSubjectToVis = true; - } - // Func resources - else if(!strcmp(theEntityName, kesFuncResource)) - { - theIsSubjectToVis = true; - } - else if(dynamic_cast(inEntity)) - { - theIsSubjectToVis = true; - } - - //sprintf(theErrorString, "Entity %s subject to vis: %d.\n", theEntityName, theIsSubjectToVis); - //ALERT(at_logged, theErrorString); - - return theIsSubjectToVis; -} - -int AvHSUGetNumHumansInGame(void) -{ - int theCount = 0; - - for(int theIndex = 1; theIndex <= gpGlobals->maxClients; theIndex++ ) - { - CBaseEntity* thePlayer = UTIL_PlayerByIndex(theIndex); - - if(thePlayer == NULL) - continue; - - - if(FNullEnt( thePlayer->pev ) ) - continue; - - - if(FStrEq(STRING(thePlayer->pev->netname), "" )) - continue; - - - if(FBitSet(thePlayer->pev->flags, FL_FAKECLIENT)) - continue; - - theCount++; - } - - return theCount; -} - -AvHHive* AvHSUGetRandomActiveHive(AvHTeamNumber inTeam) -{ - AvHHive* theHive = NULL; - - int theNumActiveHives = 0; - vector theHiveList; - - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - if(theEntity->GetIsActive() && (theEntity->GetTeamNumber() == inTeam)) - { - theHiveList.push_back(theEntity); - } - END_FOR_ALL_ENTITIES(kesTeamHive) - - if(theHiveList.size() > 0) - { - int theRandomHive = RANDOM_LONG(0, theHiveList.size()-1); - theHive = theHiveList[theRandomHive]; - } - - return theHive; -} - -AvHHive* AvHSUGetRandomActivateableHive(AvHTeamNumber inTeam) -{ - AvHHive* theHive = NULL; - - int theNumActiveHives = 0; - vector theHiveList; - - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - bool theCanBecomeActive = theEntity->CanBecomeActive(); - if(theCanBecomeActive /*&& (theEntity->GetTeamNumber() == inTeam)*/) - { - theHiveList.push_back(theEntity); - } - END_FOR_ALL_ENTITIES(kesTeamHive) - - if(theHiveList.size() > 0) - { - int theRandomHive = RANDOM_LONG(0, theHiveList.size()-1); - theHive = theHiveList[theRandomHive]; - } - - return theHive; -} - -int AvHSUGetWeaponStayTime() -{ - int theWeaponStayTime = BALANCE_VAR(kWeaponStayTime); - - if(GetGameRules()->GetIsCombatMode()) - { - theWeaponStayTime = BALANCE_VAR(kCombatWeaponStayTime); - } - - return theWeaponStayTime; -} - -void AvHSUResearchStarted(CBaseEntity* inResearchEntity, AvHMessageID inResearchingTech) -{ - if(inResearchingTech == RESEARCH_DISTRESSBEACON) - { - // Playback event for all, sending time it takes to finish - int theDistressBeaconTime = GetGameRules()->GetBuildTimeForMessageID(inResearchingTech); - - // Play sound at center of marine spawn - //Vector theSoundOrigin = GetGameRules()->GetSpawnAreaCenter((AvHTeamNumber)inResearchEntity->pev->team); - - PLAYBACK_EVENT_FULL(0, inResearchEntity->edict(), gDistressBeaconEventID, 0, inResearchEntity->pev->origin, (float *)&g_vecZero, 0.0, 0.0, theDistressBeaconTime, 0, 0, 0 ); - - // Play special siren - //EMIT_SOUND(inResearchEntity->edict(), CHAN_AUTO, kDistressBeaconSound, 1.0f, ATTN_IDLE); - } -} - -void AvHSUResearchComplete(CBaseEntity* inResearchEntity, AvHMessageID inResearchingTech) -{ - // Watch for various research messages - //if(inResearchingTech == RESEARCH_REINFORCEMENTS) - //{ - // AvHInfantryPortal* thePortal = dynamic_cast(inResearchEntity); - // This should never happen but don't crash right now while I fix the bug for the playtest - //ASSERT(thePortal); - //if(thePortal) - //{ - // thePortal->SetReinforcements(thePortal->GetReinforcements() + 1); - //} - //} - - if(inResearchingTech == RESOURCE_UPGRADE) - { - AvHResourceTower* theTower = dynamic_cast(inResearchEntity); - if(theTower) - { - theTower->Upgrade(); - } - // This should never happen but don't crash right now while I fix the bug for the playtest - //else - //{ - // ASSERT(false); - //} - } - else if(inResearchingTech == ARMORY_UPGRADE) - { - AvHArmory* theArmory = dynamic_cast(inResearchEntity); - if(theArmory) - { - theArmory->Upgrade(); - } - } - else if(inResearchingTech == TURRET_FACTORY_UPGRADE) - { - AvHTurretFactory* theTurretFactory = dynamic_cast(inResearchEntity); - if(theTurretFactory) - { - theTurretFactory->Upgrade(); - } - } - else if(inResearchingTech == RESEARCH_DISTRESSBEACON) - { - // Player distress sound effect? - - // Immediately respawn all marines at base! - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->pev->team == inResearchEntity->pev->team) - { - bool theSuccess = false; - - AvHPlayMode thePlayMode = theEntity->GetPlayMode(); - - // Respawn dead/waiting players - if((thePlayMode == PLAYMODE_REINFORCING) || (thePlayMode == PLAYMODE_AWAITINGREINFORCEMENT) || (!theEntity->IsAlive() && (thePlayMode == PLAYMODE_PLAYING))) - { - theEntity->SetPlayMode(PLAYMODE_PLAYING); - theSuccess = true; - } - // teleport existing players back - else if((thePlayMode == PLAYMODE_PLAYING) && theEntity->GetCanBeAffectedByEnemies()) - { - if ( GetGameRules()->CanPlayerBeacon(theEntity) ) - { - edict_t* theSpawnPoint = GetGameRules()->SelectSpawnPoint(theEntity); - - if(theSpawnPoint) - { - if ( VectorDistance(theSpawnPoint->v.origin, theEntity->pev->origin) > BALANCE_VAR(kDistressBeaconRange)) - { - theEntity->InitPlayerFromSpawn(theSpawnPoint); - - // Reset anything some things - theEntity->SetEnsnareState(false); - theEntity->SetIsStunned(false); - theSuccess = true; - } - } - } - } - - if(theSuccess) - { - // Play special phase effect/sound for player - int theFlags = 0; - AvHSUPlayPhaseInEffect(theFlags, theEntity, theEntity); - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - } - else if(inResearchingTech == RESEARCH_ELECTRICAL) - { - AvHBaseBuildable* theBuildable = dynamic_cast(inResearchEntity); - if(theBuildable) - { - // Only allow certain structures to be electrified (fix for exploit timing/selection exploit) - AvHMessageID theBuildableMessage = theBuildable->GetMessageID(); - switch(theBuildableMessage) - { - case BUILD_RESOURCES: - case BUILD_TURRET_FACTORY: - case TURRET_FACTORY_UPGRADE: - SetUpgradeMask(&theBuildable->pev->iuser4, MASK_UPGRADE_11); - break; - } - } - } -} - -template bool isMember(CBaseEntity* object) -{ - return (object != NULL) && (dynamic_cast(object) != NULL); -} - -//TODO: reimplememnt this functionality as a virtual function under CBaseBuildable to keep the -// information tied directly to the class that it describes, reducing total locations to track (KGP) -bool AvHSUGetIsResearchApplicable(CBaseEntity* inResearchEntity, AvHMessageID inResearchingTech) -{ - bool theIsApplicable = false; - - switch(inResearchingTech) - { - case RESOURCE_UPGRADE: - theIsApplicable = isMember(inResearchEntity); - break; - case ARMORY_UPGRADE: - case RESEARCH_GRENADES: - theIsApplicable = isMember(inResearchEntity); - break; - case TURRET_FACTORY_UPGRADE: - theIsApplicable = isMember(inResearchEntity); - break; - case RESEARCH_DISTRESSBEACON: - case RESEARCH_MOTIONTRACK: - case RESEARCH_PHASETECH: - theIsApplicable = isMember(inResearchEntity); - break; - case RESEARCH_ARMOR_ONE: - case RESEARCH_ARMOR_TWO: - case RESEARCH_ARMOR_THREE: - case RESEARCH_WEAPONS_ONE: - case RESEARCH_WEAPONS_TWO: - case RESEARCH_WEAPONS_THREE: - case RESEARCH_CATALYSTS: - theIsApplicable = isMember(inResearchEntity); - break; - case RESEARCH_HEAVYARMOR: - case RESEARCH_JETPACKS: - theIsApplicable = isMember(inResearchEntity); - break; - case RESEARCH_ELECTRICAL: - theIsApplicable = isMember(inResearchEntity) || isMember(inResearchEntity); - break; - } - return theIsApplicable; -} - -void AvHSUSetCollisionBoxFromSequence(entvars_t* inPev) -{ - ASSERT(inPev); - - studiohdr_t* theStudioHeader = (studiohdr_t*)GET_MODEL_PTR( ENT(inPev) ); - if(theStudioHeader == NULL) - { - ALERT(at_console, "AvHSUSetCollisionBoxFromSequence(): Invalid model\n"); - } - - mstudioseqdesc_t* theSeqDesc = (mstudioseqdesc_t *)((byte *)theStudioHeader + theStudioHeader->seqindex); - - Vector theMin = theSeqDesc[inPev->sequence].bbmin; - Vector theMax = theSeqDesc[inPev->sequence].bbmax; - - UTIL_SetSize(inPev, theMin, theMax); -} - - - -////========================================================================= -//// Returns 0 if the area around obj is safe to build in -//int CBaseEntity::CheckArea( CBaseEntity *pIgnore ) -//{ -// TraceResult tr; -// Vector vecOrg = pev->origin; -// -// // Check the origin -// int iContents = UTIL_PointContents(vecOrg); -// if ( iContents != CONTENT_EMPTY && iContents != CONTENT_WATER ) -// return CAREA_BLOCKED; -// -// Vector vecIgnoreOrg = pIgnore->pev->origin; -// // Get the player's origin irrelevant of crouching -// if ( pIgnore->pev->flags & FL_DUCKING ) -// { -// vecIgnoreOrg = vecIgnoreOrg + (VEC_DUCK_HULL_MIN - -// VEC_HULL_MIN); -// } -// // Trace a hull -// UTIL_TraceHull( vecIgnoreOrg, pev->origin, ignore_monsters, -// large_hull, edict(), &tr ); -// CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); -// if (tr.flFraction != 1 || tr.fAllSolid == 1) -// return CAREA_BLOCKED; -// -// // Check for solid entities in the area -// CBaseEntity *pEnt = NULL; -// while ( (pEnt = UTIL_FindEntityInSphere( pEnt, pev->origin, 48 )) != -// NULL ) -// { -// // If it's not the engineer, and its a solid entity, fail -// if (pEnt != pIgnore && pEnt != this && pEnt->pev->solid > -// SOLID_TRIGGER) -// return CAREA_BLOCKED; -// } -// -// // Cycle through all the Nobuild zones in the map and make sure this -// isn't in one of them -// CBaseEntity *pNoBuild = UTIL_FindEntityByClassname( NULL, -// "func_nobuild" ); -// while ( pNoBuild ) -// { -// // Check to see if we're building in this zone -// if ( vecOrg.x >= pNoBuild->pev->mins.x && vecOrg.y >= -// pNoBuild->pev->mins.y && vecOrg.z >= pNoBuild->pev->mins.z && -// vecOrg.x <= pNoBuild->pev->maxs.x && -// vecOrg.y <= pNoBuild->pev->maxs.y && vecOrg.z <= pNoBuild->pev->maxs.z ) -// return CAREA_NOBUILD; -// -// pNoBuild = UTIL_FindEntityByClassname( pNoBuild, -// "func_nobuild" ); -// } -// -// // Check below -// UTIL_TraceLine( vecOrg, vecOrg + Vector(0,0,-64), -// dont_ignore_monsters, edict(), &tr ); -// if ( tr.flFraction == 1.0 ) -// return CAREA_BLOCKED; -// -// return CAREA_CLEAR; -//} - -CBaseEntity* AvHSUGetEntityFromIndex(int inEntityIndex) -{ - CBaseEntity* theEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex)); - return theEntity; -} - - -CGrenade* AvHSUShootServerGrenade(entvars_t* inOwner, Vector inOrigin, Vector inVelocity, float inTime, bool inHandGrenade) -{ - CGrenade* theGrenade = CGrenade::ShootExplosiveTimed(inOwner, inOrigin, inVelocity, inTime); - ASSERT(theGrenade); - - theGrenade->pev->team = inOwner->team; - - // Set it as a marine item so it gets damage upgrades - theGrenade->pev->iuser3 = AVH_USER3_MARINEITEM; - theGrenade->pev->iuser4 = inOwner->iuser4; - - if(inHandGrenade) - { - theGrenade->pev->classname = MAKE_STRING("handgrenade"); - } - -// if(!GetGameRules()->GetDrawInvisibleEntities()) -// { -// theGrenade->pev->effects |= EF_NODRAW; -// } - - return theGrenade; -} - -void AvHSUKnockPlayerAbout(CBaseEntity* inAttcker, CBaseEntity* inVictim, int inForce) -{ - if(inAttcker && inVictim->IsPlayer()) - { - AvHPlayer* theVictim = dynamic_cast(inVictim); - - //Dont _ever_ knock around teammates - if(inAttcker->pev->team != inVictim->pev->team && theVictim && theVictim->GetCanBeAffectedByEnemies()) - { - Vector VecDir; - VecDir = inVictim->pev->origin - inAttcker->pev->origin; - VecDir = VecDir.Normalize(); - - if(theVictim->GetIsMarine() && theVictim->GetHasHeavyArmor()) - inForce = inForce * 0.50; - - //If they are in the air they have another 50% less knockback (to prevent them from becoming superplayer - if(!(theVictim->pev->flags & FL_ONGROUND) || !theVictim->pev->groundentity) - { - inForce = inForce * 0.50; - } - // Reduce knockback to 1/3 if user is ducked and on the ground - else if ((theVictim->pev->bInDuck) || (theVictim->pev->flags & FL_DUCKING)) - { - inForce = inForce * 0.33; - } - - inVictim->pev->punchangle.z = -18; - inVictim->pev->punchangle.x = 5; - inVictim->pev->velocity = VecDir * max(0.0f, 120 - VecDir.Length() ) * inForce/75; - } - } - - /*AvHPlayer* thePlayer = dynamic_cast(inEntity); - if(thePlayer && thePlayer->GetCanBeAffectedByEnemies()) - { - inEntity->pev->punchangle.z = -18; - inEntity->pev->punchangle.x = 5; - if(!(inEntity->pev->flags & FL_ONGROUND) || !inEntity->pev->groundentity) // if the player is in midair, don't use full force - { - inEntity->pev->velocity = inEntity->pev->velocity - gpGlobals->v_right*(inForce/10); - } - else - { - inEntity->pev->velocity = inEntity->pev->velocity - gpGlobals->v_right*inForce; - } - } - }*/ -} - -void AvHGetLineBounds(const Vector& vecStart, const Vector& vecEnd, Vector& outMins, Vector& outMaxs) -{ - - outMins = vecStart; - outMaxs = vecStart; - - for (int i = 0; i < 3; ++i) - { - - if (vecStart[i] < outMins[i]) - { - outMins[i] = vecStart[i]; - } - - if (vecStart[i] > outMaxs[i]) - { - outMaxs[i] = vecStart[i]; - } - - if (vecEnd[i] < outMins[i]) - { - outMins[i] = vecEnd[i]; - } - - if (vecEnd[i] > outMaxs[i]) - { - outMaxs[i] = vecEnd[i]; - } - - } - - // Increase the box by a bit since most structure hulls are not very - // accurately sized. - - const float kBoundingBoxPadding = 100; - - outMins[0] -= kBoundingBoxPadding; - outMins[1] -= kBoundingBoxPadding; - outMins[2] -= kBoundingBoxPadding; - - outMaxs[0] += kBoundingBoxPadding; - outMaxs[1] += kBoundingBoxPadding; - outMaxs[2] += kBoundingBoxPadding; - -} - -/** - * Drop in replacement for UTIL_TraceLine which works properly for - */ -void AvHTraceLine(const Vector& vecStart, const Vector& vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr) -{ - - // Trace against the world. - - UTIL_TraceLine(vecStart, vecEnd, dont_ignore_monsters, pentIgnore, ptr); - - // Trace against the players/structures. - - if (igmon != ignore_monsters) - { - - vec3_t theRayOrigin; - vec3_t theRayDirection; - - VectorCopy(vecStart, theRayOrigin); - VectorSubtract(vecEnd, vecStart, theRayDirection); - - vec3_t theLineMins; - vec3_t theLineMaxs; - - AvHGetLineBounds(vecStart, vecEnd, theLineMins, theLineMaxs); - - // Get the bounding box for the line. - - const int kMaxNumEntities = 128; - CBaseEntity* pList[kMaxNumEntities]; - - int theNumEntities = UTIL_EntitiesInBox(pList, kMaxNumEntities, theLineMins, theLineMaxs, (FL_CLIENT | FL_MONSTER)); - - for (int i = 0; i < theNumEntities; ++i) - { - - edict_t* theEdict = pList[i]->edict(); - - // tankefugl: 0000941 -- added check to remove testing of spectators - if ((!(pList[i]->pev->iuser1 > 0 || pList[i]->pev->flags & FL_SPECTATOR)) && theEdict != pentIgnore) -// if (theEdict != pentIgnore) - { - - float t = NS_TraceLineAgainstEntity(pList[i]->entindex(), gpGlobals->time, theRayOrigin, theRayDirection); - - if (t != AVH_INFINITY && t < ptr->flFraction) - { - ptr->flFraction = t; - ptr->pHit = theEdict; - ptr->iHitgroup = 0; // NS doesn't use hit groups. - } - - } - - } - - // Compute the end position. - - if (ptr->flFraction != 1.0) - { - VectorMA(theRayOrigin, ptr->flFraction, theRayDirection, ptr->vecEndPos); - } - - } - -} - - -void AvHSUServerTraceBullets(const Vector& inStart, const Vector& inEnd, IGNORE_MONSTERS inIgnoreMonsters, edict_t* inIgnoreEdict, TraceResult& outTraceResult, bool& outProtected) -{ - outProtected = false; - - // This is the old way that doesn't take into account skulk rotation. - // UTIL_TraceLine(inStart, inEnd, inIgnoreMonsters, /*dont_ignore_glass,*/ inIgnoreEdict, &outTraceResult); - - // TEMP removed the skulk hitboxes since it's too risky for the LAN. - // joev: 0000573 - // this was commented out meaning that it was just stock tracelines, not using Max M's superb hitbox collision code. - // Now *all* hitboxes perform as expected and the crouched fade can be shot pretty much anywhere on the model - // (allowing for about a 5% visual disparity) - AvHTraceLine(inStart, inEnd, inIgnoreMonsters, /*dont_ignore_glass,*/ inIgnoreEdict, &outTraceResult); - // :joev - CBaseEntity* theEntityHit = CBaseEntity::Instance(outTraceResult.pHit); - - // If we hit an entity that's hidden in the umbra, return that we didn't hit anything - if(theEntityHit) - { - if(GetHasUpgrade(theEntityHit->pev->iuser4, MASK_UMBRA)) - { - const int theUmbraEffectiveness = BALANCE_VAR(kUmbraEffectiveness); - - // Block most shots but not all - if(RANDOM_LONG(0, theUmbraEffectiveness) != 0) - { - outProtected = true; - } - } - else - { - // Check if bullets pass through an umbra cloud before hitting target - // If so, don't hit the target - } - } -} - -void AvHSUUpdateHiveTechology(AvHTeamNumber inTeamNumber, AvHMessageID inBuildID) -{ - AvHHive* theHive = NULL; - bool theTechnologyAlreadySupported = false; - - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - if(theEntity && theEntity->GetIsActive() && (theEntity->pev->team == (int)inTeamNumber)) - { - AvHMessageID theTechnology = theEntity->GetTechnology(); - if(theTechnology == inBuildID) - { - theTechnologyAlreadySupported = true; - break; - } - else if(theTechnology == MESSAGE_NULL) - { - if(!theHive) - { - theHive = theEntity; - } - } - } - END_FOR_ALL_ENTITIES(kesTeamHive); - - if(!theTechnologyAlreadySupported && theHive) - { - theHive->SetTechnology(inBuildID); - } -} - -bool gIsDebugging = false; - -bool AvHSUGetIsDebugging() -{ - return gIsDebugging; -} - -void AvHSUSetIsDebugging(bool inState) -{ - gIsDebugging = inState; -} - -void AvHSUAddDebugPoint(float inX, float inY, float inZ) -{ - CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(1)); - AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); - if(thePlayer) - { - thePlayer->AddDebugEnemyBlip(inX, inY, inZ); - } -} - -AvHUser3 AvHSUGetGroupTypeFromSelection(const EntityListType& inEntityListType) -{ - // By default, it's a generic marine - AvHUser3 theUnifiedUser3 = AVH_USER3_NONE; - - // Loop through all the entities - bool theFirstTime = true; - bool theSuccess = false; - - for(EntityListType::const_iterator theIter = inEntityListType.begin(); theIter != inEntityListType.end(); theIter++, theSuccess) - { - theSuccess = true; - - CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter)); - if(theBaseEntity) - { - // If all the user3s are the same, then use that - AvHUser3 theCurrentUser3 = (AvHUser3)(theBaseEntity->pev->iuser3); - if(theFirstTime || (theCurrentUser3 == theUnifiedUser3)) - { - theUnifiedUser3 = theCurrentUser3; - theFirstTime = false; - } - else - { - theSuccess = false; - } - } - else - { - theSuccess = false; - } - } - - if(!theSuccess) - { - theUnifiedUser3 = AVH_USER3_MARINE_PLAYER; - } - - return theUnifiedUser3; -} - -void AvHSURemoveEntityFromHotgroupsAndSelection(int inEntityIndex) -{ - CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex)); - if(theBaseEntity) - { - // Get team - AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)theBaseEntity->pev->team); - if(theTeam) - { - // Remove entity from any hotgroups on team - for(int i=0; i < kNumHotkeyGroups; i++) - { - EntityListType theHotGroup = theTeam->GetGroup(i); - - EntityListType::iterator theIter = std::find(theHotGroup.begin(), theHotGroup.end(), inEntityIndex); - if(theIter != theHotGroup.end()) - { - // Remove it and update it - theHotGroup.erase(theIter); - - theTeam->SetGroup(i, theHotGroup); - } - } - - // Now get commander and remove entity from their selection - AvHPlayer* theCommander = theTeam->GetCommanderPlayer(); - if(theCommander) - { - theCommander->RemoveSelection(inEntityIndex); - } - } - } -} - -string AvHSUGetLocationText(CBaseEntity* inEntity) -{ - // Add in location name to chat message - string theLocationString(" "); - - AvHPlayer* thePlayer = dynamic_cast(inEntity); - if(thePlayer && thePlayer->GetIsRelevant()) - { - string theLocationName; - if(AvHSHUGetNameOfLocation(GetGameRules()->GetInfoLocations(), inEntity->pev->origin, theLocationName)) - { - theLocationString = theLocationName; - } - } - - return theLocationString; -} - -void AvHSUCreateUmbraCloud(vec3_t inOrigin, AvHTeamNumber inTeamNumber, CBaseEntity* inCreatingEntity) -{ - // Fire umbra cloud event - PLAYBACK_EVENT_FULL(0, inCreatingEntity->edict(), gUmbraCloudEventID, 0, inOrigin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - - // Create the umbra cloud that will provide gameplay effects - AvHUmbraCloud* theUmbra = GetClassPtr((AvHUmbraCloud*)NULL ); - theUmbra->Spawn(); - - VectorCopy(inOrigin, theUmbra->pev->origin); - - // Set team and owner - theUmbra->pev->owner = inCreatingEntity->pev->owner; - theUmbra->pev->team = inTeamNumber; -} - -// Explode with force (players only) -void AvHSUExplosiveForce(const Vector& inOrigin, int inRadius, float inForceScalar, const CBaseEntity* inAttacker, const CBaseEntity* inIgnorePlayer) -{ - CBaseEntity* theEntity = NULL; - while ((theEntity = UTIL_FindEntityInSphere(theEntity, inOrigin, inRadius)) != NULL) - { - const char* theClassName = STRING(theEntity->pev->classname); - if(!AvHSUGetIsExternalClassName(theClassName)) - { - AvHPlayer* thePlayer = dynamic_cast(theEntity); - float theScalar = 1.0f; - if(thePlayer - && thePlayer->GetCanBeAffectedByEnemies() - && GetGameRules()->CanEntityDoDamageTo(inAttacker, thePlayer, &theScalar) - && (thePlayer != inIgnorePlayer) - && thePlayer->pev->team != inAttacker->pev->team) - { - // Apply outward force to player - Vector theForceVector; - VectorSubtract(thePlayer->pev->origin, inOrigin, theForceVector); - - float theForceScalar = inForceScalar; - float theDistanceToExplosion = theForceVector.Length(); - theDistanceToExplosion = min(max(theDistanceToExplosion, 0.0f), (float)inRadius); - - float thePercentForce = (1.0f - (theDistanceToExplosion/(float)inRadius)); - theForceScalar *= thePercentForce; - - theForceVector.Normalize(); - - // tankefugl: 0000771 - if((!(thePlayer->pev->flags & FL_ONGROUND) || !thePlayer->pev->groundentity) || - ((thePlayer->pev->bInDuck) || (thePlayer->pev->flags & FL_DUCKING))) - { - theForceScalar *= 0.33f; - } - - Vector thePreVelocity = thePlayer->pev->velocity; - float theDamageForce = thePlayer->DamageForce(theForceScalar); - Vector thePostVelocity = (thePlayer->pev->velocity + theForceVector *( theDamageForce )); - - // check and cap horisontal speed - float theMaxHorisontalSpeed = BALANCE_VAR(kExplodeMaxHorisontalSpeed); - Vector theHorisontalSpeed; - theHorisontalSpeed[0] = thePostVelocity[0]; - theHorisontalSpeed[1] = thePostVelocity[1]; - theHorisontalSpeed[2] = 0; - float theHorisontalSpeedLength = theHorisontalSpeed.Length(); - float theHorisontalFactor = theMaxHorisontalSpeed / theHorisontalSpeedLength; - if (theHorisontalSpeedLength > theMaxHorisontalSpeed) { - thePostVelocity[0] *= theHorisontalFactor; - thePostVelocity[1] *= theHorisontalFactor; - } - - // then vertical speed - float theMaxVerticalSpeed = BALANCE_VAR(kExplodeMaxVerticalSpeed); - if (fabs(thePostVelocity[2]) > theMaxVerticalSpeed) - { - thePostVelocity[2] = thePostVelocity[2]/fabs(thePostVelocity[2]) * theMaxVerticalSpeed; - } - - // assign new speed - thePlayer->pev->velocity = thePostVelocity; - // tankefugl - } - } - } -} - -int AvHSUGetValveHull(int inHull) -{ - // Convert "our" hull into VALVe hull - int theHull = inHull; - switch(inHull) - { - case 0: - theHull = 1; - break; - case 1: - theHull = 3; - break; - case 2: - theHull = 0; - break; - case 3: - theHull = 2; - break; - default: - ASSERT(false); - break; - } - - return theHull; -} - -bool AvHSUGetIsEnoughRoomForHull(const vec3_t& inCenter, int inHull, edict_t* inIgnoreEntity, bool inIgnorePlayers, bool inIgnoreEntities) -{ - ASSERT(inHull >= 0); - ASSERT(inHull <= 3); - - // Check hull against the world - if(pmove && (pmove->numphysent > 0)) - { - - model_s* theWorldModel = pmove->physents[0].model; - - // Convert "our" hull into VALVe hull. - - int theHull = AvHSUGetValveHull(inHull); - const hull_t* theHullPtr = theWorldModel->hulls + theHull; - - int thePointContents = NS_PointContents(theHullPtr, theHullPtr->firstclipnode, (float*)&inCenter); - if(thePointContents == CONTENTS_SOLID) - { - return false; - } - } - - if (!inIgnoreEntities) - { - - // If that succeeds, check hull size against entities (above check doesn't take ents into account) - - // Use input hull to get min and max vectors - vec3_t theMin; - vec3_t theMax; - - // Scale it down just a tad so we can morph on ramps. The shared movement code should get us unstuck in these minor cases - float kScalar = 1;//.85f; - - switch(inHull) - { - case 0: - theMin = HULL0_MIN*kScalar; - theMax = HULL0_MAX*kScalar; - break; - case 1: - theMin = HULL1_MIN*kScalar; - theMax = HULL1_MAX*kScalar; - break; - case 2: - theMin = HULL2_MIN*kScalar; - theMax = HULL2_MAX*kScalar; - break; - case 3: - theMin = HULL3_MIN*kScalar; - theMax = HULL3_MAX*kScalar; - break; - } - - return AvHSHUGetIsAreaFree(inCenter, theMin, theMax, inIgnoreEntity, inIgnorePlayers); - - } - - return true; - -} - -void AvHSUPrintDevMessage(const string& inMessage, bool inForce) -{ - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - - #ifndef DEBUG - bool theSendMessage = inForce; - if( !theSendMessage ) - { - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(theEntity->edict())); - if(thePlayer && thePlayer->GetIsMember(PLAYERAUTH_DEVELOPER) ) - { - theSendMessage = true; - } - } - if( !theSendMessage ) - { - continue; - } - #endif - - ClientPrint(theEntity->pev, HUD_PRINTNOTIFY, inMessage.c_str()); - - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); -} - -bool AvHCheckLineOfSight(const Vector& vecStart, const Vector& vecEnd, edict_t* pentIgnore, IGNORE_MONSTERS igmon, edict_t* pEntTarget) -{ - - TraceResult tr; - UTIL_TraceLine(vecStart, vecEnd, igmon, ignore_glass, pentIgnore, &tr); - - edict_t* theLastHit = NULL; - - while ( 1 ) - { - - bool theSeeThrough = false; - - if (tr.flFraction < 1 && tr.pHit != NULL) - { - if (tr.pHit->v.rendermode != kRenderNormal) - { - theSeeThrough = true; - } - else if ( AvHSSUGetIsClassNameFadeable(STRING(tr.pHit->v.classname)) && (int)tr.pHit->v.fuser2 != 255 ) - { - theSeeThrough = true; - } - } - - if (!theSeeThrough) - { - break; - } - - edict_t* temp = tr.pHit; - UTIL_TraceLine(tr.vecEndPos, vecEnd, igmon, ignore_glass, tr.pHit, &tr); - - // To avoid an infinite loop if multiple entities are overlapping. - - if (theLastHit == tr.pHit) - { - break; - } - - theLastHit = temp; - - } - - if (pEntTarget != NULL) - { - - const char* s = STRING(tr.pHit->v.classname); - return tr.flFraction < 1 && tr.pHit == pEntTarget; - } - else - { - return tr.flFraction == 1; - } - -} +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHServerUtil.cpp $ +// $Date: 2002/11/22 21:25:46 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHServerUtil.cpp,v $ +// Revision 1.42 2002/11/22 21:25:46 Flayra +// - Adminmod fixes +// +// Revision 1.41 2002/11/15 04:42:13 Flayra +// - Moved utility function into here from client.cpp +// +// Revision 1.40 2002/11/12 22:39:25 Flayra +// - Logging changes for Psychostats compatibility +// +// Revision 1.39 2002/11/12 02:28:53 Flayra +// - Don't reset adminmod_ entities +// +// Revision 1.38 2002/11/03 04:51:55 Flayra +// - Refactoring for AddToFullPack changes +// +// Revision 1.37 2002/10/24 21:42:12 Flayra +// - Utility function for telefragging +// - Hive technology fixes +// +// Revision 1.36 2002/10/20 21:11:34 Flayra +// - Optimizations +// +// Revision 1.35 2002/10/19 21:09:56 Flayra +// - Debugging info for linux +// +// Revision 1.34 2002/10/19 20:58:31 Flayra +// - Debugging info for linux +// +// Revision 1.33 2002/10/18 22:22:19 Flayra +// - Sensory chamber triggers vocal alert +// +// Revision 1.32 2002/10/16 01:06:33 Flayra +// - Added generic particle event +// - Visibility tweak: enemies aren't "detected" unless they're moving. This means the commander can only see nearby moving blips, and marines with motion-tracking won't see still aliens that are detected because they're nearby. This also means webs won't show up when nearby. +// - Distress beacon event +// +// Revision 1.31 2002/10/07 17:49:23 Flayra +// - Umbra balance change +// +// Revision 1.30 2002/10/03 19:06:50 Flayra +// - Profiling info for Linux build +// +// Revision 1.29 2002/09/23 22:30:05 Flayra +// - Commander dropped weapons live forever +// - Observatory and sensory chambers check range in 2D for commander regions +// - Added turret factory upgrading (for siege) +// +// Revision 1.28 2002/09/09 20:05:59 Flayra +// - Sensory chambers now detect enemies in range +// +// Revision 1.27 2002/08/31 18:01:03 Flayra +// - Work at VALVe +// +// Revision 1.26 2002/08/16 02:44:11 Flayra +// - New damage types +// +// Revision 1.25 2002/08/09 00:52:14 Flayra +// - Removed old #ifdef +// +// Revision 1.24 2002/08/02 21:50:24 Flayra +// - Removed hives-are-visible code, I think it's safe again +// +// Revision 1.23 2002/07/25 16:58:00 flayra +// - Linux changes +// +// Revision 1.22 2002/07/23 17:24:27 Flayra +// - Added random building angles for diversity, added research started hooks for distress beacon effects, added versatile alien tech tree, hives are always visible, observatories detect nearby aliens, umbra blocks most but not all bullets +// +// Revision 1.21 2002/07/08 17:16:43 Flayra +// - Tried to cut down on sound list spam by defaulting to CHAN_BODY, added debugging code for tracking down solidity issues +// +// Revision 1.20 2002/07/01 21:44:58 Flayra +// - Added primal scream and umbra support +// +// Revision 1.19 2002/06/25 18:16:56 Flayra +// - Quieted construction effects (normalized now), temporarily removed sensory chamber sight, added upgrading of armory, wrapped bullet tracing for umbra +// +// Revision 1.18 2002/06/03 16:57:44 Flayra +// - Toned down carapace and marine upgrades, removed redundant hive class name, all buildables are subject to visibility rules +// +// Revision 1.17 2002/05/28 18:13:50 Flayra +// - Sensory chambers contribute to hive sight +// +// Revision 1.16 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "util/nowarnings.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHPlayer.h" +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "dlls/cbase.h" +#include "mod/AvHPlayerUpgrade.h" +#include "common/damagetypes.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHSharedUtil.h" +#include "util/MathUtil.h" +#include "engine/studio.h" +#include "mod/AvHSoundListManager.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "mod/AvHAlienEquipment.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHParticleTemplateServer.h" +#include "mod/AvHAlienWeapons.h" +#include "common/com_model.h" +#include "mod/AvHHulls.h" +#include "mod/AnimationUtil.h" + +int NS_PointContents(const hull_t *hull, int num, float p[3]); +float NS_TraceLineAgainstEntity(int inEntityIndex, float inTime, const float inRayOrigin[3], const float inRayDirection[3]); + +extern const float AVH_INFINITY; + +extern playermove_t* pmove; + +#ifdef WIN32 +#include "mmsystem.h" +#endif + +extern int gTeleportEventID; +extern int gParticleEventID; +extern int gNumericalInfoEventID; +extern int gDistressBeaconEventID; +extern int gUmbraCloudEventID; +extern AvHParticleTemplateListServer gParticleTemplateList; + +extern int gPhaseInEventID; +extern AvHSoundListManager gSoundListManager; + +unsigned int AvHSUTimeGetTime() +{ + unsigned int theTime = 0; + +#ifdef WIN32 + theTime = timeGetTime(); +#else + FILE* theFilePointer; + double theIdleTime; + double theUpTime; + + /* Read the system uptime and accumulated idle time from /proc/uptime. We're disregarding theIdleTime */ + theFilePointer = fopen ("/proc/uptime", "r"); + + if(fscanf(theFilePointer, "%lf %lf\n", &theUpTime, &theIdleTime) == 2) + { + /* uptime is in seconds... we want milliseconds */ + theTime = (unsigned int)(theUpTime*1000); + } + + fclose (theFilePointer); + #endif + + return theTime; +} + +int AvHSUCalcCombatSpawnWaveSize(int inNumPlayersOnTeam, int inNumDeadPlayers) +{ + int theSpawnWaveSize = min(inNumDeadPlayers, BALANCE_VAR(kCombatMaxPlayersPerWave)); + return theSpawnWaveSize; +} + +float AvHSUCalcCombatSpawnTime(AvHClassType inClassType, int inNumPlayersOnTeam, int inNumDeadPlayers, int inPlayersSpentLevel) +{ + int theWaveSize = AvHSUCalcCombatSpawnWaveSize(inNumPlayersOnTeam, inNumDeadPlayers); + float theSpawnTime = BALANCE_VAR(kCombatBaseRespawnTime) + max((theWaveSize-1), 0)*BALANCE_VAR(kCombatAdditiveRespawnTime); + return theSpawnTime; +} + +char* AvHSUGetGameVersionString() +{ + static char theGameVersion[1024]; + + string theGameVersionString; + + theGameVersionString = "v" + MakeStringFromInt(BALANCE_VAR(kGameVersionMajor)) + "." + MakeStringFromInt(BALANCE_VAR(kGameVersionMinor)) + "." + + MakeStringFromInt(BALANCE_VAR(kGameVersionRevision)); + + //memset(theGameVersion, 0, 1024); + strcpy(theGameVersion, theGameVersionString.c_str()); + + return theGameVersion; +} + +bool AvHSUGetIsRelevantForTopDownPlayer(const vec3_t& inTopDownPosition, const vec3_t& inEntityPosition, float inScalar) +{ + bool theIsRelevant = false; + + //if(inEntityPosition.z <= inTopDownPosition.z) + //{ + float theXDist = fabs(inTopDownPosition.x - inEntityPosition.x); + float theYDist = fabs(inTopDownPosition.y - inEntityPosition.y); + float theXYDistance = sqrt(theXDist*theXDist + theYDist*theYDist); + + float theCullDistance = GetGameRules()->GetMapExtents().GetTopDownCullDistance()*inScalar; + if(theXYDistance <= theCullDistance) + { + theIsRelevant = true; + } + //} + + return theIsRelevant; +} + +Vector AvHSUGetRandomBuildingAngles() +{ + int theX = 0;//g_engfuncs.pfnRandomLong(0, 360); + int theY = g_engfuncs.pfnRandomLong(0, 360); + int theZ = 0; + + Vector theRandomAngles(theX, theY, theZ); + + return theRandomAngles; +} + +const char* AvHSUGetTeamName(int inTeamNumber) +{ + const char* theTeamName = "none"; + + const AvHTeam* theTeamPointer = GetGameRules()->GetTeam(AvHTeamNumber(inTeamNumber)); + if(theTeamPointer) + { + theTeamName = theTeamPointer->GetTeamName(); + } + + return theTeamName; +} + +#ifdef USE_OLDAUTH +// Steam IDs +const char* kSteamIDPending = "STEAM_ID_PENDING"; +const char* kSteamIDLocal = "STEAM_ID_LOOPBACK"; +const char* kSteamIDBot = "BOT"; +const char* kSteamIDInvalidID = "-1"; +const char* kSteamIDDefault = "STEAM_0:0:0"; +const char* kSteamIDPrefix = "STEAM_"; +bool AvHSUGetIsValidAuthID(const string& inAuthID) +{ + bool theIsValid = true; + + // "0" is WONid that hasn't been entered + if((inAuthID == "") || (inAuthID == " ") || (inAuthID == "0") || (inAuthID == kSteamIDDefault) || (inAuthID == kSteamIDInvalidID) || (inAuthID == kSteamIDBot) || (inAuthID == kSteamIDLocal)) + { + theIsValid = false; + } + + return theIsValid; +} +// Function that is backwards-compatible with WON ids +string AvHSUGetPlayerAuthIDString(edict_t* inPlayer) +{ + string thePlayerAuthID; + + // Try to get SteamID + const char* theSteamID = g_engfuncs.pfnGetPlayerAuthId(inPlayer); + if(strcmp(theSteamID, kSteamIDInvalidID)) + { + thePlayerAuthID = theSteamID; + } + // If that fails, get WonID and put it into a string + else + { + int theWonID = g_engfuncs.pfnGetPlayerWONId(inPlayer); + thePlayerAuthID = MakeStringFromInt(theWonID); + } + + return thePlayerAuthID; +} +#endif + +void AvHSUKillPlayersTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor) +{ + // If new player is stuck inside another player, kill old player + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if((theEntity != inPlayer) && (theEntity->GetIsRelevant())) + { + // : 0000892 -- fixed to allow spawnkilling of crouching players on IP + float theDistanceToPlayer = VectorDistance(inPlayer->pev->origin, theEntity->pev->origin); + float zDistance = inPlayer->pev->origin[2] - theEntity->pev->origin[2]; + float xyDistance = VectorDistance2D(inPlayer->pev->origin, theEntity->pev->origin); + if(theDistanceToPlayer < 30 || (xyDistance < 30 && zDistance > 0 && zDistance < 40)) + { + theEntity->TakeDamage(inInflictor, theEntity->pev, 10000, DMG_GENERIC); + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) +} + +void AvHSUPushbackPlayersTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor) +{ + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if((theEntity != inPlayer) && (theEntity->GetIsRelevant())) + { + // : 0000892 -- fixed to allow spawnkilling of crouching players on IP + float theDistanceToPlayer = VectorDistance(inPlayer->pev->origin, theEntity->pev->origin); + float zDistance = inPlayer->pev->origin[2] - theEntity->pev->origin[2]; + float xyDistance = VectorDistance2D(inPlayer->pev->origin, theEntity->pev->origin); + if(theDistanceToPlayer < 30 || (xyDistance < 30 && zDistance > 0 && zDistance < 40)) + { + vec3_t theDirection; + // If distance == 0, generate a random direction. + if (xyDistance < 0.1f) + { + theDirection[0] = RANDOM_FLOAT(-1, 1); + theDirection[1] = RANDOM_FLOAT(-1, 1); + } + else + { + VectorSubtract(inPlayer->pev->origin, theEntity->pev->origin, theDirection); + } + theDirection[2] = 0; + VectorNormalize(theDirection); + + // Project the speed orthogonal to the direction to the spawning player + // proj n a = (n . a)/||n||^2 * n + float n_dot_a = DotProduct(theDirection, theEntity->pev->velocity); + vec3_t parallelVec; + VectorScale(theDirection, n_dot_a, parallelVec); + VectorSubtract(theEntity->pev->velocity, theDirection, theEntity->pev->velocity); + + // Apply a pushback velocity away from the spawning player + VectorScale(theDirection, -200, theDirection); + theDirection[2] = 150; + + VectorAdd(theEntity->pev->velocity, theDirection, theEntity->pev->velocity); + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) +} + +void AvHSUBuildingJustCreated(AvHMessageID inBuildID, CBaseEntity* theBuilding, AvHPlayer* inPlayer) +{ + if((inBuildID == BUILD_RESOURCES) || (inBuildID == ALIEN_BUILD_RESOURCES)) + { + // Add it to team for performance reasons (so world doesn't have to be polled during AvHTeam::UpdateResources) + AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)theBuilding->pev->team); + ASSERT(theTeam); + theTeam->AddResourceTower(theBuilding->entindex()); + } + + // Don't expire weapons the commander drops + AvHBasePlayerWeapon* theBaseWeapon = dynamic_cast(theBuilding); + if(theBaseWeapon && (inPlayer->GetUser3() == AVH_USER3_COMMANDER_PLAYER)) + { + theBaseWeapon->SetGroundLifetime(-1); + } + + AvHBuildable* theBuildable = dynamic_cast(theBuilding); + if(theBuildable && inPlayer) + { + theBuildable->SetBuilder(inPlayer->entindex()); + } + + // If it was an alien tech building, look for a hive that doesn't support this tech and make it support it + if((inBuildID == ALIEN_BUILD_DEFENSE_CHAMBER) || (inBuildID == ALIEN_BUILD_MOVEMENT_CHAMBER) || (inBuildID == ALIEN_BUILD_SENSORY_CHAMBER)) + { + if(inPlayer) + { + AvHSUUpdateHiveTechology(inPlayer->GetTeam(), inBuildID); + } + } + + const char* theClassName = STRING(theBuilding->pev->classname); + if(inPlayer && theClassName) + { + inPlayer->LogPlayerAction("structure_built", theClassName); + } + + // Notify player and his teammates + if(!GetGameRules()->GetIsCombatMode()) + { + inPlayer->PlayHUDStructureNotification(inBuildID, theBuilding->pev->origin); + } +} + +// Gets the build count for the given message +int AvHSUGetStructureCount(AvHMessageID inMessageID) +{ + int theNumBuildings = 0; + switch (inMessageID) + { + case ALIEN_BUILD_OFFENSE_CHAMBER: + case ALIEN_BUILD_DEFENSE_CHAMBER: + case ALIEN_BUILD_SENSORY_CHAMBER: + case ALIEN_BUILD_MOVEMENT_CHAMBER: + case BUILD_INFANTRYPORTAL: + case BUILD_TURRET_FACTORY: + case BUILD_TURRET: + case BUILD_SIEGE: + case BUILD_ARMORY: + case BUILD_ARMSLAB: + case BUILD_PROTOTYPE_LAB: + case BUILD_OBSERVATORY: + case BUILD_PHASEGATE: + FOR_ALL_BASEENTITIES() + switch(theBaseEntity->pev->iuser3) + { + case AVH_USER3_DEFENSE_CHAMBER: + case AVH_USER3_MOVEMENT_CHAMBER: + case AVH_USER3_OFFENSE_CHAMBER: + case AVH_USER3_SENSORY_CHAMBER: + case AVH_USER3_INFANTRYPORTAL: + case AVH_USER3_TURRET_FACTORY: + case AVH_USER3_ADVANCED_TURRET_FACTORY: + case AVH_USER3_SIEGETURRET: + case AVH_USER3_TURRET: + case AVH_USER3_ARMORY: + case AVH_USER3_ADVANCED_ARMORY: + case AVH_USER3_ARMSLAB: + case AVH_USER3_PROTOTYPE_LAB: + case AVH_USER3_OBSERVATORY: + case AVH_USER3_PHASEGATE: + theNumBuildings++; + break; + } + END_FOR_ALL_BASEENTITIES(); + } + return theNumBuildings; +} + +CBaseEntity* AvHSUBuildTechForPlayer(AvHMessageID inBuildID, const Vector& inLocation, AvHPlayer* inPlayer) +{ + CBaseEntity* theEntity = NULL; + + if(!GetGameRules()->GetIsCombatMode() || AvHSHUGetIsCombatModeTech(inBuildID)) + { + char* theClassName; + + if(AvHSHUGetBuildTechClassName(inBuildID, theClassName)) + { + // Create without owner to fix solidity problems? + //theEntity = CBaseEntity::Create(theClassName, inLocation, theAngles, inPlayer->edict()); + theEntity = CBaseEntity::Create(theClassName, inLocation, AvHSUGetRandomBuildingAngles()); + if(theEntity) + { + // Set team + theEntity->pev->team = inPlayer->pev->team; + + // Set any team-wide upgrades + AvHTeam* theTeam = inPlayer->GetTeamPointer(); + ASSERT(theTeam); + theEntity->pev->iuser4 |= theTeam->GetTeamWideUpgrades(); + + AvHSUBuildingJustCreated(inBuildID, theEntity, inPlayer); + + if(!theEntity->IsInWorld() && GetGameRules()->GetIsCombatMode()) + { + //: okay, if for WHATEVER reason this isnt touched by a player, set it to remove in 2.5 seconds. + theEntity->SetThink(&CBaseEntity::SUB_Remove); + theEntity->pev->nextthink = gpGlobals->time + 2.5f; + + //: force them to touch it. + DispatchTouch(ENT(theEntity->pev), ENT(inPlayer->pev)); + } + + // Do special stuff for some buildings (special case scan so it doesn't "teleport in") + if(inBuildID != BUILD_SCAN) + { + //: play effect at player origin in combat, cause the item is in the middle of nowhere. + Vector vecOrigin = (theEntity->IsInWorld() && !GetGameRules()->GetIsCombatMode()) ? inLocation : inPlayer->pev->origin; + if( GetGameRules()->GetIsCombatMode() ) { + PLAYBACK_EVENT_FULL(0, inPlayer->edict(), gPhaseInEventID, 0, vecOrigin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); + } + else { + PLAYBACK_EVENT_FULL(0, 0, gPhaseInEventID, 0, vecOrigin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); + } + } + } + } + } + + return theEntity; +} + +void AvHSUExplodeEntity(CBaseEntity* inEntity, Materials inMaterial) +{ + CBreakable* theDebris = NULL; + theDebris = GetClassPtr((CBreakable*)NULL); + + UTIL_SetOrigin(VARS(theDebris->pev), inEntity->pev->origin); + + theDebris->pev->model = inEntity->pev->model; + + theDebris->SetMaterial(inMaterial); + + theDebris->Spawn(); + + theDebris->Die(); + + UTIL_Remove(theDebris); +} + +Vector AvHSUEyeToBodyVector(entvars_t* inEye, CBaseEntity* inTarget) +{ + Vector vecMid = inEye->origin + inEye->view_ofs; + Vector vecMidEnemy = inTarget->BodyTarget(vecMid); + Vector vecDirToEnemy = vecMidEnemy - vecMid; + return vecDirToEnemy; +} + +float AvHSUEyeToBodyDistance(entvars_t* inEye, CBaseEntity* inTarget) +{ + return AvHSUEyeToBodyVector(inEye, inTarget).Length(); +} + +float AvHSUEyeToBodyXYDistance(entvars_t* inEye, CBaseEntity* inTarget) +{ + return AvHSUEyeToBodyVector(inEye, inTarget).Length2D(); +} + +bool AvHSSUGetIsClassNameFadeable(const char* inClassName) +{ + bool theSuccess = false; + + if(/*FStrEq(inClassName, kesFuncDoor) || FStrEq(inClassName, "func_door_rotating") || FStrEq(inClassName, "momentary_door") ||*/ FStrEq(inClassName, kesSeethrough) || FStrEq(inClassName, kesSeethroughDoor)) + { + theSuccess = true; + } + + return theSuccess; +} + +void AvHSUPlayPhaseInEffect(int inFlags, CBaseEntity* inStartEntity, CBaseEntity* inEndEntity) +{ + // Play sound and implosion effect + EMIT_SOUND(ENT(inStartEntity->pev), CHAN_AUTO, kPhaseGateTransportSound, .8, ATTN_NORM); + + MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, inStartEntity->pev->origin); + WRITE_BYTE(TE_IMPLOSION); + WRITE_COORD(inStartEntity->pev->origin.x); + WRITE_COORD(inStartEntity->pev->origin.y); + WRITE_COORD(inStartEntity->pev->origin.z + 16); + WRITE_BYTE(255); + WRITE_BYTE(15); + WRITE_BYTE(4); + MESSAGE_END(); + + + // Play sound and particles + EMIT_SOUND(ENT(inEndEntity->pev), CHAN_AUTO, kPhaseGateTransportSound, .8, ATTN_NORM); + + PLAYBACK_EVENT_FULL(inFlags, inEndEntity->edict(), gTeleportEventID, 0, inEndEntity->pev->origin, inEndEntity->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); +} + +void AvHSUPlayRandomConstructionEffect(AvHPlayer* inPlayer, CBaseEntity* inConstructee) +{ + bool theIsMarine = inPlayer->GetIsMarine(); + float theVolume = .3f*AvHPlayerUpgrade::GetSilenceVolumeLevel(inPlayer->GetUser3(), inPlayer->pev->iuser4); + if(!theIsMarine) + { + gSoundListManager.PlaySoundInList(kAlienConstructionSoundList, inConstructee, CHAN_BODY, theVolume); + + // TODO: Play cool particle puff or something + } + else + { + gSoundListManager.PlaySoundInList(kMarineConstructionSoundList, inConstructee, CHAN_BODY, theVolume); + + // Play sparks every other time + if(RANDOM_LONG(0, 1) == 1) + { + // Emit sparks from most of the way up the turret + Vector theVector(inConstructee->pev->origin); + float theRandomFloat = RANDOM_FLOAT(0.5F, 1.0f); + float theHeight = theRandomFloat*(inConstructee->pev->absmax.z - inConstructee->pev->absmin.z); + theVector.z += theHeight; + UTIL_Sparks(theVector); + } + + } +} + +bool AvHSUPlayerCanBuild(entvars_t* inPev) +{ + bool thePlayerCanBuild = false; + + // If player is a marine or a builder-alien, he can build + if((inPev->iuser3 == AVH_USER3_MARINE_PLAYER) || (inPev->iuser3 == AVH_USER3_ALIEN_PLAYER2)) + { + thePlayerCanBuild = true; + } + + return thePlayerCanBuild; +} + +bool AvHSUPlayParticleEvent(const char* inParticleSystemName, const edict_t* inEdict, const Vector& inOrigin, int inEventFlags) +{ + bool theSuccess = false; + + // Look up particle system template by name + uint32 theTemplateIndex = 0; + if(gParticleTemplateList.GetTemplateIndexWithName(inParticleSystemName, theTemplateIndex)) + { + PLAYBACK_EVENT_FULL(inEventFlags, inEdict, gParticleEventID, 0, (float*)&inOrigin, (float*)&g_vecZero, 0.0f, 0.0, theTemplateIndex, 0, 0, 0); + + theSuccess = true; + } + + return theSuccess; +} + +void AvHSUPlayNumericEvent(float inNumber, const edict_t* inEdict, Vector& inOrigin, int inEventFlags, int inNumericEventType, int inTeamNumber) +{ + PLAYBACK_EVENT_FULL(0, inEdict, gNumericalInfoEventID, 0, inOrigin, (float *)&g_vecZero, inNumber, 0.0, inNumericEventType, inTeamNumber, 0, 0); +} + +void AvHSUPlayNumericEventAboveStructure(float inNumber, AvHBaseBuildable* inBuildable, int inNumericEventType) +{ + // Generate visual resource event + Vector theMinSize; + Vector theMaxSize; + AvHSHUGetSizeForTech(inBuildable->GetMessageID(), theMinSize, theMaxSize); + + Vector theStartPos = inBuildable->pev->origin; + theStartPos.z += theMaxSize.z; + + AvHSUPlayNumericEvent(inNumber, inBuildable->edict(), theStartPos, 0, inNumericEventType, inBuildable->pev->team); +} + +void AvHSURemoveAllEntities(const char* inClassName) +{ + FOR_ALL_ENTITIES(inClassName, CBaseEntity*); + UTIL_Remove(theEntity); + END_FOR_ALL_ENTITIES(inClassName); +} + +// Works for ammo and health only, for use in Combat mode. +void AvHSUResupplyFriendliesInRange(int inNumEntitiesToCreate, AvHPlayer* inPlayer, int inRange) +{ + // Create list of nearby eligible players + PlayerListType thePlayerList; + + thePlayerList.push_back(inPlayer); + + if(inRange > 0) + { + CBaseEntity* theEntity = NULL; + while ((theEntity = UTIL_FindEntityInSphere(theEntity, inPlayer->pev->origin, inRange)) != NULL) + { + const char* theClassName = STRING(theEntity->pev->classname); + if(!AvHSUGetIsExternalClassName(theClassName)) + { + AvHPlayer* thePlayer = dynamic_cast(theEntity); + if(thePlayer && thePlayer->GetIsRelevant() && (thePlayer->GetTeam() == inPlayer->GetTeam()) && (thePlayer != inPlayer)) + { + thePlayerList.push_back(thePlayer); + } + } + } + } + + // While there are more to supply + while(inNumEntitiesToCreate > 0) + { + // Pick the most eligible player, giving preference to creating player + int theRandomOffset = RANDOM_LONG(0, thePlayerList.size() - 1); + AvHPlayer* thePlayer = thePlayerList[theRandomOffset]; + + // Give player entity + //AvHSUBuildTechForPlayer(inMessageID, thePlayer->pev->origin, inPlayer); + + // : 1017 combat resupply amount + BOOL theHelpedPlayer = AvHHealth::GiveHealth(thePlayer, BALANCE_VAR(kPointsPerHealth)); + + if(!theHelpedPlayer) + { + theHelpedPlayer = AvHGenericAmmo::GiveAmmo(thePlayer); + } + + if(theHelpedPlayer) + { + // Play event for each person helped + PLAYBACK_EVENT_FULL(0, thePlayer->edict(), gPhaseInEventID, 0, thePlayer->pev->origin, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); + } + + // Decrement + inNumEntitiesToCreate--; + } +} + +bool AvHSUGetIsOftRepeatedAlert(AvHAlertType inAlertType) +{ + bool theIsOftRepeated = false; + + switch(inAlertType) + { + case ALERT_UNDER_ATTACK: + case ALERT_HIVE_DYING: + case ALERT_HIVE_DEFEND: + case ALERT_PLAYER_ENGAGE: + case ALERT_SENTRY_FIRING: + case ALERT_SENTRY_DAMAGED: + case ALERT_ORDER_NEEDED: + case ALERT_SOLDIER_NEEDS_AMMO: + case ALERT_SOLDIER_NEEDS_HEALTH: + theIsOftRepeated = true; + break; + } + + return theIsOftRepeated; +} + +bool AvHSUGetIsUrgentAlert(AvHAlertType inAlertType) +{ + bool theIsUrgent = false; + + switch(inAlertType) + { + // Very important and relatively rare + case ALERT_HIVE_COMPLETE: + case ALERT_NEW_TRAIT: + case ALERT_LOW_RESOURCES: + case ALERT_UNDER_ATTACK: + case ALERT_HIVE_DYING: + case ALERT_HIVE_DEFEND: + + // These must always be played because they don't have any notification on the commander UI + case ALERT_RESEARCH_COMPLETE: + case ALERT_UPGRADE_COMPLETE: + theIsUrgent = true; + break; + } + + return theIsUrgent; +} + +// Used to screen for non-NS entities (they can't use RTTI among others) +bool AvHSUGetIsExternalClassName(const char* inClassName) +{ + bool theIsExternal = false; + + if(inClassName && (!strncmp(inClassName, "adminmod_", 9))) + { + theIsExternal = true; + } + + return theIsExternal; +} + +bool AvHSUGetIsSubjectToVisibilityRules(CBaseEntity* inEntity) +{ + bool theIsSubjectToVis = false; + + //char theErrorString[256]; + const char* theEntityName = "unknown"; + if(inEntity && inEntity->pev) + { + const char* theTentativeEntityName = NULL; + theTentativeEntityName = STRING(inEntity->pev->classname); + if(theTentativeEntityName) + { + theEntityName = theTentativeEntityName; + } + } + + // Players + if(!strcmp(theEntityName, kAvHPlayerClassName)) + { + theIsSubjectToVis = true; + } + // All buildables + else if(GetHasUpgrade(inEntity->pev->iuser4, MASK_BUILDABLE)) + { + theIsSubjectToVis = true; + } + // Hives + else if(!strcmp(theEntityName, kesTeamHive)) + { + theIsSubjectToVis = true; + } + // Webs + else if(!strcmp(theEntityName, kesTeamWebStrand)) + { + theIsSubjectToVis = true; + } + // Visibility for particle systems + else if(!strcmp(theEntityName, kesParticles)) + { + theIsSubjectToVis = true; + } + else if(!strcmp(theEntityName, kesParticlesCustom)) + { + theIsSubjectToVis = true; + } + // Func resources + else if(!strcmp(theEntityName, kesFuncResource)) + { + theIsSubjectToVis = true; + } + else if(dynamic_cast(inEntity)) + { + theIsSubjectToVis = true; + } + + //sprintf(theErrorString, "Entity %s subject to vis: %d.\n", theEntityName, theIsSubjectToVis); + //ALERT(at_logged, theErrorString); + + return theIsSubjectToVis; +} + +int AvHSUGetNumHumansInGame(void) +{ + int theCount = 0; + + for(int theIndex = 1; theIndex <= gpGlobals->maxClients; theIndex++ ) + { + CBaseEntity* thePlayer = UTIL_PlayerByIndex(theIndex); + + if(thePlayer == NULL) + continue; + + + if(FNullEnt( thePlayer->pev ) ) + continue; + + + if(FStrEq(STRING(thePlayer->pev->netname), "" )) + continue; + + + if(FBitSet(thePlayer->pev->flags, FL_FAKECLIENT)) + continue; + + theCount++; + } + + return theCount; +} +void AvHSUFillScoreInfo(ScoreInfo &info, AvHPlayer *player) { + if ( player == NULL ) + return; + int theAuthMask = player->GetAuthenticationMask(); + int theTotalScore = player->GetScore() + player->pev->frags /*- this->m_iDeaths*/; + if(GetGameRules()->GetIsCombatMode()) + { + int theCurrentLevel = AvHPlayerUpgrade::GetPlayerLevel(player->GetExperience()); + theTotalScore += max((theCurrentLevel - 1), 0); + } + + + info.player_index = ENTINDEX(player->edict()); + info.score = theTotalScore; + info.frags = player->pev->frags; + info.deaths = player->m_iDeaths; + info.player_class = player->GetEffectivePlayerClass(); + info.auth = player->GetAuthenticationMask(); + info.team = GetGameRules()->GetTeamIndex(player->TeamID()); + info.health = (int)(player->pev->health); + + if ( GetGameRules()->GetIsCombatMode()) { + info.extra=player->GetExperienceLevel(); + } + else { + if ( player->GetIsAlien() ) { + info.extra=(int)player->GetResources(); + } + else { + info.extra=0; + // go through all of the weapons and make a list of the ones to pack + for ( int i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { + if ( player->m_rgpPlayerItems[i] ) { + + CBasePlayerItem* theItem = player->m_rgpPlayerItems[i]; + AvHBasePlayerWeapon *theWeapon=dynamic_cast(theItem); + while ( theItem ) { + ItemInfo ii; + theItem->GetItemInfo(&ii); + switch ( ii.iId ) { + case AVH_WEAPON_HMG: + info.extra |= WEAPON_HMG; + break; + case AVH_WEAPON_GRENADE_GUN: + info.extra |= WEAPON_GL; + break; + case AVH_WEAPON_SONIC: + info.extra |= WEAPON_SG; + break; + case AVH_WEAPON_MINE: + if ( theWeapon && theWeapon->GetIsCapableOfFiring() ) + info.extra |= WEAPON_MINE; + break; + case AVH_WEAPON_WELDER: + info.extra |= WEAPON_WELDER; + break; + } + theItem = theItem->m_pNext; + } + } + } + } + } +} + +AvHHive* AvHSUGetRandomActiveHive(AvHTeamNumber inTeam) +{ + AvHHive* theHive = NULL; + + int theNumActiveHives = 0; + vector theHiveList; + + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if(theEntity->GetIsActive() && (theEntity->GetTeamNumber() == inTeam)) + { + theHiveList.push_back(theEntity); + } + END_FOR_ALL_ENTITIES(kesTeamHive) + + if(theHiveList.size() > 0) + { + int theRandomHive = RANDOM_LONG(0, theHiveList.size()-1); + theHive = theHiveList[theRandomHive]; + } + + return theHive; +} + +AvHHive* AvHSUGetRandomActivateableHive(AvHTeamNumber inTeam) +{ + AvHHive* theHive = NULL; + + int theNumActiveHives = 0; + vector theHiveList; + + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + bool theCanBecomeActive = theEntity->CanBecomeActive(); + if(theCanBecomeActive /*&& (theEntity->GetTeamNumber() == inTeam)*/) + { + theHiveList.push_back(theEntity); + } + END_FOR_ALL_ENTITIES(kesTeamHive) + + if(theHiveList.size() > 0) + { + int theRandomHive = RANDOM_LONG(0, theHiveList.size()-1); + theHive = theHiveList[theRandomHive]; + } + + return theHive; +} + +int AvHSUGetWeaponStayTime() +{ + int theWeaponStayTime = BALANCE_VAR(kWeaponStayTime); + + if(GetGameRules()->GetIsCombatMode()) + { + theWeaponStayTime = BALANCE_VAR(kCombatWeaponStayTime); + } + + return theWeaponStayTime; +} + +void AvHSUResearchStarted(CBaseEntity* inResearchEntity, AvHMessageID inResearchingTech) +{ + if(inResearchingTech == RESEARCH_DISTRESSBEACON) + { + // Playback event for all, sending time it takes to finish + int theDistressBeaconTime = GetGameRules()->GetBuildTimeForMessageID(inResearchingTech); + + // Play sound at center of marine spawn + //Vector theSoundOrigin = GetGameRules()->GetSpawnAreaCenter((AvHTeamNumber)inResearchEntity->pev->team); + + PLAYBACK_EVENT_FULL(0, inResearchEntity->edict(), gDistressBeaconEventID, 0, inResearchEntity->pev->origin, (float *)&g_vecZero, 0.0, 0.0, theDistressBeaconTime, 0, 0, 0 ); + + // Play special siren + //EMIT_SOUND(inResearchEntity->edict(), CHAN_AUTO, kDistressBeaconSound, 1.0f, ATTN_IDLE); + } +} + +void AvHSUResearchComplete(CBaseEntity* inResearchEntity, AvHMessageID inResearchingTech) +{ + // Watch for various research messages + //if(inResearchingTech == RESEARCH_REINFORCEMENTS) + //{ + // AvHInfantryPortal* thePortal = dynamic_cast(inResearchEntity); + // This should never happen but don't crash right now while I fix the bug for the playtest + //ASSERT(thePortal); + //if(thePortal) + //{ + // thePortal->SetReinforcements(thePortal->GetReinforcements() + 1); + //} + //} + + if(inResearchingTech == RESOURCE_UPGRADE) + { + AvHResourceTower* theTower = dynamic_cast(inResearchEntity); + if(theTower) + { + theTower->Upgrade(); + } + // This should never happen but don't crash right now while I fix the bug for the playtest + //else + //{ + // ASSERT(false); + //} + } + else if(inResearchingTech == ARMORY_UPGRADE) + { + AvHArmory* theArmory = dynamic_cast(inResearchEntity); + if(theArmory) + { + theArmory->Upgrade(); + } + } + else if(inResearchingTech == TURRET_FACTORY_UPGRADE) + { + AvHTurretFactory* theTurretFactory = dynamic_cast(inResearchEntity); + if(theTurretFactory) + { + theTurretFactory->Upgrade(); + } + } + else if(inResearchingTech == RESEARCH_DISTRESSBEACON) + { + // Player distress sound effect? + + // Immediately respawn all marines at base! + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->pev->team == inResearchEntity->pev->team) + { + bool theSuccess = false; + + AvHPlayMode thePlayMode = theEntity->GetPlayMode(); + + // Respawn dead/waiting players + if((thePlayMode == PLAYMODE_REINFORCING) || (thePlayMode == PLAYMODE_AWAITINGREINFORCEMENT) || (!theEntity->IsAlive() && (thePlayMode == PLAYMODE_PLAYING))) + { + theEntity->SetPlayMode(PLAYMODE_PLAYING); + theSuccess = true; + } + // teleport existing players back + else if((thePlayMode == PLAYMODE_PLAYING) && theEntity->GetCanBeAffectedByEnemies()) + { + if ( GetGameRules()->CanPlayerBeacon(theEntity) ) + { + edict_t* theSpawnPoint = GetGameRules()->SelectSpawnPoint(theEntity); + + if(theSpawnPoint) + { + if ( VectorDistance(theSpawnPoint->v.origin, theEntity->pev->origin) > BALANCE_VAR(kDistressBeaconRange)) + { + theEntity->InitPlayerFromSpawn(theSpawnPoint); + + // Reset anything some things + theEntity->SetEnsnareState(false); + theEntity->SetIsStunned(false); + theSuccess = true; + } + } + } + } + + if(theSuccess) + { + // Play special phase effect/sound for player + int theFlags = 0; + AvHSUPlayPhaseInEffect(theFlags, theEntity, theEntity); + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + } + else if(inResearchingTech == RESEARCH_ELECTRICAL) + { + AvHBaseBuildable* theBuildable = dynamic_cast(inResearchEntity); + if(theBuildable) + { + // Only allow certain structures to be electrified (fix for exploit timing/selection exploit) + AvHMessageID theBuildableMessage = theBuildable->GetMessageID(); + switch(theBuildableMessage) + { + case BUILD_RESOURCES: + case BUILD_TURRET_FACTORY: + case TURRET_FACTORY_UPGRADE: + SetUpgradeMask(&theBuildable->pev->iuser4, MASK_UPGRADE_11); + break; + } + } + } +} + +template bool isMember(CBaseEntity* object) +{ + return (object != NULL) && (dynamic_cast(object) != NULL); +} + +//TODO: reimplememnt this functionality as a virtual function under CBaseBuildable to keep the +// information tied directly to the class that it describes, reducing total locations to track (KGP) +bool AvHSUGetIsResearchApplicable(CBaseEntity* inResearchEntity, AvHMessageID inResearchingTech) +{ + bool theIsApplicable = false; + + switch(inResearchingTech) + { + case RESOURCE_UPGRADE: + theIsApplicable = isMember(inResearchEntity); + break; + case ARMORY_UPGRADE: + case RESEARCH_GRENADES: + theIsApplicable = isMember(inResearchEntity); + break; + case TURRET_FACTORY_UPGRADE: + theIsApplicable = isMember(inResearchEntity); + break; + case RESEARCH_DISTRESSBEACON: + case RESEARCH_MOTIONTRACK: + case RESEARCH_PHASETECH: + theIsApplicable = isMember(inResearchEntity); + break; + case RESEARCH_ARMOR_ONE: + case RESEARCH_ARMOR_TWO: + case RESEARCH_ARMOR_THREE: + case RESEARCH_WEAPONS_ONE: + case RESEARCH_WEAPONS_TWO: + case RESEARCH_WEAPONS_THREE: + case RESEARCH_CATALYSTS: + theIsApplicable = isMember(inResearchEntity); + break; + case RESEARCH_HEAVYARMOR: + case RESEARCH_JETPACKS: + theIsApplicable = isMember(inResearchEntity); + break; + case RESEARCH_ELECTRICAL: + theIsApplicable = isMember(inResearchEntity) || isMember(inResearchEntity); + break; + } + return theIsApplicable; +} + +void AvHSUSetCollisionBoxFromSequence(entvars_t* inPev) +{ + ASSERT(inPev); + + studiohdr_t* theStudioHeader = (studiohdr_t*)GET_MODEL_PTR( ENT(inPev) ); + if(theStudioHeader == NULL) + { + ALERT(at_console, "AvHSUSetCollisionBoxFromSequence(): Invalid model\n"); + } + + mstudioseqdesc_t* theSeqDesc = (mstudioseqdesc_t *)((byte *)theStudioHeader + theStudioHeader->seqindex); + + Vector theMin = theSeqDesc[inPev->sequence].bbmin; + Vector theMax = theSeqDesc[inPev->sequence].bbmax; + + UTIL_SetSize(inPev, theMin, theMax); +} + + + +////========================================================================= +//// Returns 0 if the area around obj is safe to build in +//int CBaseEntity::CheckArea( CBaseEntity *pIgnore ) +//{ +// TraceResult tr; +// Vector vecOrg = pev->origin; +// +// // Check the origin +// int iContents = UTIL_PointContents(vecOrg); +// if ( iContents != CONTENT_EMPTY && iContents != CONTENT_WATER ) +// return CAREA_BLOCKED; +// +// Vector vecIgnoreOrg = pIgnore->pev->origin; +// // Get the player's origin irrelevant of crouching +// if ( pIgnore->pev->flags & FL_DUCKING ) +// { +// vecIgnoreOrg = vecIgnoreOrg + (VEC_DUCK_HULL_MIN - +// VEC_HULL_MIN); +// } +// // Trace a hull +// UTIL_TraceHull( vecIgnoreOrg, pev->origin, ignore_monsters, +// large_hull, edict(), &tr ); +// CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); +// if (tr.flFraction != 1 || tr.fAllSolid == 1) +// return CAREA_BLOCKED; +// +// // Check for solid entities in the area +// CBaseEntity *pEnt = NULL; +// while ( (pEnt = UTIL_FindEntityInSphere( pEnt, pev->origin, 48 )) != +// NULL ) +// { +// // If it's not the engineer, and its a solid entity, fail +// if (pEnt != pIgnore && pEnt != this && pEnt->pev->solid > +// SOLID_TRIGGER) +// return CAREA_BLOCKED; +// } +// +// // Cycle through all the Nobuild zones in the map and make sure this +// isn't in one of them +// CBaseEntity *pNoBuild = UTIL_FindEntityByClassname( NULL, +// "func_nobuild" ); +// while ( pNoBuild ) +// { +// // Check to see if we're building in this zone +// if ( vecOrg.x >= pNoBuild->pev->mins.x && vecOrg.y >= +// pNoBuild->pev->mins.y && vecOrg.z >= pNoBuild->pev->mins.z && +// vecOrg.x <= pNoBuild->pev->maxs.x && +// vecOrg.y <= pNoBuild->pev->maxs.y && vecOrg.z <= pNoBuild->pev->maxs.z ) +// return CAREA_NOBUILD; +// +// pNoBuild = UTIL_FindEntityByClassname( pNoBuild, +// "func_nobuild" ); +// } +// +// // Check below +// UTIL_TraceLine( vecOrg, vecOrg + Vector(0,0,-64), +// dont_ignore_monsters, edict(), &tr ); +// if ( tr.flFraction == 1.0 ) +// return CAREA_BLOCKED; +// +// return CAREA_CLEAR; +//} + +CBaseEntity* AvHSUGetEntityFromIndex(int inEntityIndex) +{ + CBaseEntity* theEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex)); + return theEntity; +} + + +CGrenade* AvHSUShootServerGrenade(entvars_t* inOwner, Vector inOrigin, Vector inVelocity, float inTime, bool inHandGrenade) +{ + CGrenade* theGrenade = CGrenade::ShootExplosiveTimed(inOwner, inOrigin, inVelocity, inTime, inHandGrenade ? NS_DMG_NORMAL : NS_DMG_BLAST ); + ASSERT(theGrenade); + + theGrenade->pev->team = inOwner->team; + + // Set it as a marine item so it gets damage upgrades + theGrenade->pev->iuser3 = AVH_USER3_MARINEITEM; + theGrenade->pev->iuser4 = inOwner->iuser4; + + if(inHandGrenade) + { + theGrenade->pev->classname = MAKE_STRING("handgrenade"); + } + +// if(!GetGameRules()->GetDrawInvisibleEntities()) +// { +// theGrenade->pev->effects |= EF_NODRAW; +// } + + return theGrenade; +} + +void AvHSUKnockPlayerAbout(CBaseEntity* inAttcker, CBaseEntity* inVictim, int inForce) +{ + if(inAttcker && inVictim->IsPlayer()) + { + AvHPlayer* theVictim = dynamic_cast(inVictim); + + //Dont _ever_ knock around teammates + if(inAttcker->pev->team != inVictim->pev->team && theVictim && theVictim->GetCanBeAffectedByEnemies()) + { + Vector VecDir; + VecDir = inVictim->pev->origin - inAttcker->pev->origin; + VecDir = VecDir.Normalize(); + + if(theVictim->GetIsMarine() && theVictim->GetHasHeavyArmor()) + inForce = inForce * 0.50; + + //If they are in the air they have another 50% less knockback (to prevent them from becoming superplayer + if(!(theVictim->pev->flags & FL_ONGROUND) || !theVictim->pev->groundentity) + { + inForce = inForce * 0.50; + } + // Reduce knockback to 1/3 if user is ducked and on the ground + else if ((theVictim->pev->bInDuck) || (theVictim->pev->flags & FL_DUCKING)) + { + inForce = inForce * 0.33; + } + + inVictim->pev->punchangle.z = -18; + inVictim->pev->punchangle.x = 5; + inVictim->pev->velocity = VecDir * max(0.0f, 120 - VecDir.Length() ) * inForce/75; + } + } + + /*AvHPlayer* thePlayer = dynamic_cast(inEntity); + if(thePlayer && thePlayer->GetCanBeAffectedByEnemies()) + { + inEntity->pev->punchangle.z = -18; + inEntity->pev->punchangle.x = 5; + if(!(inEntity->pev->flags & FL_ONGROUND) || !inEntity->pev->groundentity) // if the player is in midair, don't use full force + { + inEntity->pev->velocity = inEntity->pev->velocity - gpGlobals->v_right*(inForce/10); + } + else + { + inEntity->pev->velocity = inEntity->pev->velocity - gpGlobals->v_right*inForce; + } + } + }*/ +} + +void AvHGetLineBounds(const Vector& vecStart, const Vector& vecEnd, Vector& outMins, Vector& outMaxs) +{ + + outMins = vecStart; + outMaxs = vecStart; + + for (int i = 0; i < 3; ++i) + { + + if (vecStart[i] < outMins[i]) + { + outMins[i] = vecStart[i]; + } + + if (vecStart[i] > outMaxs[i]) + { + outMaxs[i] = vecStart[i]; + } + + if (vecEnd[i] < outMins[i]) + { + outMins[i] = vecEnd[i]; + } + + if (vecEnd[i] > outMaxs[i]) + { + outMaxs[i] = vecEnd[i]; + } + + } + + // Increase the box by a bit since most structure hulls are not very + // accurately sized. + + const float kBoundingBoxPadding = 100; + + outMins[0] -= kBoundingBoxPadding; + outMins[1] -= kBoundingBoxPadding; + outMins[2] -= kBoundingBoxPadding; + + outMaxs[0] += kBoundingBoxPadding; + outMaxs[1] += kBoundingBoxPadding; + outMaxs[2] += kBoundingBoxPadding; + +} + +/** + * Drop in replacement for UTIL_TraceLine which works properly for + */ +void AvHTraceLine(const Vector& vecStart, const Vector& vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr) +{ + + // Trace against the world. + + UTIL_TraceLine(vecStart, vecEnd, dont_ignore_monsters, pentIgnore, ptr); + + // Trace against the players/structures. + + if (igmon != ignore_monsters) + { + + vec3_t theRayOrigin; + vec3_t theRayDirection; + + VectorCopy(vecStart, theRayOrigin); + VectorSubtract(vecEnd, vecStart, theRayDirection); + + vec3_t theLineMins; + vec3_t theLineMaxs; + + AvHGetLineBounds(vecStart, vecEnd, theLineMins, theLineMaxs); + + // Get the bounding box for the line. + + const int kMaxNumEntities = 128; + CBaseEntity* pList[kMaxNumEntities]; + + int theNumEntities = UTIL_EntitiesInBox(pList, kMaxNumEntities, theLineMins, theLineMaxs, (FL_CLIENT | FL_MONSTER)); + + for (int i = 0; i < theNumEntities; ++i) + { + + edict_t* theEdict = pList[i]->edict(); + + // : 0000941 -- added check to remove testing of spectators + if ((!(pList[i]->pev->iuser1 > 0 || pList[i]->pev->flags & FL_SPECTATOR)) && theEdict != pentIgnore) +// if (theEdict != pentIgnore) + { + + float t = NS_TraceLineAgainstEntity(pList[i]->entindex(), gpGlobals->time, theRayOrigin, theRayDirection); + + if (t != AVH_INFINITY && t < ptr->flFraction) + { + ptr->flFraction = t; + ptr->pHit = theEdict; + ptr->iHitgroup = 0; // NS doesn't use hit groups. + } + + } + + } + + // Compute the end position. + + if (ptr->flFraction != 1.0) + { + VectorMA(theRayOrigin, ptr->flFraction, theRayDirection, ptr->vecEndPos); + } + + } + +} + + +void AvHSUServerTraceBullets(const Vector& inStart, const Vector& inEnd, IGNORE_MONSTERS inIgnoreMonsters, edict_t* inIgnoreEdict, TraceResult& outTraceResult, bool& outProtected) +{ + outProtected = false; + + // This is the old way that doesn't take into account skulk rotation. + // UTIL_TraceLine(inStart, inEnd, inIgnoreMonsters, /*dont_ignore_glass,*/ inIgnoreEdict, &outTraceResult); + + // TEMP removed the skulk hitboxes since it's too risky for the LAN. + // : 0000573 + // this was commented out meaning that it was just stock tracelines, not using Max M's superb hitbox collision code. + // Now *all* hitboxes perform as expected and the crouched fade can be shot pretty much anywhere on the model + // (allowing for about a 5% visual disparity) + AvHTraceLine(inStart, inEnd, inIgnoreMonsters, /*dont_ignore_glass,*/ inIgnoreEdict, &outTraceResult); + // : + CBaseEntity* theEntityHit = CBaseEntity::Instance(outTraceResult.pHit); + + // If we hit an entity that's hidden in the umbra, return that we didn't hit anything + if(theEntityHit) + { + if(GetHasUpgrade(theEntityHit->pev->iuser4, MASK_UMBRA)) + { + const int theUmbraEffectiveness = BALANCE_VAR(kUmbraEffectiveness); + + // Block most shots but not all + if(RANDOM_LONG(0, theUmbraEffectiveness) != 0) + { + outProtected = true; + } + } + else + { + // Check if bullets pass through an umbra cloud before hitting target + // If so, don't hit the target + } + } +} + +void AvHSUUpdateHiveTechology(AvHTeamNumber inTeamNumber, AvHMessageID inBuildID) +{ + AvHHive* theHive = NULL; + bool theTechnologyAlreadySupported = false; + + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if(theEntity && theEntity->GetIsActive() && (theEntity->pev->team == (int)inTeamNumber)) + { + AvHMessageID theTechnology = theEntity->GetTechnology(); + if(theTechnology == inBuildID) + { + theTechnologyAlreadySupported = true; + break; + } + else if(theTechnology == MESSAGE_NULL) + { + if(!theHive) + { + theHive = theEntity; + } + } + } + END_FOR_ALL_ENTITIES(kesTeamHive); + + if(!theTechnologyAlreadySupported && theHive) + { + theHive->SetTechnology(inBuildID); + } +} + +bool gIsDebugging = false; + +bool AvHSUGetIsDebugging() +{ + return gIsDebugging; +} + +void AvHSUSetIsDebugging(bool inState) +{ + gIsDebugging = inState; +} + +void AvHSUAddDebugPoint(float inX, float inY, float inZ) +{ + CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(1)); + AvHPlayer* thePlayer = dynamic_cast(theBaseEntity); + if(thePlayer) + { + thePlayer->AddDebugEnemyBlip(inX, inY, inZ); + } +} + +AvHUser3 AvHSUGetGroupTypeFromSelection(const EntityListType& inEntityListType) +{ + // By default, it's a generic marine + AvHUser3 theUnifiedUser3 = AVH_USER3_NONE; + + // Loop through all the entities + bool theFirstTime = true; + bool theSuccess = false; + + for(EntityListType::const_iterator theIter = inEntityListType.begin(); theIter != inEntityListType.end(); theIter++, theSuccess) + { + theSuccess = true; + + CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theIter)); + if(theBaseEntity) + { + // If all the user3s are the same, then use that + AvHUser3 theCurrentUser3 = (AvHUser3)(theBaseEntity->pev->iuser3); + if(theFirstTime || (theCurrentUser3 == theUnifiedUser3)) + { + theUnifiedUser3 = theCurrentUser3; + theFirstTime = false; + } + else + { + theSuccess = false; + } + } + else + { + theSuccess = false; + } + } + + if(!theSuccess) + { + theUnifiedUser3 = AVH_USER3_MARINE_PLAYER; + } + + return theUnifiedUser3; +} + +void AvHSURemoveEntityFromHotgroupsAndSelection(int inEntityIndex) +{ + CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inEntityIndex)); + if(theBaseEntity) + { + // Get team + AvHTeam* theTeam = GetGameRules()->GetTeam((AvHTeamNumber)theBaseEntity->pev->team); + if(theTeam) + { + // Remove entity from any hotgroups on team + for(int i=0; i < kNumHotkeyGroups; i++) + { + EntityListType theHotGroup = theTeam->GetGroup(i); + + EntityListType::iterator theIter = std::find(theHotGroup.begin(), theHotGroup.end(), inEntityIndex); + if(theIter != theHotGroup.end()) + { + // Remove it and update it + theHotGroup.erase(theIter); + + theTeam->SetGroup(i, theHotGroup); + } + } + + // Now get commander and remove entity from their selection + AvHPlayer* theCommander = theTeam->GetCommanderPlayer(); + if(theCommander) + { + theCommander->RemoveSelection(inEntityIndex); + } + } + } +} + +string AvHSUGetLocationText(CBaseEntity* inEntity) +{ + // Add in location name to chat message + string theLocationString(" "); + + AvHPlayer* thePlayer = dynamic_cast(inEntity); + if(thePlayer && thePlayer->GetIsRelevant()) + { + string theLocationName; + if(AvHSHUGetNameOfLocation(GetGameRules()->GetInfoLocations(), inEntity->pev->origin, theLocationName)) + { + theLocationString = theLocationName; + } + } + + return theLocationString; +} + +void AvHSUCreateUmbraCloud(vec3_t inOrigin, AvHTeamNumber inTeamNumber, CBaseEntity* inCreatingEntity) +{ + // Fire umbra cloud event + PLAYBACK_EVENT_FULL(0, inCreatingEntity->edict(), gUmbraCloudEventID, 0, inOrigin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + + // Create the umbra cloud that will provide gameplay effects + AvHUmbraCloud* theUmbra = GetClassPtr((AvHUmbraCloud*)NULL ); + theUmbra->Spawn(); + + VectorCopy(inOrigin, theUmbra->pev->origin); + + // Set team and owner + theUmbra->pev->owner = inCreatingEntity->pev->owner; + theUmbra->pev->team = inTeamNumber; +} + +// Explode with force (players only) +void AvHSUExplosiveForce(const Vector& inOrigin, int inRadius, float inForceScalar, const CBaseEntity* inAttacker, const CBaseEntity* inIgnorePlayer) +{ + CBaseEntity* theEntity = NULL; + while ((theEntity = UTIL_FindEntityInSphere(theEntity, inOrigin, inRadius)) != NULL) + { + const char* theClassName = STRING(theEntity->pev->classname); + if(!AvHSUGetIsExternalClassName(theClassName)) + { + AvHPlayer* thePlayer = dynamic_cast(theEntity); + float theScalar = 1.0f; + if(thePlayer + && thePlayer->GetCanBeAffectedByEnemies() + && GetGameRules()->CanEntityDoDamageTo(inAttacker, thePlayer, &theScalar) + && (thePlayer != inIgnorePlayer) + && thePlayer->pev->team != inAttacker->pev->team) + { + // Apply outward force to player + Vector theForceVector; + VectorSubtract(thePlayer->pev->origin, inOrigin, theForceVector); + + float theForceScalar = inForceScalar; + float theDistanceToExplosion = theForceVector.Length(); + theDistanceToExplosion = min(max(theDistanceToExplosion, 0.0f), (float)inRadius); + + float thePercentForce = (1.0f - (theDistanceToExplosion/(float)inRadius)); + theForceScalar *= thePercentForce; + + theForceVector.Normalize(); + + // : 0000771 + if((!(thePlayer->pev->flags & FL_ONGROUND) || !thePlayer->pev->groundentity) || + ((thePlayer->pev->bInDuck) || (thePlayer->pev->flags & FL_DUCKING))) + { + theForceScalar *= 0.33f; + } + + Vector thePreVelocity = thePlayer->pev->velocity; + float theDamageForce = thePlayer->DamageForce(theForceScalar); + Vector thePostVelocity = (thePlayer->pev->velocity + theForceVector *( theDamageForce )); + + // check and cap horisontal speed + float theMaxHorisontalSpeed = BALANCE_VAR(kExplodeMaxHorisontalSpeed); + Vector theHorisontalSpeed; + theHorisontalSpeed[0] = thePostVelocity[0]; + theHorisontalSpeed[1] = thePostVelocity[1]; + theHorisontalSpeed[2] = 0; + float theHorisontalSpeedLength = theHorisontalSpeed.Length(); + float theHorisontalFactor = theMaxHorisontalSpeed / theHorisontalSpeedLength; + if (theHorisontalSpeedLength > theMaxHorisontalSpeed) { + thePostVelocity[0] *= theHorisontalFactor; + thePostVelocity[1] *= theHorisontalFactor; + } + + // then vertical speed + float theMaxVerticalSpeed = BALANCE_VAR(kExplodeMaxVerticalSpeed); + if (fabs(thePostVelocity[2]) > theMaxVerticalSpeed) + { + thePostVelocity[2] = thePostVelocity[2]/fabs(thePostVelocity[2]) * theMaxVerticalSpeed; + } + + // assign new speed + thePlayer->pev->velocity = thePostVelocity; + // + } + } + } +} + +int AvHSUGetValveHull(int inHull) +{ + // Convert "our" hull into VALVe hull + int theHull = inHull; + switch(inHull) + { + case 0: + theHull = 1; + break; + case 1: + theHull = 3; + break; + case 2: + theHull = 0; + break; + case 3: + theHull = 2; + break; + default: + ASSERT(false); + break; + } + + return theHull; +} + +bool AvHSUGetIsEnoughRoomForHull(const vec3_t& inCenter, int inHull, edict_t* inIgnoreEntity, bool inIgnorePlayers, bool inIgnoreEntities) +{ + ASSERT(inHull >= 0); + ASSERT(inHull <= 3); + + // Check hull against the world + if(pmove && (pmove->numphysent > 0)) + { + + model_s* theWorldModel = pmove->physents[0].model; + + // Convert "our" hull into VALVe hull. + + int theHull = AvHSUGetValveHull(inHull); + const hull_t* theHullPtr = theWorldModel->hulls + theHull; + + int thePointContents = NS_PointContents(theHullPtr, theHullPtr->firstclipnode, (float*)&inCenter); + if(thePointContents == CONTENTS_SOLID) + { + return false; + } + } + + if (!inIgnoreEntities) + { + + // If that succeeds, check hull size against entities (above check doesn't take ents into account) + + // Use input hull to get min and max vectors + vec3_t theMin; + vec3_t theMax; + + // Scale it down just a tad so we can morph on ramps. The shared movement code should get us unstuck in these minor cases + float kScalar = 1;//.85f; + + switch(inHull) + { + case 0: + theMin = HULL0_MIN*kScalar; + theMax = HULL0_MAX*kScalar; + break; + case 1: + theMin = HULL1_MIN*kScalar; + theMax = HULL1_MAX*kScalar; + break; + case 2: + theMin = HULL2_MIN*kScalar; + theMax = HULL2_MAX*kScalar; + break; + case 3: + theMin = HULL3_MIN*kScalar; + theMax = HULL3_MAX*kScalar; + break; + } + + return AvHSHUGetIsAreaFree(inCenter, theMin, theMax, inIgnoreEntity, inIgnorePlayers); + + } + + return true; + +} + +void AvHSUPrintDevMessage(const string& inMessage, bool inForce) +{ + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + + #ifndef DEBUG + bool theSendMessage = inForce; + if( !theSendMessage ) + { + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(theEntity->edict())); + if(thePlayer && thePlayer->GetIsMember(PLAYERAUTH_DEVELOPER) ) + { + theSendMessage = true; + } + } + if( !theSendMessage ) + { + continue; + } + #endif + + ClientPrint(theEntity->pev, HUD_PRINTNOTIFY, inMessage.c_str()); + + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); +} + +bool AvHCheckLineOfSight(const Vector& vecStart, const Vector& vecEnd, edict_t* pentIgnore, IGNORE_MONSTERS igmon, edict_t* pEntTarget) +{ + + TraceResult tr; + UTIL_TraceLine(vecStart, vecEnd, igmon, ignore_glass, pentIgnore, &tr); + + edict_t* theLastHit = NULL; + + while ( 1 ) + { + + bool theSeeThrough = false; + + if (tr.flFraction < 1 && tr.pHit != NULL) + { + if (tr.pHit->v.rendermode != kRenderNormal) + { + theSeeThrough = true; + } + else if ( AvHSSUGetIsClassNameFadeable(STRING(tr.pHit->v.classname)) && (int)tr.pHit->v.fuser2 != 255 ) + { + theSeeThrough = true; + } + } + + if (!theSeeThrough) + { + break; + } + + edict_t* temp = tr.pHit; + UTIL_TraceLine(tr.vecEndPos, vecEnd, igmon, ignore_glass, tr.pHit, &tr); + + // To avoid an infinite loop if multiple entities are overlapping. + + if (theLastHit == tr.pHit) + { + break; + } + + theLastHit = temp; + + } + + if (pEntTarget != NULL) + { + + const char* s = STRING(tr.pHit->v.classname); + return tr.flFraction < 1 && tr.pHit == pEntTarget; + } + else + { + return tr.flFraction == 1 && !tr.fAllSolid; + } + +} + +char *ns_cvar_string(const cvar_t *cvar) +{ + if (!cvar || !cvar->string) + { + return 0; + } + return cvar->string; +} +int ns_cvar_int(const cvar_t *cvar) +{ + if (!cvar || !cvar->string) + { + return 0; + } + return atoi(cvar->string); +} +float ns_cvar_float(const cvar_t *cvar) +{ + if (!cvar || !cvar->string) + { + return 0; + } + return atoi(cvar->string); +} + diff --git a/main/source/mod/AvHServerUtil.h b/main/source/mod/AvHServerUtil.h index 72d3df2..07647aa 100644 --- a/main/source/mod/AvHServerUtil.h +++ b/main/source/mod/AvHServerUtil.h @@ -136,9 +136,11 @@ bool AvHSUGetIsValidAuthID(const string& inAuthID); string AvHSUGetPlayerAuthIDString(edict_t* inPlayer); #endif +void AvHSUPushbackPlayersTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor); void AvHSUKillPlayersTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor); void AvHSUKillBuildablesTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor); void AvHSUBuildingJustCreated(AvHMessageID inBuildID, CBaseEntity* theBuilding, AvHPlayer* inPlayer); +int AvHSUGetStructureCount(AvHMessageID inMessageID); CBaseEntity* AvHSUBuildTechForPlayer(AvHMessageID inBuildID, const Vector& inLocation, AvHPlayer* inPlayer); @@ -169,6 +171,7 @@ bool AvHSUGetIsExternalClassName(const char* inClassName); bool AvHSUGetIsSubjectToVisibilityRules(CBaseEntity* inEntity); int AvHSUGetNumHumansInGame(void); +void AvHSUFillScoreInfo(ScoreInfo &info, AvHPlayer *player); AvHHive* AvHSUGetRandomActiveHive(AvHTeamNumber inTeam); AvHHive* AvHSUGetRandomActivateableHive(AvHTeamNumber inTeam); diff --git a/main/source/mod/AvHServerVariables.h b/main/source/mod/AvHServerVariables.h index 83bf456..8c0aca0 100644 --- a/main/source/mod/AvHServerVariables.h +++ b/main/source/mod/AvHServerVariables.h @@ -66,9 +66,44 @@ #include "common/cvardef.h" -extern cvar_t avh_tournamentmode; +extern cvar_t avh_blockscripts; +extern cvar_t avh_combattime; +extern cvar_t *avh_cheats; +extern cvar_t avh_defaultteam; +extern cvar_t avh_drawdamage; +extern cvar_t avh_footsteps; +extern cvar_t avh_gravity; +extern cvar_t avh_ironman; +extern cvar_t avh_ironmantime; +extern cvar_t avh_networkdebug; +extern cvar_t avh_team1damagepercent; +extern cvar_t avh_team2damagepercent; +extern cvar_t avh_team3damagepercent; +extern cvar_t avh_team4damagepercent; +extern cvar_t avh_testing; +extern cvar_t timelimit; +extern cvar_t avh_tournamentmode; +extern cvar_t avh_trainingmode; +extern cvar_t avh_structurelimit; +extern cvar_t avh_latejointime; +extern cvar_t avh_bulletcam; +extern cvar_t avh_logdetail; +extern cvar_t *violence_hblood; +extern cvar_t *violence_ablood; +extern cvar_t *violence_hgibs; +extern cvar_t *violence_agibs; +extern cvar_t avh_killdelay; +extern cvar_t *showtriggers; + +extern cvar_t *showtriggers; + +char *ns_cvar_string(const cvar_t *cvar); +int ns_cvar_int(const cvar_t *cvar); +float ns_cvar_float(const cvar_t *cvar); + // Variables + #define kvTournamentMode "mp_tournamentmode" #define kvTrainingMode "mp_trainingmode" #define kvDrawDamage "mp_drawdamage" @@ -86,6 +121,7 @@ extern cvar_t avh_tournamentmode; #define kvTeam2DamagePercent "mp_team2damagepercent" #define kvTeam3DamagePercent "mp_team3damagepercent" #define kvTeam4DamagePercent "mp_team4damagepercent" +#define kvStructureLimit "sv_structurelimit" #define kvSpawnInvulnerableTime "mp_spawninvulnerabletime" #define kvVoteCastTime "mp_votecasttime" #define kvVoteDownTime "mp_votedowntime" diff --git a/main/source/mod/AvHSharedTypes.h b/main/source/mod/AvHSharedTypes.h index f31447a..e60a3d0 100644 --- a/main/source/mod/AvHSharedTypes.h +++ b/main/source/mod/AvHSharedTypes.h @@ -58,12 +58,21 @@ typedef struct ScoreInfo_s int player_index; int score; int frags; + int extra; int deaths; int player_class; int auth; int team; + int health; } ScoreInfo; +#define WEAPON_SG 0x1 +#define WEAPON_GL 0x2 +#define WEAPON_HMG 0x4 +#define WEAPON_MINE 0x8 +#define WEAPON_WELDER 0x10 + + typedef struct WeaponList_s { string weapon_name; diff --git a/main/source/mod/AvHSharedUtil.cpp b/main/source/mod/AvHSharedUtil.cpp index 5147e70..7dbfed1 100644 --- a/main/source/mod/AvHSharedUtil.cpp +++ b/main/source/mod/AvHSharedUtil.cpp @@ -1,3904 +1,3911 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHSharedUtil.cpp $ -// $Date: 2002/11/12 22:39:25 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHSharedUtil.cpp,v $ -// Revision 1.33 2002/11/12 22:39:25 Flayra -// - Logging changes for Psychostats compatibility -// -// Revision 1.32 2002/10/24 21:42:32 Flayra -// - Don't give waypoint to selected troops when scanning -// -// Revision 1.31 2002/10/19 21:19:49 Flayra -// - Debugging info for linux -// -// Revision 1.30 2002/10/16 01:07:26 Flayra -// - Added official sizes that HL supports (ugh) -// - Added utility function for drawing range of ghost building, but it's unused -// -// Revision 1.29 2002/10/03 19:07:40 Flayra -// - Hives can never be blocked by func_resources -// -// Revision 1.28 2002/09/25 20:50:58 Flayra -// - Allow small items to be built on entities (health can be dropped right on players) -// -// Revision 1.27 2002/09/23 22:31:40 Flayra -// - Updated team 0 colors so dictation can be read in readyroom -// - Draw range for prototype lab, observatory and sensory chamber -// - Alien building rings -// - Don't allow non-resource buildings built too close to func_resources -// - Added heavy armor and jetpacks -// -// Revision 1.26 2002/09/09 20:06:43 Flayra -// - New observatory artwork -// -// Revision 1.25 2002/08/31 18:01:03 Flayra -// - Work at VALVe -// -// Revision 1.24 2002/08/16 02:47:06 Flayra -// - Fixed bug where not all the entities were iterated through -// - Support for ring-drawing (but not for all entities) -// -// Revision 1.23 2002/08/09 00:51:11 Flayra -// - Cleaned up useless special casing of tracetangible, fixed some problems where buildings could be built on players -// -// Revision 1.22 2002/08/02 21:50:05 Flayra -// - Made more general, by detecting all entities, not just players -// -// Revision 1.21 2002/07/24 18:45:43 Flayra -// - Linux and scripting changes -// -// Revision 1.20 2002/07/23 17:26:49 Flayra -// - Siege don't require a nearby turret factory, only add built buildings for range detection, build problems on client (point contents failing on rough surfaces) -// -// Revision 1.19 2002/07/08 17:17:44 Flayra -// - Reworked colors, moved functions into server util -// -// Revision 1.18 2002/07/01 21:46:39 Flayra -// - Added support for generic building ranges, fixed morphing problems -// -// Revision 1.17 2002/06/25 18:18:00 Flayra -// - Renamed buildings, better is-area-free detection -// -// Revision 1.16 2002/06/03 16:58:13 Flayra -// - Renamed weapons factory and armory, hive is now a buildable -// -// Revision 1.15 2002/05/28 18:13:43 Flayra -// - Changed "entity blocked by whatever" message to be logged for PR/screenshot purposes -// -// Revision 1.14 2002/05/23 02:33:20 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== - -#ifdef AVH_SERVER -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "types.h" -#endif - -#ifdef AVH_CLIENT -#include "cl_dll/hud.h" -#include "cl_dll/cl_util.h" -#endif - -#include "mod/AvHSharedUtil.h" -#include "mod/AvHSelectionHelper.h" -#include "mod/AvHConstants.h" - -#ifdef AVH_SERVER -#include "mod/AvHPlayer.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHEntities.h" -#include "mod/AvHWeldable.h" -#include "mod/AvHGamerules.h" -#include "dlls/cfuncwall.h" -//#include "common/com_model.h" -//int NS_PointContents(const hull_t *hull, int num, float p[3]); -#endif - -#include "pm_shared/pm_defs.h" -#include "pm_shared/pm_shared.h" - -#ifdef AVH_CLIENT -#include "pm_shared/pm_debug.h" -//extern DebugPointListType gTriDebugLocations; -//extern DebugPointListType gSquareDebugLocations; -#endif - -#include "util/MathUtil.h" -#include "util/STLUtil.h" - -#include "common/vector_util.h" - -#ifdef AVH_CLIENT -#include "cl_dll/eventscripts.h" - -#include "common/r_efx.h" -#include "common/event_api.h" -#include "common/event_args.h" -#include "cl_dll/in_defs.h" -#endif - -#include "common/com_model.h" - -#include "mod/AvHSpecials.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "dlls/turretconst.h" -#include "mod/AvHMarineWeaponConstants.h" -#include "mod/AvHHulls.h" -#include "mod/AvHAlienEquipmentConstants.h" - -#include "mod/CollisionUtil.h" -/* -bool NS_BoxesOverlap(float origin1[3], float size1[3], float origin2[3], float size2[3]); -int NS_PointContents(const hull_t *hull, int num, float p[3]); -int NS_BoxContents(const hull_t *hull, int num, float mins[3], float maxs[3]); -int NS_GetValveHull(int inHull); -void NS_TraceLine(const hull_t* hull, float srcPoint[3], float dstPoint[3], trace_t* trace); -*/ -#include -extern playermove_t* pmove; -vec3_t AvHSHUGetRealLocation(const vec3_t& inLocation, const vec3_t& inMinBox, const vec3_t& inMaxBox); - - -vec3_t gPMDebugPoint; - -int kTeamColors[iNumberOfTeamColors][3] = -{ - { 255, 255, 255 }, // White (default) - -// { 125, 165, 210 }, // Blue (marine 1) - { 0, 153, 255 }, // Blue (marine 1) - -// { 255, 170, 0 }, // HL orange (alien 1) - { 255, 160, 0 }, // HL orange (alien 1) - - { 145, 215, 140 }, // Green (marine 2) - { 200, 90, 70 }, // Red (alien 2) - { 255, 255, 255 } // White (spectator) -}; - -float kFTeamColors[iNumberOfTeamColors][3] = -{ - { kTeamColors[0][0] / 255.0f, kTeamColors[0][1] / 255.0f, kTeamColors[0][2] / 255.0f }, - { kTeamColors[1][0] / 255.0f, kTeamColors[1][1] / 255.0f, kTeamColors[1][2] / 255.0f }, - { kTeamColors[2][0] / 255.0f, kTeamColors[2][1] / 255.0f, kTeamColors[2][2] / 255.0f }, - { kTeamColors[3][0] / 255.0f, kTeamColors[3][1] / 255.0f, kTeamColors[3][2] / 255.0f }, - { kTeamColors[4][0] / 255.0f, kTeamColors[4][1] / 255.0f, kTeamColors[4][2] / 255.0f }, - { kTeamColors[5][0] / 255.0f, kTeamColors[5][1] / 255.0f, kTeamColors[5][2] / 255.0f } -}; - - -// Official allowed sizes -// {0, 0, 0 } { 0, 0, 0 } 0x0x0 -// { -16, -16, -18 } { 16, 16, 18 } 32x32x36 -// { -16, -16, -36 } { 16, 16, 36 } 32x32x72 -// { -32, -32, -32 } { 32, 32, 32 } 64x64x64 - -//#define kBuilding1MinSize Vector(-14.33, -14.84, 0.02) -////#define kBuilding1MaxSize Vector(21.61, 14.86, 66.9686) -//#define kBuilding1MaxSize Vector(14.33, 14.86, 66.9686) -// -//#define kBuilding2MinSize Vector(-25.0, -25.0, 0.02) -//#define kBuilding2MaxSize Vector(25.0, 25.0, 66.9686) - -#define kResourceMinSize Vector(-16.0, -16.0, 0.0) -#define kResourceMaxSize Vector(16.0, 16.0, 66.9486) - -// Tried 100, 110, still jitters. Shrink tower down a bit? -#define kAlienResourceMinSize Vector(-16.0, -16.0, 0.0) -#define kAlienResourceMaxSize Vector(16.0, 16.0, 80.7443) - -//physent_t* AvHSUGetEntity(int inPhysIndex) -//{ -// physent_t* theTarget = NULL; -// int i = 0; -// -// if(inPhysIndex > 0) -// { -// #ifdef AVH_CLIENT -// gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); -// -// // Store off the old count -// //gEngfuncs.pEventAPI->EV_PushPMStates(); -// -// // Now add in all of the players. -// gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); -// -// for (i = 0; i < MAX_PHYSENTS; i++) -// //for (i = 0; i < pmove->numphysent; i++) -// { -// physent_t* thePotentialCandidate = gEngfuncs.pEventAPI->EV_GetPhysent(i); -// if ( thePotentialCandidate && (thePotentialCandidate->info == inPhysIndex)) -// { -// theTarget = &pmove->physents[i]; -// break; -// } -// } -// -// //gEngfuncs.pEventAPI->EV_PopPMStates(); -// #endif -// -// // Check phys entities on server -// #ifdef AVH_SERVER -// for (i = 0; i < MAX_PHYSENTS; i++) -// //for (i = 0; i < pmove->numphysent; i++) -// { -// if ( pmove->physents[i].info == inPhysIndex ) -// { -// theTarget = &pmove->physents[i]; -// break; -// } -// } -// -// //CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); -// #endif -// } -// -// return theTarget; -//} - -const char* AvHSHUGetClassNameFromUser3(AvHUser3 inUser3) -{ - const char* theClassName = ""; - - switch(inUser3) - { - case AVH_USER3_MARINE_PLAYER: - theClassName = "soldier"; - break; - - case AVH_USER3_COMMANDER_PLAYER: - theClassName = "commander"; - break; - - case AVH_USER3_ALIEN_PLAYER1: - theClassName = "skulk"; - break; - - case AVH_USER3_ALIEN_PLAYER2: - theClassName = "gorge"; - break; - - case AVH_USER3_ALIEN_PLAYER3: - theClassName = "lerk"; - break; - - case AVH_USER3_ALIEN_PLAYER4: - theClassName = "fade"; - break; - - case AVH_USER3_ALIEN_PLAYER5: - theClassName = "onos"; - break; - - case AVH_USER3_ALIEN_EMBRYO: - theClassName = "gestate"; - break; - } - - return theClassName; -} - - -// Pass in -1 to get all entities with a non-zero iuser3 -void AvHSHUGetEntities(int inUser3, EntityListType& outEntities) -{ - #ifdef AVH_SERVER - FOR_ALL_BASEENTITIES(); - if((theBaseEntity->pev->iuser3 == inUser3) || ((inUser3 == -1) && (theBaseEntity->pev->iuser3 > 0))) - { - outEntities.push_back(theBaseEntity->entindex()); - } - END_FOR_ALL_BASEENTITIES(); - - #endif - - #ifdef AVH_CLIENT - - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - - // Store off the old count - gEngfuncs.pEventAPI->EV_PushPMStates(); - - // Now add in all of the players. - gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); - - physent_t* theEntity = NULL; - int theNumEnts = pmove->numphysent; - for (int i = 0; i < theNumEnts; i++) - { - theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(i); - if((theEntity->iuser3 == inUser3) || ((inUser3 == -1) && (theEntity->iuser3 > 0))) - { - outEntities.push_back(i); - } - } - - gEngfuncs.pEventAPI->EV_PopPMStates(); - #endif -} - -bool AvHSHUGetEntityLocation(int inEntity, vec3_t& outLocation) -{ - bool theSuccess = false; - - #ifdef AVH_SERVER - edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity); - if(pEdict && !pEdict->free) - { - CBaseEntity* theEntity = CBaseEntity::Instance(pEdict); - if(theEntity) - { - outLocation = AvHSHUGetRealLocation(theEntity->pev->origin, theEntity->pev->mins, theEntity->pev->maxs); - theSuccess = true; - } - } - #endif - - #ifdef AVH_CLIENT - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - - // Store off the old count - gEngfuncs.pEventAPI->EV_PushPMStates(); - - // Now add in all of the players. - gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); - - physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity); - - gEngfuncs.pEventAPI->EV_PopPMStates(); - - if(theEntity) - { - outLocation = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs); - theSuccess = true; - } - #endif - - return theSuccess; -} - - -bool AvHSHUGetEntityIUser3(int inEntity, AvHUser3& outIUser3) -{ - bool theSuccess = false; - - #ifdef AVH_SERVER - edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity); - if(pEdict && !pEdict->free) - { - CBaseEntity* theEntity = CBaseEntity::Instance(pEdict); - if(theEntity) - { - outIUser3 = (AvHUser3)theEntity->pev->iuser3; - theSuccess = true; - } - } -#endif - -#ifdef AVH_CLIENT - // Store off the old count - gEngfuncs.pEventAPI->EV_PushPMStates(); - - // Now add in all of the players. - gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); - - physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity); - if(theEntity) - { - outIUser3 = (AvHUser3)theEntity->iuser3; - theSuccess = true; - } - gEngfuncs.pEventAPI->EV_PopPMStates(); - -#endif - return theSuccess; -} - - -bool AvHSHUGetEntityIUser4(int inEntity, int& outIUser4) -{ - bool theSuccess = false; - - #ifdef AVH_SERVER - edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity); - if(pEdict && !pEdict->free) - { - CBaseEntity* theEntity = CBaseEntity::Instance(pEdict); - if(theEntity) - { - outIUser4 = theEntity->pev->iuser4; - theSuccess = true; - } - } -#endif - -#ifdef AVH_CLIENT - physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity); - if(theEntity) - { - outIUser4 = theEntity->iuser4; - theSuccess = true; - } -#endif - - return theSuccess; -} - - -vec3_t AvHSHUGetRealLocation(const vec3_t& inLocation, const vec3_t& inMinBox, const vec3_t& inMaxBox) -{ - vec3_t outLocation; - VectorCopy(inLocation, outLocation); - - if((outLocation.x == outLocation.y) && (outLocation.y == outLocation.z) && (outLocation.z == 0.0f)) - { - outLocation.x = (inMinBox.x + inMaxBox.x)/2.0f; - outLocation.y = (inMinBox.y + inMaxBox.y)/2.0f; - outLocation.z = (inMinBox.z + inMaxBox.z)/2.0f; - } - return outLocation; -} - -// Returns range of user3, whatever that means. Returns -1 for no meaningful range. -int AvHSHUGetDrawRangeForUser3(AvHUser3 inUser3) -{ - int theRange = -1; - - switch(inUser3) - { - case AVH_USER3_COMMANDER_STATION: - theRange = BALANCE_VAR(kCommandStationBuildDistance); - break; - - case AVH_USER3_FUNC_RESOURCE: - theRange = BALANCE_VAR(kResourceTowerBuildDistanceTolerance); - break; - - case AVH_USER3_TURRET_FACTORY: - case AVH_USER3_ADVANCED_TURRET_FACTORY: - theRange = BALANCE_VAR(kTurretFactoryBuildDistance); - break; - - case AVH_USER3_ARMORY: - case AVH_USER3_ADVANCED_ARMORY: - theRange = BALANCE_VAR(kArmoryBuildDistance); - break; - - case AVH_USER3_TURRET: - theRange = BALANCE_VAR(kTurretRange); - break; - - case AVH_USER3_SIEGETURRET: - // TODO: Figure out a way to return minimum range also? - theRange = BALANCE_VAR(kSiegeTurretRange); - break; - - case AVH_USER3_PROTOTYPE_LAB: - theRange = BALANCE_VAR(kArmorDropRange); - break; - - case AVH_USER3_OBSERVATORY: - theRange = BALANCE_VAR(kObservatoryXYDetectionRadius); - break; - - case AVH_USER3_SENSORY_CHAMBER: - theRange = BALANCE_VAR(kSensoryChamberRange); - break; - } - - return theRange; -} - -int AvHSHUGetDrawRangeForMessageID(AvHMessageID inMessageID) -{ - AvHUser3 theUser3 = AVH_USER3_NONE; - - switch(inMessageID) - { - case BUILD_COMMANDSTATION: - theUser3 = AVH_USER3_COMMANDER_STATION; - break; - - case BUILD_TURRET: - theUser3 = AVH_USER3_TURRET; - break; - - case BUILD_SIEGE: - theUser3 = AVH_USER3_SIEGETURRET; - break; - - case BUILD_OBSERVATORY: - theUser3 = AVH_USER3_OBSERVATORY; - break; - - case BUILD_TURRET_FACTORY: - theUser3 = AVH_USER3_TURRET_FACTORY; - break; - - case BUILD_PROTOTYPE_LAB: - theUser3 = AVH_USER3_PROTOTYPE_LAB; - break; - - case BUILD_ARMORY: - theUser3 = AVH_USER3_ARMORY; - break; - } - - return AvHSHUGetDrawRangeForUser3(theUser3); -} - - -// Specify whether we draw rings for this entity or not, and draw them at different sizes for aesthetics. The scalar might go away when the -// artwork all has the correct bounding boxes. -bool AvHSHUGetDrawRingsForUser3(AvHUser3 inUser3, float& outScalar) -{ - bool theDrawRings = false; - float theScalar = 1.0f; - - switch(inUser3) - { - case AVH_USER3_MARINE_PLAYER: - theScalar = .9f; - theDrawRings = true; - break; - - case AVH_USER3_RESTOWER: - theScalar = 1.6f; - theDrawRings = true; - break; - - case AVH_USER3_INFANTRYPORTAL: - theScalar = 1.35f; - theDrawRings = true; - break; - - case AVH_USER3_ARMORY: - case AVH_USER3_ADVANCED_ARMORY: - theScalar = 1.35f; - theDrawRings = true; - break; - - case AVH_USER3_COMMANDER_STATION: - theScalar = 1.4f; - theDrawRings = true; - break; - - case AVH_USER3_PHASEGATE: - theScalar = 2.0f; - theDrawRings = true; - break; - - case AVH_USER3_OBSERVATORY: - theScalar = .9f; - theDrawRings = true; - break; - - case AVH_USER3_TURRET_FACTORY: - case AVH_USER3_ADVANCED_TURRET_FACTORY: - theScalar = 1.6f; - theDrawRings = true; - break; - - case AVH_USER3_TURRET: - theScalar = .7f; - theDrawRings = true; - break; - - case AVH_USER3_NUKEPLANT: - theScalar = 1.5f; - theDrawRings = true; - break; - - case AVH_USER3_ARMSLAB: - case AVH_USER3_PROTOTYPE_LAB: - case AVH_USER3_CHEMLAB: - case AVH_USER3_MEDLAB: - case AVH_USER3_SIEGETURRET: - theScalar = 1.3f; - theDrawRings = true; - break; - - // Alien buildings - case AVH_USER3_DEFENSE_CHAMBER: - case AVH_USER3_OFFENSE_CHAMBER: - case AVH_USER3_MOVEMENT_CHAMBER: - case AVH_USER3_SENSORY_CHAMBER: - case AVH_USER3_ALIENRESTOWER: - theScalar = 1.3f; - theDrawRings = true; - break; - - case AVH_USER3_ALIEN_PLAYER1: - theScalar = .6f; - theDrawRings = true; - break; - case AVH_USER3_ALIEN_PLAYER2: - theScalar = 1.0f; - theDrawRings = true; - break; - case AVH_USER3_ALIEN_PLAYER3: - theScalar = 1.0; - theDrawRings = true; - break; - case AVH_USER3_ALIEN_PLAYER4: - theScalar = 1.3f; - theDrawRings = true; - break; - case AVH_USER3_ALIEN_PLAYER5: - theScalar = 1.2f; - theDrawRings = true; - break; - } - - if(theDrawRings) - { - outScalar = theScalar; - } - - return theDrawRings; -} - -bool AvHSHUGetBuildRegions(AvHMessageID inMessageID, EntityListType& outEntities, IntList& outRanges, float& outZAdjustment, bool& outSnapToLocation) -{ - bool theFoundTech = false; - - typedef vector User3ListType; - User3ListType theUser3s; - - outZAdjustment = 0.0f; - outSnapToLocation = false; - - switch(inMessageID) - { - case BUILD_RESOURCES: - case ALIEN_BUILD_RESOURCES: - theUser3s.push_back(AVH_USER3_FUNC_RESOURCE); - outZAdjustment = (kFuncResourceMaxSize.z - kFuncResourceMinSize.z); - outSnapToLocation = true; - break; - - case BUILD_INFANTRYPORTAL: - theUser3s.push_back(AVH_USER3_COMMANDER_STATION); - break; - - case BUILD_TURRET: - theUser3s.push_back(AVH_USER3_TURRET_FACTORY); - theUser3s.push_back(AVH_USER3_ADVANCED_TURRET_FACTORY); - break; - - case BUILD_SIEGE: - theUser3s.push_back(AVH_USER3_ADVANCED_TURRET_FACTORY); - break; - - case BUILD_MINES: - case BUILD_WELDER: - case BUILD_SHOTGUN: - theUser3s.push_back(AVH_USER3_ARMORY); - theUser3s.push_back(AVH_USER3_ADVANCED_ARMORY); - break; - - case BUILD_HMG: - case BUILD_GRENADE_GUN: - theUser3s.push_back(AVH_USER3_ADVANCED_ARMORY); - break; - - case BUILD_HEAVY: - case BUILD_JETPACK: - theUser3s.push_back(AVH_USER3_PROTOTYPE_LAB); - break; - } - - if(theUser3s.size() > 0) - { - for(User3ListType::iterator theUser3Iter = theUser3s.begin(); theUser3Iter != theUser3s.end(); theUser3Iter++) - { - EntityListType theEntities; - AvHSHUGetEntities(*theUser3Iter, theEntities); - - for(EntityListType::iterator theEntityIter = theEntities.begin(); theEntityIter != theEntities.end(); theEntityIter++) - { - // Only add buildings that are fully built - int theIuser4 = 0; - if(AvHSHUGetEntityIUser4(*theEntityIter, theIuser4)) - { - if(!GetHasUpgrade(theIuser4, MASK_BUILDABLE)) - { - outEntities.push_back(*theEntityIter); - outRanges.push_back(AvHSHUGetDrawRangeForUser3(*theUser3Iter)); - } - } - } - } - theFoundTech = true; - } - - return theFoundTech; -} - - -// tankefugl: 0000291 -- allows listed structures to be dropped on resource towers -bool AvHSHUGetIsDroppableOnRTs(AvHMessageID inMessageID) -{ - switch (inMessageID) - { - case BUILD_HEALTH: - case BUILD_AMMO: - case BUILD_MINES: - case BUILD_WELDER: - case BUILD_SHOTGUN: - case BUILD_HMG: - case BUILD_GRENADE_GUN: - case BUILD_CAT: - case BUILD_HEAVY: - case BUILD_JETPACK: - case BUILD_RESOURCES: - case ALIEN_BUILD_RESOURCES: - case ALIEN_BUILD_HIVE: - return true; - default: - return false; - } -} -// :tankefugl - -bool AvHSHUGetIsMarineStructure(AvHMessageID inMessageID) -{ - - switch (inMessageID) - { - - case BUILD_INFANTRYPORTAL: - case BUILD_RESOURCES: - case BUILD_TURRET_FACTORY: - case BUILD_ARMSLAB: - case BUILD_PROTOTYPE_LAB: - case BUILD_ARMORY: - case BUILD_NUKE_PLANT: - case BUILD_OBSERVATORY: - case BUILD_PHASEGATE: - case BUILD_TURRET: - case BUILD_SIEGE: - case BUILD_COMMANDSTATION: - return true; - default: - return false; - - } - -} - -bool AvHSHUGetIsMarineStructure(AvHUser3 inUser3) -{ - - switch (inUser3) - { - case AVH_USER3_COMMANDER_STATION: - case AVH_USER3_TURRET_FACTORY: - case AVH_USER3_ARMORY: - case AVH_USER3_ADVANCED_ARMORY: - case AVH_USER3_ARMSLAB: - case AVH_USER3_PROTOTYPE_LAB: - case AVH_USER3_OBSERVATORY: - case AVH_USER3_CHEMLAB: - case AVH_USER3_MEDLAB: - case AVH_USER3_NUKEPLANT: - case AVH_USER3_TURRET: - case AVH_USER3_SIEGETURRET: - case AVH_USER3_RESTOWER: - case AVH_USER3_INFANTRYPORTAL: - case AVH_USER3_PHASEGATE: - case AVH_USER3_ADVANCED_TURRET_FACTORY: - return true; - default: - return false; - } - -} - -void AvHSHUGetMinBuildRadiusViolations(AvHMessageID inMessageID, vec3_t& inLocation, EntityListType& outViolations) -{ - - // Enforce a minimum build radius for marine structures. - - if (AvHSHUGetIsMarineStructure(inMessageID)) - { - EntityListType theEntities; - AvHSHUGetEntities(-1, theEntities); - - vec3_t theMinSize, theMaxSize; - - AvHSHUGetSizeForTech(inMessageID, theMinSize, theMaxSize); - float theMaxRadius1 = max(-min(theMinSize.x, theMinSize.y), max(theMaxSize.x, theMaxSize.y)); - - for (EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++) - { - - AvHUser3 theUser3; - AvHSHUGetEntityIUser3(*theIter, theUser3); - - bool theEntityCouldBlock = AvHSHUGetIsMarineStructure(theUser3); - if(inMessageID != BUILD_RESOURCES) - { - theEntityCouldBlock |= (theUser3 == AVH_USER3_FUNC_RESOURCE); - } - - bool theEntityIsSolid = false; - -#ifdef AVH_SERVER - edict_t* theEntity = INDEXENT(*theIter); - - if(theEntity) - theEntityIsSolid = (theEntity->v.solid == SOLID_BBOX); -#endif - -#ifdef AVH_CLIENT - - physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theIter); - - if(theEntity) - theEntityIsSolid = (theEntity->solid == SOLID_BBOX); - -#endif - // joev: 0000291 - // It's possible to place "on" marines if you're offset a little from center. This code and - // associated changes below and in AvHHudRender.cpp is to enforce a build distance around players - // in the same way as buildings to prevent this exploit. - if (theUser3 == AVH_USER3_MARINE_PLAYER) - { - theEntityIsSolid = true; - theEntityCouldBlock = true; - } - - if (theEntityCouldBlock && theEntityIsSolid) - { - AvHSHUGetSizeForUser3(theUser3, theMinSize, theMaxSize); - float theMaxRadius2 = max(max(theMinSize.x, theMaxSize.x), max(theMinSize.y, theMaxSize.y)); - - vec3_t theLocation; - if(AvHSHUGetEntityLocation(*theIter, theLocation)) - { - vec3_t theXYInLocation = inLocation; - vec3_t theXYTheLocation = theLocation; - - theXYInLocation.z = 0; - theXYTheLocation.z = 0; - - float theDistance = VectorDistance((float*)&theXYInLocation, (float*)&theXYTheLocation); - // joev: 0000291 - // It's possible to place "on" marines if you're offset a little from center. This code and - // associated changes above and in AvHHudRender.cpp is to enforce a build distance around players - // in the same way as buildings to prevent this exploit. - float theMinMarineBuildDistance; - if (theUser3 == AVH_USER3_MARINE_PLAYER) { - theMinMarineBuildDistance = BALANCE_VAR(kMinMarinePlayerBuildDistance); - } - else - { - theMinMarineBuildDistance = BALANCE_VAR(kMinMarineBuildDistance); - } - // :joev - if (theDistance < theMinMarineBuildDistance + theMaxRadius1 + theMaxRadius2) - { - outViolations.push_back(*theIter); - } - } - } - } - } -} - -bool AvHSHUGetAreSpecialBuildingRequirementsMet(AvHMessageID inMessageID, vec3_t& inLocation) -{ - bool theRequirementsMet = false; - EntityListType theEntities; - IntList theDistanceRequirements; - float theZAdjustment = 0.0f; - bool theSnapToLocation = false; - - if(inMessageID == ALIEN_BUILD_HIVE) - { - // Look for inactive hives within radius - EntityListType theEntities; - - // Look for a unoccupied hive spot within range - AvHSHUGetEntities(AVH_USER3_HIVE, theEntities); - - for(EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++) - { - vec3_t theLocation; - if(AvHSHUGetEntityLocation(*theIter, theLocation)) - { - // Set z's equal to check 2D distance only - inLocation.z = theLocation.z; - float theDistance = VectorDistance((float*)&inLocation, (float*)&theLocation); - - if(theDistance <= kHiveXYDistanceTolerance) - { - // Make sure this hive isn't already active - #ifdef AVH_SERVER - CBaseEntity* theEntity = AvHSUGetEntityFromIndex(*theIter); - ASSERT(theEntity); - if(theEntity->pev->team == 0) - { - #endif - - theRequirementsMet = true; - inLocation = theLocation; - - #ifdef AVH_SERVER - } - #endif - - break; - } - } - } - } - else if(AvHSHUGetBuildRegions(inMessageID, theEntities, theDistanceRequirements, theZAdjustment, theSnapToLocation)) - { - ASSERT(theEntities.size() == theDistanceRequirements.size()); - vec3_t theBestLocation; - - int i = 0; - float theClosestDistance = kMaxMapDimension; - for(EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++, i++) - { - vec3_t theLocation; - if(AvHSHUGetEntityLocation(*theIter, theLocation)) - { - // Only check xy distance - vec3_t theXYInLocation = inLocation; - vec3_t theXYTheLocation = theLocation; - theXYInLocation.z = 0; - theXYTheLocation.z = 0; - - float theDistance = VectorDistance((float*)&theXYInLocation, (float*)&theXYTheLocation); - - int theDistanceRequirement = theDistanceRequirements[i]; - if((theDistance <= theDistanceRequirement) || (theDistanceRequirement == -1)) - { - // Pick the closest one, in case there are multiples in range - if(theDistance < theClosestDistance) - { - theClosestDistance = theDistance; - VectorCopy(theLocation,theBestLocation); - theRequirementsMet = true; - } - } - } - } - if(theRequirementsMet && theSnapToLocation) - { - inLocation = theBestLocation; - inLocation.z += theZAdjustment; - } - } - else - { - theRequirementsMet = true; - } - - EntityListType theBuildRadiusViolations; - AvHSHUGetMinBuildRadiusViolations(inMessageID, inLocation, theBuildRadiusViolations); - - if (theBuildRadiusViolations.size() > 0) - { - theRequirementsMet = false; - } - - // Anti-llama/newbie tactic: don't allow non-resource buildings to be placed such that they block access to nozzles - // Make sure generic building isn't being placed on top of resource nozzles - // tankefugl: 0000291 - // allow equipment, rts and hives to be dropped around nodes - if(AvHSHUGetIsDroppableOnRTs(inMessageID) == false) - { - // :tankefugl - // If building is too close to an empty nozzle, don't allow it - float theResourceBuildingRadius, theTotalMinRadius; - vec3_t theMinSize, theMaxSize, theMinRadius, theFlattenedInLocation, theLocation; - - theFlattenedInLocation[0] = inLocation[0]; - theFlattenedInLocation[1] = inLocation[1]; - theFlattenedInLocation[2] = 0; - - theResourceBuildingRadius = 60; - - EntityListType theEntities; - AvHSHUGetEntities(AVH_USER3_FUNC_RESOURCE,theEntities); - if(AvHSHUGetSizeForTech(inMessageID,theMinSize,theMaxSize)) - { - EntityListType::iterator end = theEntities.end(); - for(EntityListType::iterator current = theEntities.begin(); current < end; ++current) - { - if(AvHSHUGetEntityLocation(*current,theLocation)) - { - //flatten to 2 dimensions - theLocation[2] = 0; - - //space = radius of both buildings combined - theTotalMinRadius = theResourceBuildingRadius; - theTotalMinRadius += max(-min(theMinSize.x,theMinSize.y),max(theMaxSize.x,theMaxSize.y)); - - if(VectorDistance((float*)&theFlattenedInLocation,(float*)&theLocation) < theTotalMinRadius) - { - theRequirementsMet = false; - break; - } - } - } - } - } - - return theRequirementsMet; -} - -bool AvHSHUGetBuildTechClassName(AvHMessageID inMessageID, char*& outClassName) -{ - bool theSuccess = true; - - switch(inMessageID) - { - // Buildings - case BUILD_RESOURCES: - outClassName = kwsResourceTower; - break; - - //case BUILD_REINFORCEMENTS: - // outClassName = kwsInfantryPortal; - // break; - - case BUILD_INFANTRYPORTAL: - outClassName = kwsInfantryPortal; - break; - - case BUILD_COMMANDSTATION: - outClassName = kwsTeamCommand; - break; - - case BUILD_TURRET_FACTORY: - outClassName = kwsTurretFactory; - break; - - case BUILD_ARMSLAB: - outClassName = kwsArmsLab; - break; - - case BUILD_PROTOTYPE_LAB: - outClassName = kwsPrototypeLab; - break; - - case BUILD_ARMORY: - outClassName = kwsArmory; - break; - - case ARMORY_UPGRADE: - outClassName = kwsAdvancedArmory; - break; - - case BUILD_NUKE_PLANT: - outClassName = kwsNukePlant; - break; - - case BUILD_OBSERVATORY: - outClassName = kwsObservatory; - break; - - case BUILD_SCAN: - outClassName = kwsScan; - break; - - case BUILD_PHASEGATE: - outClassName = kwsPhaseGate; - break; - - case BUILD_TURRET: - outClassName = kwsDeployedTurret; - break; - - case BUILD_SIEGE: - outClassName = kwsSiegeTurret; - break; - - // Equipment - case BUILD_HEALTH: - outClassName = kwsHealth; - break; - - case BUILD_CAT: - outClassName = kwsCatalyst; - break; - - case BUILD_JETPACK: - outClassName = kwsJetpack; - break; - - case BUILD_HEAVY: - outClassName = kwsHeavyArmor; - break; - - case BUILD_AMMO: - outClassName = kwsGenericAmmo; - break; - - case BUILD_WELDER: - outClassName = kwsWelder; - break; - - case BUILD_MINES: - outClassName = kwsMine; - break; - - case BUILD_SHOTGUN: - outClassName = kwsShotGun; - break; - - case BUILD_HMG: - outClassName = kwsHeavyMachineGun; - break; - - case BUILD_NUKE: - outClassName = kwsNuke; - break; - - case BUILD_GRENADE_GUN: - outClassName = kwsGrenadeGun; - break; - - //case BUILD_MEDKIT: - // break; - - case ALIEN_BUILD_RESOURCES: - outClassName = kwsAlienResourceTower; - break; - - case ALIEN_BUILD_OFFENSE_CHAMBER: - outClassName = kwsOffenseChamber; - break; - - case ALIEN_BUILD_DEFENSE_CHAMBER: - outClassName = kwsDefenseChamber; - break; - - case ALIEN_BUILD_SENSORY_CHAMBER: - outClassName = kwsSensoryChamber; - break; - - case ALIEN_BUILD_MOVEMENT_CHAMBER: - outClassName = kwsMovementChamber; - break; - - case ALIEN_BUILD_HIVE: - outClassName = kesTeamHive; - break; - - default: - theSuccess = false; - break; - } - - return theSuccess; -} - -bool AvHSHUGetResearchTechName(AvHMessageID inResearchID, char*& outResearchTechName) -{ - bool theSuccess = true; - - switch(inResearchID) - { - case RESEARCH_ELECTRICAL: - outResearchTechName = "research_electrical"; - break; - - case RESEARCH_ARMOR_ONE: - outResearchTechName = "research_armorl1"; - break; - - case RESEARCH_ARMOR_TWO: - outResearchTechName = "research_armorl2"; - break; - - case RESEARCH_ARMOR_THREE: - outResearchTechName = "research_armorl3"; - break; - - case RESEARCH_WEAPONS_ONE: - outResearchTechName = "research_weaponsl1"; - break; - - case RESEARCH_WEAPONS_TWO: - outResearchTechName = "research_weaponsl2"; - break; - - case RESEARCH_WEAPONS_THREE: - outResearchTechName = "research_weaponsl3"; - break; - - case ARMORY_UPGRADE: - outResearchTechName = "research_advarmory"; - break; - - case TURRET_FACTORY_UPGRADE: - outResearchTechName = "research_advturretfactory"; - break; - - case RESEARCH_JETPACKS: - outResearchTechName = "research_jetpacks"; - break; - - case RESEARCH_HEAVYARMOR: - outResearchTechName = "research_heavyarmor"; - break; - - case RESEARCH_DISTRESSBEACON: - outResearchTechName = "research_distressbeacon"; - break; - - case RESEARCH_HEALTH: - outResearchTechName = "research_health"; - break; - - case RESEARCH_CATALYSTS: - outResearchTechName = "research_catalysts"; - break; - - case MESSAGE_CANCEL: - outResearchTechName = "research_cancel"; - break; - - case RESEARCH_MOTIONTRACK: - outResearchTechName = "research_motiontracking"; - break; - - case RESEARCH_PHASETECH: - outResearchTechName = "research_phasetech"; - break; - - case RESEARCH_GRENADES: - outResearchTechName = "research_grenades"; - break; - - default: - theSuccess = false; - break; - } - - return theSuccess; -} - - -bool AvHSHUGetCenterPositionForGroup(int inGroupNumber, float* inPlayerOrigin, float* outCenterPosition) -{ - bool theSuccess = false; - vec3_t thePosition; - float theX, theY; - - #ifdef AVH_CLIENT - theSuccess = gHUD.GetCenterPositionForGroup(inGroupNumber, thePosition); - theX = thePosition[0]; - theY = thePosition[1]; - #endif - - #ifdef AVH_SERVER - - // Loop through players, find the closest player to inPlayerOrigin, to see which player is being predicted. Is there a better way? - AvHPlayer* theClosestPlayer = NULL; - float theClosestDistance = 10000; - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - float theDistance = VectorDistance(theEntity->pev->origin, inPlayerOrigin); - if(theDistance < theClosestDistance) - { - theClosestPlayer = theEntity; - theClosestDistance = theDistance; - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - if(theClosestPlayer) - { - theSuccess = theClosestPlayer->GetCenterPositionForGroup(inGroupNumber, theX, theY); - } - #endif - - if(theSuccess) - { - outCenterPosition[0] = theX; - outCenterPosition[1] = theY; - } - - return theSuccess; -} - - -bool AvHSHUGetIsBuilding(AvHMessageID inMessageID) -{ - bool theIsBuilding = false; - - switch(inMessageID) - { - // Buildings - case BUILD_RESOURCES: - case BUILD_INFANTRYPORTAL: - case BUILD_COMMANDSTATION: - case BUILD_TURRET_FACTORY: - case BUILD_ARMSLAB: - case BUILD_PROTOTYPE_LAB: - case BUILD_ARMORY: - case BUILD_NUKE_PLANT: - case BUILD_OBSERVATORY: - case BUILD_PHASEGATE: - case BUILD_TURRET: - case BUILD_SIEGE: - - // Alien buildings - case ALIEN_BUILD_OFFENSE_CHAMBER: - case ALIEN_BUILD_DEFENSE_CHAMBER: - case ALIEN_BUILD_SENSORY_CHAMBER: - case ALIEN_BUILD_MOVEMENT_CHAMBER: - case ALIEN_BUILD_HIVE: - theIsBuilding = true; - break; - } - - return theIsBuilding; -} - -bool AvHSHUGetIsBuildTech(AvHMessageID inMessageID) -{ - bool theIsBuildTech = false; - - switch(inMessageID) - { - // Buildings - case BUILD_RESOURCES: - case BUILD_INFANTRYPORTAL: - case BUILD_COMMANDSTATION: - case BUILD_TURRET_FACTORY: - case BUILD_ARMSLAB: - case BUILD_PROTOTYPE_LAB: - case BUILD_ARMORY: - //case UPGRADE_ADVANCED_WEAPON_FACTORY: - case BUILD_NUKE_PLANT: - case BUILD_OBSERVATORY: - case BUILD_SCAN: - case BUILD_PHASEGATE: - case BUILD_TURRET: - case BUILD_SIEGE: - case BUILD_HEAVY: - case BUILD_JETPACK: - - // Equipment - case BUILD_AMMO: - case BUILD_HEALTH: - case BUILD_CAT: - case BUILD_WELDER: - case BUILD_MINES: - case BUILD_SHOTGUN: - case BUILD_HMG: - case BUILD_NUKE: - case BUILD_GRENADE_GUN: - //case BUILD_MEDKIT: - //case BUILD_STIMPACK: - - // Alien buildings - case ALIEN_BUILD_OFFENSE_CHAMBER: - case ALIEN_BUILD_DEFENSE_CHAMBER: - case ALIEN_BUILD_SENSORY_CHAMBER: - case ALIEN_BUILD_MOVEMENT_CHAMBER: - theIsBuildTech = true; - break; - } - - return theIsBuildTech; -} - -bool AvHSHUGetIsWeaponFocusable(AvHWeaponID inWeaponID) -{ - bool theIsFocusable = false; - - switch(inWeaponID) - { - case AVH_WEAPON_BITE: - case AVH_WEAPON_SPIT: - case AVH_WEAPON_BITE2: - case AVH_WEAPON_SWIPE: - case AVH_WEAPON_CLAWS: - theIsFocusable = true; - break; - } - - return theIsFocusable; -} - -bool AvHSHUGetDoesTechCostEnergy(AvHMessageID inMessageID) -{ - bool theTechCostsEnergy = false; - - switch(inMessageID) - { - case BUILD_SCAN: - theTechCostsEnergy = true; - break; - } - - return theTechCostsEnergy; -} - -bool AvHSHUGetIsCombatModeTech(AvHMessageID inMessageID) -{ - bool theIsCombatModeTech = false; - - switch(inMessageID) - { - case BUILD_SHOTGUN: - case BUILD_GRENADE_GUN: - case BUILD_HMG: - case BUILD_WELDER: - case BUILD_MINES: - case BUILD_JETPACK: - case BUILD_HEAVY: - case BUILD_SCAN: - theIsCombatModeTech = true; - break; - } - - return theIsCombatModeTech; -} - -bool AvHSHUGetIsResearchTech(AvHMessageID inMessageID) -{ - bool theIsResearchTech = false; - - switch(inMessageID) - { - case RESEARCH_ELECTRICAL: - case RESEARCH_ARMOR_ONE: - case RESEARCH_ARMOR_TWO: - case RESEARCH_ARMOR_THREE: - case RESEARCH_WEAPONS_ONE: - case RESEARCH_WEAPONS_TWO: - case RESEARCH_WEAPONS_THREE: - case TURRET_FACTORY_UPGRADE: - case RESEARCH_JETPACKS: - case RESEARCH_HEAVYARMOR: - case RESEARCH_DISTRESSBEACON: - case RESEARCH_HEALTH: - case RESEARCH_CATALYSTS: - case MESSAGE_CANCEL: - case RESEARCH_MOTIONTRACK: - case RESEARCH_PHASETECH: - case RESEARCH_GRENADES: - - case RESOURCE_UPGRADE: - case ARMORY_UPGRADE: - theIsResearchTech = true; - break; - } - - return theIsResearchTech; -} - - - - -//Here's TFC's code that checks whether a player's allowed to build a sentry -//or not. -//I can't remember if there was any good reason why we used -//UTIL_FindEntitiesInSphere() -//instead of UTIL_EntitiesInBox(). -// -////========================================================================= -//// Returns 0 if the area around obj is safe to build in -//int CBaseEntity::CheckArea( CBaseEntity *pIgnore ) -//{ -// TraceResult tr; -// Vector vecOrg = pev->origin; -// -// // Check the origin -// int iContents = UTIL_PointContents(vecOrg); -// if ( iContents != CONTENT_EMPTY && iContents != CONTENT_WATER ) -// return CAREA_BLOCKED; -// -// Vector vecIgnoreOrg = pIgnore->pev->origin; -// // Get the player's origin irrelevant of crouching -// if ( pIgnore->pev->flags & FL_DUCKING ) -// { -// vecIgnoreOrg = vecIgnoreOrg + (VEC_DUCK_HULL_MIN - -// VEC_HULL_MIN); -// } -// // Trace a hull -// UTIL_TraceHull( vecIgnoreOrg, pev->origin, ignore_monsters, -// large_hull, edict(), &tr ); -// CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); -// if (tr.flFraction != 1 || tr.fAllSolid == 1) -// return CAREA_BLOCKED; -// -// // Check for solid entities in the area -// CBaseEntity *pEnt = NULL; -// while ( (pEnt = UTIL_FindEntityInSphere( pEnt, pev->origin, 48 )) != -// NULL ) -// { -// // If it's not the engineer, and its a solid entity, fail -// if (pEnt != pIgnore && pEnt != this && pEnt->pev->solid > -// SOLID_TRIGGER) -// return CAREA_BLOCKED; -// } -// -// // Cycle through all the Nobuild zones in the map and make sure this -// isn't in one of them -// CBaseEntity *pNoBuild = UTIL_FindEntityByClassname( NULL, -// "func_nobuild" ); -// while ( pNoBuild ) -// { -// // Check to see if we're building in this zone -// if ( vecOrg.x >= pNoBuild->pev->mins.x && vecOrg.y >= -// pNoBuild->pev->mins.y && vecOrg.z >= pNoBuild->pev->mins.z && -// vecOrg.x <= pNoBuild->pev->maxs.x && -// vecOrg.y <= pNoBuild->pev->maxs.y && vecOrg.z <= pNoBuild->pev->maxs.z ) -// return CAREA_NOBUILD; -// -// pNoBuild = UTIL_FindEntityByClassname( pNoBuild, -// "func_nobuild" ); -// } -// -// // Check below -// UTIL_TraceLine( vecOrg, vecOrg + Vector(0,0,-64), -// dont_ignore_monsters, edict(), &tr ); -// if ( tr.flFraction == 1.0 ) -// return CAREA_BLOCKED; -// -// return CAREA_CLEAR; -//} - -//bool AvHSHUGetIsEnoughRoom(const vec3_t& inCenter, const vec3_t& inMin, const vec3_t& inMax) -//{ -// bool theEnoughRoom = false; -// -// // Do a traceline from center to center + min -// // Hit nothing? -// // Do a traceline from center to center + max -// // Hit nothing? -// // Success -// theEnoughRoom = true; -// -// return theEnoughRoom; -//} - -bool AvHSHUGetIsGroupMessage(AvHMessageID inMessageID) -{ - bool theIsGroupMessage = false; - - switch(inMessageID) - { - case GROUP_CREATE_1: - case GROUP_CREATE_2: - case GROUP_CREATE_3: - case GROUP_CREATE_4: - case GROUP_CREATE_5: - case GROUP_SELECT_1: - case GROUP_SELECT_2: - case GROUP_SELECT_3: - case GROUP_SELECT_4: - case GROUP_SELECT_5: - theIsGroupMessage = true; - } - - return theIsGroupMessage; -} - -bool AvHSHUGetSizeForTech(AvHMessageID inMessageID, Vector& outMinSize, Vector& outMaxSize, bool inGetSizeToPlace) -{ - bool theSuccess = false; - - // Onos-sized - //const int theOnosHeightNeededToSpawn = HULL3_MAXZ - HULL3_MINZ + kRespawnFudgeFactorHeight; - //const int theOnosWidthNeededToSpawn = HULL3_MAXY - HULL3_MINY + kRespawnFudgeFactorHeight; - - // Marine-sized - const int theMarineHeightNeededToSpawn = HULL0_MAXZ - HULL0_MINZ + kRespawnFudgeFactorHeight; - - switch(inMessageID) - { - case BUILD_INFANTRYPORTAL: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 14.49); - - // Make sure there is room above it for players to spawn - if(inGetSizeToPlace) - { - outMaxSize.z += theMarineHeightNeededToSpawn; - } - - theSuccess = true; - break; - - case BUILD_PHASEGATE: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 14.49); - - // Make sure there is remove above it for players to spawn - if(inGetSizeToPlace) - { - //outMinSize.x = outMinSize.y = -theOnosWidthNeededToSpawn; - //outMaxSize.x = outMaxSize.y = theOnosWidthNeededToSpawn; - //outMaxSize.z += theOnosHeightNeededToSpawn; - outMaxSize.z += theMarineHeightNeededToSpawn; - } - - theSuccess = true; - break; - - case BUILD_RESOURCES: - outMinSize = kResourceMinSize; - outMaxSize = kResourceMaxSize; - theSuccess = true; - break; - - case ALIEN_BUILD_RESOURCES: - outMinSize = kAlienResourceMinSize; - outMaxSize = kAlienResourceMaxSize; - theSuccess = true; - break; - - default: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 66.9486); - theSuccess = true; - break; - - case BUILD_TURRET: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 42.0); - theSuccess = true; - break; - - case ALIEN_BUILD_OFFENSE_CHAMBER: - case ALIEN_BUILD_DEFENSE_CHAMBER: - case ALIEN_BUILD_SENSORY_CHAMBER: - case ALIEN_BUILD_MOVEMENT_CHAMBER: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 44.0); - theSuccess = true; - break; - - case BUILD_COMMANDSTATION: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 70.34); - theSuccess = true; - break; - - case BUILD_TURRET_FACTORY: - outMinSize = Vector(-16, -16, 0); - //outMaxSize = Vector(16.0, 16.0, 55.68); - outMaxSize = Vector(16.0, 16.0, 62.1931); - theSuccess = true; - break; - - case BUILD_ARMORY: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 62.1931); - theSuccess = true; - break; - - case BUILD_PROTOTYPE_LAB: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 67.7443); - theSuccess = true; - break; - - case BUILD_OBSERVATORY: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 80.7443); - theSuccess = true; - break; - - case BUILD_SIEGE: - outMinSize = Vector(-16, -16, 0); - outMaxSize = Vector(16.0, 16.0, 62.1931/*50.6678*/); - theSuccess = true; - break; - - case BUILD_HEALTH: - outMinSize = kHealthMinSize; - outMaxSize = kHealthMaxSize; - theSuccess = true; - break; - - case BUILD_CAT: - outMinSize = kCatalystMinSize; - outMaxSize = kCatalystMaxSize; - theSuccess = true; - break; - - case BUILD_HEAVY: - outMinSize = kHeavyMinSize; - outMaxSize = kHeavyMaxSize; - theSuccess = true; - break; - - case BUILD_JETPACK: - outMinSize = kJetpackMinSize; - outMaxSize = kJetpackMaxSize; - theSuccess = true; - break; - - case BUILD_AMMO: - outMinSize = kAmmoMinSize; - outMaxSize = kAmmoMaxSize; - theSuccess = true; - break; - - case BUILD_MINES: - outMinSize = kWeaponMinSize; - outMaxSize = Vector(16, 16, 40); - theSuccess = true; - break; - - case BUILD_SHOTGUN: - case BUILD_HMG: - case BUILD_WELDER: - case BUILD_NUKE: - case BUILD_GRENADE_GUN: - outMinSize = kWeaponMinSize; - outMaxSize = kWeaponMaxSize; - theSuccess = true; - break; - - case ALIEN_BUILD_HIVE: - outMinSize = kHiveMinSize; - outMaxSize = kHiveMaxSize; - theSuccess = true; - break; - } - - return theSuccess; -} - -bool AvHSHUGetSizeForPlayerUser3(AvHUser3 inUser3, Vector& outMinSize, Vector& outMaxSize, bool inDucking) -{ - bool theSuccess = false; - - // Now set size - switch(inUser3) - { - case AVH_USER3_NONE: - case AVH_USER3_MARINE_PLAYER: - case AVH_USER3_COMMANDER_PLAYER: - case AVH_USER3_ALIEN_PLAYER4: - // Get simple case working first - if(inDucking) - { - outMinSize = HULL1_MIN; - outMaxSize = HULL1_MAX; - } - else - { - outMinSize = HULL0_MIN; - outMaxSize = HULL0_MAX; - } - theSuccess = true; - break; - - case AVH_USER3_ALIEN_PLAYER1: - case AVH_USER3_ALIEN_PLAYER2: - case AVH_USER3_ALIEN_PLAYER3: - case AVH_USER3_ALIEN_EMBRYO: - outMinSize = HULL1_MIN; - outMaxSize = HULL1_MAX; - theSuccess = true; - break; - - case AVH_USER3_ALIEN_PLAYER5: - if(inDucking) - { - outMinSize = HULL0_MIN; - outMaxSize = HULL0_MAX; - } - else - { - outMinSize = HULL3_MIN; - outMaxSize = HULL3_MAX; - } - theSuccess = true; - break; - } - - return theSuccess; -} - -bool AvHSHUGetSizeForUser3(AvHUser3 inUser3, Vector& outMinSize, Vector& outMaxSize) -{ - bool theSuccess = false; - - // If it's a player, get max size he can be (assuming he's not ducking) - theSuccess = AvHSHUGetSizeForPlayerUser3(inUser3, outMinSize, outMaxSize, false); - if(!theSuccess) - { - AvHMessageID theMessageID = MESSAGE_NULL; - - // Convert it to a AvHMessageID if possible - if(AvHSHUUser3ToMessageID(inUser3, theMessageID)) - { - theSuccess = AvHSHUGetSizeForTech(theMessageID, outMinSize, outMaxSize); - } - } - - return theSuccess; -} - -bool AvHSHUUser3ToMessageID(AvHUser3 inUser3, AvHMessageID& outMessageID) -{ - AvHMessageID theMessageID = MESSAGE_NULL; - bool theSuccess = false; - - switch(inUser3) - { - case AVH_USER3_FUNC_RESOURCE: - case AVH_USER3_RESTOWER: - theMessageID = BUILD_RESOURCES; - break; - - case AVH_USER3_COMMANDER_STATION: - theMessageID = BUILD_COMMANDSTATION; - break; - - case AVH_USER3_TURRET_FACTORY: - case AVH_USER3_ADVANCED_TURRET_FACTORY: - theMessageID = BUILD_TURRET_FACTORY; - break; - - case AVH_USER3_ARMORY: - case AVH_USER3_ADVANCED_ARMORY: - theMessageID = BUILD_ARMORY; - break; - - case AVH_USER3_ARMSLAB: - theMessageID = BUILD_ARMSLAB; - break; - - case AVH_USER3_PROTOTYPE_LAB: - theMessageID = BUILD_PROTOTYPE_LAB; - break; - - case AVH_USER3_OBSERVATORY: - theMessageID = BUILD_OBSERVATORY; - break; - - case AVH_USER3_TURRET: - theMessageID = BUILD_TURRET; - break; - - case AVH_USER3_SIEGETURRET: - theMessageID = BUILD_SIEGE; - break; - - case AVH_USER3_INFANTRYPORTAL: - theMessageID = BUILD_INFANTRYPORTAL; - break; - - case AVH_USER3_PHASEGATE: - theMessageID = BUILD_PHASEGATE; - break; - - case AVH_USER3_HEAVY: - theMessageID = BUILD_HEAVY; - break; - - case AVH_USER3_JETPACK: - theMessageID = BUILD_JETPACK; - break; - - case AVH_USER3_DEFENSE_CHAMBER: - theMessageID = ALIEN_BUILD_DEFENSE_CHAMBER; - break; - - case AVH_USER3_MOVEMENT_CHAMBER: - theMessageID = ALIEN_BUILD_MOVEMENT_CHAMBER; - break; - - case AVH_USER3_OFFENSE_CHAMBER: - theMessageID = ALIEN_BUILD_OFFENSE_CHAMBER; - break; - - case AVH_USER3_SENSORY_CHAMBER: - theMessageID = ALIEN_BUILD_SENSORY_CHAMBER; - break; - - case AVH_USER3_ALIENRESTOWER: - theMessageID = ALIEN_BUILD_RESOURCES; - break; - - case AVH_USER3_HIVE: - theMessageID = ALIEN_BUILD_HIVE; - break; - - case AVH_USER3_ALIEN_PLAYER1: - theMessageID = ALIEN_LIFEFORM_ONE; - break; - - case AVH_USER3_ALIEN_PLAYER2: - theMessageID = ALIEN_LIFEFORM_TWO; - break; - - case AVH_USER3_ALIEN_PLAYER3: - theMessageID = ALIEN_LIFEFORM_THREE; - break; - - case AVH_USER3_ALIEN_PLAYER4: - theMessageID = ALIEN_LIFEFORM_FOUR; - break; - - case AVH_USER3_ALIEN_PLAYER5: - theMessageID = ALIEN_LIFEFORM_FIVE; - break; - } - - if(theMessageID != MESSAGE_NULL) - { - outMessageID = theMessageID; - theSuccess = true; - } - - return theSuccess; -} - -bool AvHSHUMessageIDToUser3(AvHMessageID inMessageID, AvHUser3& outUser3) -{ - bool theSuccess = false; - - AvHUser3 theUser3 = AVH_USER3_NONE; - - switch(inMessageID) - { - case BUILD_RESOURCES: - theUser3 = AVH_USER3_RESTOWER; - break; - - case BUILD_COMMANDSTATION: - theUser3 = AVH_USER3_COMMANDER_STATION; - break; - - case BUILD_TURRET_FACTORY: - theUser3 = AVH_USER3_TURRET_FACTORY; - break; - - case TURRET_FACTORY_UPGRADE: - theUser3 = AVH_USER3_ADVANCED_TURRET_FACTORY; - break; - - case BUILD_ARMORY: - theUser3 = AVH_USER3_ARMORY; - break; - - case ARMORY_UPGRADE: - theUser3 = AVH_USER3_ADVANCED_ARMORY; - break; - - case BUILD_ARMSLAB: - theUser3 = AVH_USER3_ARMSLAB; - break; - - case BUILD_PROTOTYPE_LAB: - theUser3 = AVH_USER3_PROTOTYPE_LAB; - break; - - case BUILD_OBSERVATORY: - theUser3 = AVH_USER3_OBSERVATORY; - break; - - case BUILD_TURRET: - theUser3 = AVH_USER3_TURRET; - break; - - case BUILD_SIEGE: - theUser3 = AVH_USER3_SIEGETURRET; - break; - - case BUILD_INFANTRYPORTAL: - theUser3 = AVH_USER3_INFANTRYPORTAL; - break; - - case BUILD_PHASEGATE: - theUser3 = AVH_USER3_PHASEGATE; - break; - - case BUILD_HEAVY: - theUser3 = AVH_USER3_HEAVY; - break; - - case BUILD_JETPACK: - theUser3 = AVH_USER3_JETPACK; - break; - - // Menus - case MENU_BUILD: - theUser3 = AVH_USER3_MENU_BUILD; - break; - case MENU_BUILD_ADVANCED: - theUser3 = AVH_USER3_MENU_BUILD_ADVANCED; - break; - case MENU_ASSIST: - theUser3 = AVH_USER3_MENU_ASSIST; - break; - case MENU_EQUIP: - theUser3 = AVH_USER3_MENU_EQUIP; - break; - - // Weapons - case BUILD_MINES: - theUser3 = AVH_USER3_MINE; - break; - - // Lifeforms - case ALIEN_LIFEFORM_ONE: - theUser3 = AVH_USER3_ALIEN_PLAYER1; - break; - - case ALIEN_LIFEFORM_TWO: - theUser3 = AVH_USER3_ALIEN_PLAYER2; - break; - - case ALIEN_LIFEFORM_THREE: - theUser3 = AVH_USER3_ALIEN_PLAYER3; - break; - - case ALIEN_LIFEFORM_FOUR: - theUser3 = AVH_USER3_ALIEN_PLAYER4; - break; - - case ALIEN_LIFEFORM_FIVE: - theUser3 = AVH_USER3_ALIEN_PLAYER5; - break; - } - - if(theUser3 != AVH_USER3_NONE) - { - outUser3 = theUser3; - theSuccess = true; - } - - return theSuccess; -} - - -float AvHSHUGetTime() -{ - float theTime = 0; - - #ifdef AVH_SERVER - theTime = gpGlobals->time; - #else - theTime = gEngfuncs.GetClientTime(); - #endif - - return theTime; -} - - - -// Note, all these models must be precached already -char* AvHSHUGetBuildTechModelName(AvHMessageID inMessageID) -{ - char* theModelName = NULL; - - switch(inMessageID) - { - case BUILD_RESOURCES: - theModelName = kResourceTowerModel; - break; - - //case BUILD_REINFORCEMENTS: - // theModelName = kInfantryPortalModel; - // break; - - case BUILD_INFANTRYPORTAL: - theModelName = kInfantryPortalModel; - break; - - case BUILD_COMMANDSTATION: - theModelName = kCommandStationModel; - break; - - case BUILD_TURRET_FACTORY: - theModelName = kTurretFactoryModel; - break; - - case BUILD_ARMSLAB: - theModelName = kArmsLabModel; - break; - - case BUILD_PROTOTYPE_LAB: - theModelName = kPrototypeLabModel; - break; - - case BUILD_ARMORY: - theModelName = kArmoryModel; - break; - - case ARMORY_UPGRADE: - theModelName = kAdvancedWeaponFactoryModel; - break; - - case BUILD_OBSERVATORY: - theModelName = kObservatoryModel; - break; - - case BUILD_SCAN: - theModelName = kScanModel; - break; - - case BUILD_PHASEGATE: - theModelName = kPhaseGateModel; - break; - - case BUILD_TURRET: - theModelName = kDeployedTurretModel; - break; - - case BUILD_NUKE: - theModelName = kNukeModel; - break; - - case BUILD_SIEGE: - theModelName = kSiegeTurretModel; - //theModelName = kDeployedTurretModel; - break; - - case BUILD_CAT: - theModelName = kCatalystModel; - break; - - case BUILD_HEALTH: - theModelName = kHealthModel; - break; - - case BUILD_HEAVY: - theModelName = kHeavyModel; - break; - - case BUILD_JETPACK: - theModelName = kJetpackModel; - break; - - case BUILD_AMMO: - theModelName = kAmmoModel; - break; - - case BUILD_WELDER: - theModelName = kWelderWModel; - break; - - case BUILD_MINES: - theModelName = kTripmineW2Model; - break; - - case BUILD_SHOTGUN: - theModelName = kSGWModel; - break; - - case BUILD_HMG: - theModelName = kHMGWModel; - break; - - case BUILD_GRENADE_GUN: - theModelName = kGGWModel; - break; - - // Alien buildings - case ALIEN_BUILD_HIVE: - theModelName = kHiveModel; - break; - - case ALIEN_BUILD_RESOURCES: - theModelName = kAlienResourceTowerModel; - break; - - case ALIEN_BUILD_OFFENSE_CHAMBER: - theModelName = kOffenseChamberModel; - break; - - case ALIEN_BUILD_DEFENSE_CHAMBER: - theModelName = kDefenseChamberModel; - break; - - case ALIEN_BUILD_SENSORY_CHAMBER: - theModelName = kSensoryChamberModel; - break; - - case ALIEN_BUILD_MOVEMENT_CHAMBER: - theModelName = kMovementChamberModel; - break; - } - - return theModelName; -} - -bool AvHSHUGetBuildTechRange(AvHMessageID inMessageID, float& outRange) -{ - bool theSuccess = false; - - // switch(inMessageID) - // { - // case BUILD_CAMERA: - // outRange = kResourceTowerSightRange; - // theSuccess = true; - // break; - // case BUILD_PHASEGATE: - // break; - // case BUILD_TURRET: - // outRange = TURRET_RANGE; - // theSuccess = true; - // break; - // case BUILD_SIEGE: - // outRange = kSiegeTurretMaxRange; - // theSuccess = true; - // break; - // } - - return theSuccess; -} - -bool AvHSHUTraceLineIsAreaFree(Vector& inStart, Vector& inEnd, edict_t* inIgnoreEntity, bool inIgnorePlayers) -{ - bool theAreaIsFree = true; - -#ifdef AVH_SERVER - TraceResult theTR; - bool theIsDone = false; - - // Do tracelines between the corners, to make sure there's no geometry inside the box - int theNumIters = 0; - - IGNORE_MONSTERS theIgnoreMonsters = dont_ignore_monsters; - - if (inIgnorePlayers) - { - theIgnoreMonsters = ignore_monsters; - } - - while(!theIsDone) - { - UTIL_TraceLine(inStart, inEnd, theIgnoreMonsters, inIgnoreEntity, &theTR); - if(theTR.flFraction != 1.0f) - { - CBaseEntity* theEntity = CBaseEntity::Instance(ENT(theTR.pHit)); - if(theEntity && (theEntity->pev->solid != SOLID_BBOX) && (theEntity->pev->solid != SOLID_SLIDEBOX) && (theEntity->pev->solid != SOLID_BSP) && (inIgnorePlayers != !!theEntity->IsPlayer()) ) - { - VectorCopy(theTR.vecEndPos, inStart); - } - else - { - theIsDone = true; - theAreaIsFree = false; - } - } - else - { - theIsDone = true; - } - - if(theNumIters++ > 50) - { - theIsDone = true; - theAreaIsFree = false; - } - } -#endif - -// int theIndex; -// vec3_t theEndLocation; -// AvHTeamNumber theTeam; -// bool thePlayerHit; -// int theUserThree; -// int theUserFour; -// -// if(AvHSHUTraceTangible(inStart, inEnd, theIndex, theEndLocation, theTeam, thePlayerHit, theUserThree, theUserFour)) -// { -// if(theIndex >= 0) -// { -// theAreaIsFree = false; -// } -// } - - return theAreaIsFree; -} - -float AvHTraceLineAgainstWorld(Vector& vecStart, Vector& vecEnd) -{ - -#ifdef AVH_SERVER - - TraceResult tr; - UTIL_TraceLine(vecStart, vecEnd, ignore_monsters, dont_ignore_glass, NULL, &tr); - - return tr.flFraction; - -#endif - -#ifdef AVH_CLIENT - - // This may not be the most efficient way, but it seems to get the job done. - - float theFraction = 1; - - for (int i = 0; i < kMaxEntities && theFraction > 0; ++i) - { - - cl_entity_t* theEntity = gEngfuncs.GetEntityByIndex(i); - - if (theEntity != NULL && theEntity->model != NULL && theEntity->model->type == mod_brush) - { - - vec3_t localStart; - vec3_t localEnd; - - // Translate the start and end into the model's frame of reference. - - VectorSubtract(vecStart, theEntity->origin, localStart); - VectorSubtract(vecEnd, theEntity->origin, localEnd); - - // Rotate the start and end into the model's frame of reference. - - if (theEntity->angles[0] || - theEntity->angles[1] || - theEntity->angles[2]) - { - - vec3_t forward; - vec3_t right; - vec3_t up; - - AngleVectors(theEntity->angles, forward, right, up); - - vec3_t temp; - - VectorCopy(localStart, temp); - localStart[0] = DotProduct(temp, forward); - localStart[1] = -DotProduct(temp, right); - localStart[2] = DotProduct(temp, up); - - VectorCopy(localEnd, temp); - localEnd[0] = DotProduct(temp, forward); - localEnd[1] = -DotProduct(temp, right); - localEnd[2] = DotProduct(temp, up); - - } - - trace_t tr; - NS_TraceLine(&theEntity->model->hulls[0], localStart, localEnd, &tr); - - if (tr.fraction < theFraction) - { - theFraction = tr.fraction; - } - - } - - } - - return theFraction; - -#endif - -} - - -bool AvHSHUGetCanBeBuiltOnPlayers(AvHMessageID inMessageID) -{ - bool theCanBeBuiltOnPlayers = false; - - switch(inMessageID) - { - case ALIEN_BUILD_HIVE: - case BUILD_SCAN: - case BUILD_AMMO: - case BUILD_HEALTH: - case BUILD_CAT: - case BUILD_MINES: - case BUILD_WELDER: - case BUILD_SHOTGUN: - case BUILD_HMG: - case BUILD_GRENADE_GUN: - case BUILD_HEAVY: - case BUILD_JETPACK: - theCanBeBuiltOnPlayers = true; - break; - } - - return theCanBeBuiltOnPlayers; -} - -bool AvHSHUGetIsSiteValidForBuild(AvHMessageID inMessageID, Vector* inLocation, int inIgnoredEntityIndex) -{ - //TODO: - check entity returned by client side commander trace for drop validity before we ever get this far - - bool theSuccess = false; - - // Check that there's enough room for building - vec3_t theMinSize, theMaxSize; - if(AvHSHUGetSizeForTech(inMessageID, theMinSize, theMaxSize, true)) - { - - // If it's a build with special placement requirements, check that here (this could modify the location also) - if(AvHSHUGetAreSpecialBuildingRequirementsMet(inMessageID, *inLocation)) - { - - // Assume mappers place hives correctly (it's such a showstopper if aliens can't create a hive because it's too close to a wall) - // KGP 4/28/04 - add res towers to hives for the same reason -- if a mapper didn't give room, it's a showstopper. - bool theIgnorePlayers = AvHSHUGetCanBeBuiltOnPlayers(inMessageID); - float theSlopeTangent = theIgnorePlayers ? kMaxEquipmentDropSlope : kMaxBuildingDropSlope; - bool theSkipDropCheck = false; - switch(inMessageID) - { - case BUILD_RESOURCES: - theSkipDropCheck = true; - break; - case ALIEN_BUILD_RESOURCES: - theSkipDropCheck = true; - break; - case ALIEN_BUILD_HIVE: - theSkipDropCheck = true; - break; - } - if(theSkipDropCheck || AvHSHUGetCanDropItem(*inLocation, theMinSize, theMaxSize, theSlopeTangent, inIgnoredEntityIndex, theIgnorePlayers)) - { - //TODO: - better check for entity below drop, since it's often off center. - // Check to make sure building isn't being created on top of another building - vec3_t thePositionBelowBuild = *inLocation; - thePositionBelowBuild.z = -kMaxMapDimension; - - int theIndex; - vec3_t theEndLocation; - AvHTeamNumber theTeam; - bool thePlayerHit; - int theUserThree; - int theUserFour; - - // Don't allow building on any entities, except allow resource towers on top of func_resources - - //TODO : use full list instead of physents, which isn't accounting for items the commander can't see - bool theHitSomething = AvHSHUTraceTangible(*inLocation, thePositionBelowBuild, theIndex, theEndLocation, theTeam, thePlayerHit, theUserThree, theUserFour); - - //res building case - if(inMessageID == BUILD_RESOURCES || inMessageID == ALIEN_BUILD_RESOURCES) - { -#ifdef AVH_SERVER //on server, return false if occupied - FOR_ALL_ENTITIES(kesFuncResource,AvHFuncResource*) - if(!theEntity->GetIsOccupied()) // open for use - { - if(VectorDistance(theEntity->pev->origin,*inLocation) < 20) //small enough that we don't check the wrong nozzle - { - theSuccess = (!theHitSomething || theIndex <= 0 || theUserThree == AVH_USER3_FUNC_RESOURCE); - break; - } - } - END_FOR_ALL_ENTITIES(kesFuncResource) -#else //on client the occupied function isn't available - theSuccess = (!theHitSomething || theIndex <= 0 || theUserThree == AVH_USER3_FUNC_RESOURCE); -#endif - } - else if ( inMessageID == ALIEN_BUILD_HIVE ) - { - theSuccess = true; - //theSuccess = ( !theHitSomething || ( *inLocation[2] - theEndLocation[2] > 40.0f ) ); -//#ifdef AVH_SERVER -// ALERT(at_console, "theHitSomething=%d theSuccess=%d\n", theHitSomething, theSuccess); -// ALERT(at_console, "%f\n", *inLocation[2] - theEndLocation[2]); -// ALERT(at_console, "{%f, %f, %f} : { %f, %f, %f }\n", -// inLocation[0], inLocation[1], inLocation[2], -// theEndLocation[0], theEndLocation[1], theEndLocation[2]); -//#endif - } - else if(!theHitSomething || (theIgnorePlayers && thePlayerHit) || theIndex <= 0) - { - // THEN it's a legal build spot and the building shows up green. - theSuccess = true; - } - } - } - } - return theSuccess; -} - -int AvHSHUGetPointContents(vec3_t inPoint) -{ - int thePointContents = 0; - - #ifdef AVH_SERVER - thePointContents = UTIL_PointContents(inPoint); - #endif - - #ifdef AVH_CLIENT - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - gEngfuncs.pEventAPI->EV_PushPMStates(); - gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1); - gEngfuncs.pEventAPI->EV_SetTraceHull(2); - - thePointContents = gEngfuncs.PM_PointContents(inPoint, NULL); - - gEngfuncs.pEventAPI->EV_PopPMStates(); - #endif - - return thePointContents; -} - - /* - * Check to see if the build or item is overlapping the world. - */ -bool AvHSHUGetIsVolumeContentNonsolid(vec3_t inCenter, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers) -{ - - float theMins[3], theMaxs[3]; - VectorAdd(inCenter,inMinSize,theMins); - VectorAdd(inCenter,inMaxSize,theMaxs); - - bool theIsNonsolid = true; //innocent until proven guilty - - extern playermove_t *pmove; - if (pmove != NULL && pmove->physents[0].model != NULL) - { - const hull_t* theHull = &pmove->physents[0].model->hulls[NS_GetValveHull(2)]; - int theContents = NS_BoxContents(theHull,theHull->firstclipnode,theMins,theMaxs); - theIsNonsolid = (theContents != CONTENT_SOLID); - } - else //fallback if we're just starting a round - { - int theContents = AvHSHUGetPointContents(inCenter); - if(theContents != CONTENT_SOLID) - { - //trace between each corner pair once looking for obstructions - 28 total comparisons - const static int MIN = 0; - const static int MAX = 1; - const static int NUM_CORNERS = 8; - int theIndex; - Vector theCorners[NUM_CORNERS]; - - for(int XPos = MIN; XPos <= MAX; ++XPos) - { - for(int YPos = MIN; YPos <= MAX; ++YPos) - { - for(int ZPos = MIN; ZPos <= MAX; ++ZPos) - { - theIndex = XPos+YPos*2+ZPos*4; - theCorners[theIndex].x = inCenter.x + ((XPos == MIN) ? inMinSize.x : inMaxSize.x); - theCorners[theIndex].y = inCenter.y + ((YPos == MIN) ? inMinSize.y : inMaxSize.y); - theCorners[theIndex].z = inCenter.z + ((ZPos == MIN) ? inMinSize.z : inMaxSize.z); - } - } - } - - for(int startCorner = 0; startCorner < NUM_CORNERS; ++startCorner) - { - for(int endCorner = startCorner+1; endCorner < NUM_CORNERS; ++endCorner) - { - if(!AvHSHUTraceLineIsAreaFree(theCorners[startCorner],theCorners[endCorner],inIgnoreEntity,inIgnorePlayers)) - { - theIsNonsolid = false; - break; - } - } - if(!theIsNonsolid) - { - break; - } - } - } - else - { - theIsNonsolid = false; - } - } - return theIsNonsolid; -} - -bool AvHSHUGetIsAreaFree(vec3_t inCenter, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers) -{ - bool theAreaIsFree = AvHSHUGetIsVolumeContentNonsolid(inCenter,inMinSize,inMaxSize,inIgnoreEntity,inIgnorePlayers); - - if(theAreaIsFree) - { - theAreaIsFree = !AvHSHUGetEntitiesBlocking(inCenter, inMinSize, inMaxSize, inIgnoreEntity, inIgnorePlayers); - } - - return theAreaIsFree; -} - -bool AvHSHUGetEntitiesBlocking(Vector& inOrigin, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers) -{ - bool theEntBlocking = false; - - typedef vector< pair > PhysEntListType; - PhysEntListType theEntList; - - const int kSearchRadius = 800; - - // Populate phys ent list - #ifdef AVH_SERVER - CBaseEntity* theEntity = NULL; - while ((theEntity = UTIL_FindEntityInSphere(theEntity, inOrigin, kSearchRadius)) != NULL) - { - - // If entity is visible non-world object, add it - if((theEntity->pev->team != TEAM_IND) && (theEntity->pev->solid != SOLID_NOT && theEntity->pev->solid != SOLID_TRIGGER)) - { - - AvHPlayer* thePlayer = dynamic_cast(theEntity); - - if(theEntity->edict() != inIgnoreEntity && !(thePlayer != NULL && inIgnorePlayers)) - { - theEntList.push_back(make_pair(theEntity->pev->iuser3, theEntity->pev->origin)); - } - } - } - #endif - - #ifdef AVH_CLIENT - // Haven't implemented this, so depend on it not being passed in - ASSERT(inIgnoreEntity == NULL); - - for(int i = 0; i < pmove->numphysent; i++) - { - physent_t* thePhysEnt = pmove->physents + i; - if((thePhysEnt->team != TEAM_IND) && (thePhysEnt->solid != SOLID_NOT && thePhysEnt->solid != SOLID_TRIGGER)) - { - theEntList.push_back(make_pair(thePhysEnt->iuser3, thePhysEnt->origin)); - } - } - #endif - - Vector theAbsMax; - VectorAdd(inOrigin, inMaxSize, theAbsMax); - - Vector theAbsMin; - VectorAdd(inOrigin, inMinSize, theAbsMin); - - Vector theOrigin1; - VectorAdd(theAbsMax, theAbsMin, theOrigin1); - theOrigin1 = theOrigin1*.5f; - - Vector theSize1; - VectorSubtract(theAbsMax, theAbsMin, theSize1); - theSize1 = theSize1*.5f; - - // Do collision on list, making sure none are too close to inOrigin - for(PhysEntListType::iterator theIter = theEntList.begin(); theIter != theEntList.end(); theIter++) - { - // Get size for tech - Vector theMinSize, theMaxSize; - if(AvHSHUGetSizeForUser3(AvHUser3(theIter->first), theMinSize, theMaxSize)) - { - Vector theEntOrigin = theIter->second; - - Vector theAbsMax; - VectorAdd(theEntOrigin, theMaxSize, theAbsMax); - - Vector theAbsMin; - VectorAdd(theEntOrigin, inMinSize, theAbsMin); - - Vector theOrigin2; - VectorAdd(theAbsMax, theAbsMin, theOrigin2); - theOrigin2 = theOrigin2*.5f; - - Vector theSize2; - VectorSubtract(theAbsMax, theAbsMin, theSize2); - theSize2 = theSize2*.5f; - - // Do simple box collision here - if(NS_BoxesOverlap((float*)theOrigin1, (float*)theSize1, (float*)theOrigin2, (float*)theSize2)) - { - theEntBlocking = true; - break; - } - } - } - - return theEntBlocking; -} - - - -void AvHSHUMakeViewFriendlyKillerName(string& ioKillerName) -{ - string theOutputName = ioKillerName; - int theStrLen = (int)ioKillerName.length(); - - if(!strncmp(ioKillerName.c_str(), "weapon_", 7)) - { - theOutputName = ioKillerName.substr(7); - } - else if(!strncmp(ioKillerName.c_str(), "monster_", 8)) - { - theOutputName = ioKillerName.substr(8); - } - else if(!strncmp(ioKillerName.c_str(), "func_", 5)) - { - theOutputName = ioKillerName.substr(5); - } - - ioKillerName = theOutputName; -} - -bool AvHSHUGetCanDropItem(vec3_t& ioCenter, Vector& inMinSize, Vector& inMaxSize, float inMaxSlopeTangent, int inIgnoreIndex, bool inIgnorePlayers) -{ - - float theMaxXWidth = max(-inMinSize[0],inMaxSize[0]); - float theMaxYWidth = max(-inMinSize[1],inMaxSize[1]); - float theRadius = sqrt(theMaxXWidth*theMaxXWidth + theMaxYWidth * theMaxYWidth); - float theHeight = inMaxSize[2] - inMinSize[2]; - //adjust origin to be base - float theOrigin[3] = { ioCenter[0], ioCenter[1], ioCenter[2] + inMinSize[2] }; - - CollisionChecker Checker(pmove,kHLPointHullIndex,CollisionChecker::HULL_TYPE_ALL,inIgnorePlayers,CollisionChecker::IGNORE_NONE,inIgnoreIndex); - bool theCanDropItem = (Checker.GetContentsInCylinder(theOrigin,theRadius,theHeight) != CONTENTS_SOLID); - - - if(!theCanDropItem) //can't place it -- can we drop it? - { - - float theMaxDropHeight = theRadius * inMaxSlopeTangent; - - float theDropOrigin[3] = { theOrigin[0], theOrigin[1], theOrigin[2] + theMaxDropHeight }; - theCanDropItem = (Checker.GetContentsInCylinder(theDropOrigin,theRadius,theHeight) != CONTENTS_SOLID); - - if(theCanDropItem) //can drop it -- get as low to ground as possible for drop - { - - float theBestDropHeight = theMaxDropHeight; - float theShiftFraction = 1.0f; - for(float theShiftAdjust = 0.5f; theShiftAdjust > 0.05f; theShiftAdjust /= 2) - { - theShiftFraction += theShiftAdjust * (theCanDropItem ? -1 : 1); //try lower if last was a success - theDropOrigin[2] = theMaxDropHeight; - theDropOrigin[2] *= theShiftFraction; - theDropOrigin[2] += theOrigin[2]; - - theCanDropItem = (Checker.GetContentsInCylinder(theDropOrigin,theRadius,theHeight) != CONTENTS_SOLID); - if(theCanDropItem) - { - theBestDropHeight = theShiftFraction; - theBestDropHeight *= theMaxDropHeight; - } - } - - //adjust center to best position - ioCenter[2] += theBestDropHeight; - theCanDropItem = true; - } - } - - return theCanDropItem; -} - -bool AvHSHUTraceAndGetIsSiteValidForBuild(AvHMessageID inMessageID, const Vector& inPointOfView, const Vector& inNormRay, Vector* outLocation) -{ - bool theSuccess = false; - - // TODO: Check if area is within the mapextents - - int theUser3; - bool theTraceSuccess = AvHSHUTraceTangible(inPointOfView, inNormRay, &theUser3, outLocation); - - // tankefugl: 0000291 - // ignore trace for scans (removed due to cost being paid when drop failed) - //if (inMessageID == BUILD_SCAN) - //{ - // theSuccess = true; - //} - //else - // :tankefugl - if(theTraceSuccess) - { - // tankefugl: 0000291 - if((inMessageID == BUILD_SCAN) || (AvHSHUGetIsSiteValidForBuild(inMessageID, outLocation))) - // :tankefugl - { - theSuccess = true; - } - - } - return theSuccess; -} - -#ifdef AVH_CLIENT -bool AvHSHUGetEntityAtRay(const Vector& inPointOfView, const Vector& inNormRay, int& outEntIndex) -{ - bool theSuccess = false; - - // Offset starting position a little so we don't select ourselves - Vector theStartPosition; - VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theStartPosition); - - Vector theEndPos; - VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theEndPos); - - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - - // Store off the old count - gEngfuncs.pEventAPI->EV_PushPMStates(); - - // Now add in all of the players. - int theNumPlayers = gEngfuncs.GetMaxClients(); - gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 ); - - bool theDone = false; - int theEntityIndex = -1; - - vec3_t theNewStartPos; - AvHSHUGetFirstNonSolidPoint(theStartPosition, theEndPos, theNewStartPos); - VectorCopy(theNewStartPos, theStartPosition); - - do - { - pmtrace_t tr; - gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); - gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPosition, theEndPos, PM_NORMAL, theEntityIndex, &tr ); - - physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); - theEntityIndex = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr ); - - if(pEntity) - { - int theUser3 = pEntity->iuser3; - if(theEntityIndex > 0) - { - outEntIndex = theEntityIndex; - theSuccess = true; - theDone = true; - } - } - - if((tr.fraction >= (1.0f - kFloatTolerance)) || (tr.fraction < kFloatTolerance)) - { - theDone = true; - } - else - { - VectorCopy(tr.endpos, theStartPosition); - } - - } while(!theDone); - - gEngfuncs.pEventAPI->EV_PopPMStates(); - - return theSuccess; -} -#endif - -#ifdef AVH_SERVER -bool AvHSHUGetEntityAtRay(const Vector& inPointOfView, const Vector& inNormRay, int& outEntIndex) -{ - TraceResult tr; - Vector theStartPos; - Vector theEndPos; - bool theSuccess = false; - bool theDone = false; - - VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theStartPos); - VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theEndPos); - - CBaseEntity* theEntityHit = NULL; - edict_t* theEdictToIgnore = NULL; - - vec3_t theNewStartPos; - AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); - VectorCopy(theNewStartPos, theStartPos); - - do - { - UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, ignore_glass, theEdictToIgnore, &tr); - - theEntityHit = CBaseEntity::Instance(tr.pHit); - if(theEntityHit) - { - if(theEntityHit->entindex() > 0) - { - outEntIndex = theEntityHit->entindex(); - theSuccess = true; - theDone = true; - } - } - - - if((tr.flFraction > (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance)) - { - theDone = true; - } - else - { - if(theEntityHit) - { - theEdictToIgnore = ENT(theEntityHit->pev); - } - VectorCopy(tr.vecEndPos, theStartPos); - } - } while(!theDone); - - return theSuccess; -} -#endif - -const AvHMapExtents& AvHSHUGetMapExtents() -{ - #ifdef AVH_CLIENT - const AvHMapExtents& theMapExtents = gHUD.GetMapExtents(); - #endif - - #ifdef AVH_SERVER - const AvHMapExtents& theMapExtents = GetGameRules()->GetMapExtents(); - #endif - - return theMapExtents; -} - - -#ifdef AVH_CLIENT -bool AvHSUClientTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour) -{ - physent_t* theEntity = NULL; - vec3_t theStartPos; - vec3_t theEndPos; - int theFoundEntity = -1; - bool theSuccess = false; - bool theDone = false; - - VectorCopy(inStartPos, theStartPos); - VectorCopy(inEndPos, theEndPos); - - vec3_t theNormal; - VectorSubtract(inEndPos, inStartPos, theNormal); - VectorNormalize(theNormal); - - outIndex = -1; - - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - - // Store off the old count - gEngfuncs.pEventAPI->EV_PushPMStates(); - - // Now add in all of the players. - int theNumPlayers = gEngfuncs.GetMaxClients(); - gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 ); - - vec3_t theNewStartPos; - AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); - VectorCopy(theNewStartPos, theStartPos); - - do - { - pmtrace_t tr; - gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); - gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL/*PM_GLASS_IGNORE*/, theFoundEntity, &tr ); - - physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); - theFoundEntity = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr ); - - if(pEntity) - { - if(pEntity->iuser3 != AVH_USER3_NONE) - { - VectorCopy(tr.endpos, outLocation); - outIndex = theFoundEntity; - theEntity = pEntity; - outUserThree = pEntity->iuser3; - outUserFour = pEntity->iuser4; - outTeamNumber = (AvHTeamNumber)(pEntity->team); - - if(pEntity->player) - { - outPlayerWasHit = true; - } - - theSuccess = true; - theDone = true; - } - } - - if(tr.fraction >= (1.0f - kFloatTolerance)) - { - theDone = true; - } - else - { - vec3_t theDiff = theNormal*kHitOffsetAmount; - float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); - if(theLength > kFloatTolerance) - { - VectorAdd(tr.endpos, theDiff, theStartPos); - } - else - { - theDone = true; - } - // Offset a bit so we don't hit again - //VectorMA(tr.endpos, kHitOffsetAmount, theNormal, theStartPos); - } - } while(!theDone); - - gEngfuncs.pEventAPI->EV_PopPMStates(); - - // If we didn't hit any special targets, see if it's a valid area to build or be ordered to - if(!theSuccess) - { - WaypointReturnCode theReturnCode = WAYPOINT_SUCCESS; - theSuccess = AvHSHUClientTraceWaypoint(inStartPos, inEndPos, &outLocation, &theReturnCode); - bool theWaypointTooSteep = (theReturnCode == WAYPOINT_TOOSTEEP); - - if(theSuccess || theWaypointTooSteep) - { - outUserThree = AVH_USER3_WAYPOINT; - outPlayerWasHit = false; - outIndex = -1; - outTeamNumber = TEAM_IND; - theSuccess = true; - } - - // Treat too steep as success, but mark it as nobuild - if(theWaypointTooSteep) - { - outUserThree = AVH_USER3_NOBUILD; - } - } - - return theSuccess; -} -#endif - -#ifdef AVH_SERVER -bool AvHSUServerTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour) -{ - bool theSuccess = false; - bool theDone = false; - edict_t* theEdictToIgnore = NULL; - vec3_t theStartPos; - vec3_t theEndPos; - - VectorCopy(inStartPos, theStartPos); - VectorCopy(inEndPos, theEndPos); - - vec3_t theNormal; - VectorSubtract(inEndPos, inStartPos, theNormal); - VectorNormalize(theNormal); - -// vec3_t theNewStartPos; -// AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); -// VectorCopy(theNewStartPos, theStartPos); - - do - { - TraceResult tr; - UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, theEdictToIgnore, &tr); - CBaseEntity* theEntityHit = NULL; - - // Return the entity in special way - if(tr.flFraction < 1 && tr.pHit) - { - theEntityHit = CBaseEntity::Instance(tr.pHit); - AvHPlayer* thePlayer = dynamic_cast(theEntityHit); - AvHWeldable* theWeldable = dynamic_cast(theEntityHit); - - AvHBasePlayerWeapon* theWeapon = dynamic_cast(theEntityHit); - - if(AvHSUGetIsDebugging()) - { - // Create an entity where the trace hit - Vector theAngles(0, 0, 0); - - CBaseEntity* pEnt = CBaseEntity::Create(kwsDebugEntity, tr.vecEndPos, theAngles); - ASSERT(pEnt); - pEnt->pev->movetype = MOVETYPE_FLY; - pEnt->pev->solid = SOLID_NOT; - } - - if(theEntityHit && (theEntityHit->pev->iuser3 != AVH_USER3_NONE)) - { - // Don't hit seethroughs - if(theEntityHit->pev->iuser3 != AVH_USER3_ALPHA) - { - outIndex = ENTINDEX(tr.pHit); - VectorCopy(tr.vecEndPos, outLocation); - outTeamNumber = (AvHTeamNumber)theEntityHit->pev->team; - outUserThree = theEntityHit->pev->iuser3; - outUserFour = theEntityHit->pev->iuser4; - - if(thePlayer) - { - outPlayerWasHit = true; - } - - theSuccess = true; - theDone = true; - } - } - - theEdictToIgnore = theEntityHit->edict(); - } - - if((tr.flFraction >= (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance)) - { - theDone = true; - } - else - { - vec3_t theDiff = theNormal*kHitOffsetAmount; - float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); - if(theLength > kFloatTolerance) - { - VectorAdd(theStartPos, theDiff, theStartPos); - } - else - { - theDone = true; - } - // Offset a bit so we don't hit again - //VectorMA(tr.vecEndPos, kHitOffsetAmount, theNormal, theStartPos); - } - } while(!theDone); - - if(!theSuccess) - { - WaypointReturnCode theReturnCode = WAYPOINT_SUCCESS; - theSuccess = AvHSHUServerTraceWaypoint(inStartPos, inEndPos, &outLocation, &theReturnCode); - bool theWaypointTooSteep = (theReturnCode == WAYPOINT_TOOSTEEP); - if(theSuccess || theWaypointTooSteep) - { - outUserThree = AVH_USER3_WAYPOINT; - outPlayerWasHit = false; - outIndex = -1; - outTeamNumber = TEAM_IND; - theSuccess = true; - } - // Treat too steep as success, but mark it as nobuild - if(theWaypointTooSteep) - { - outUserThree = AVH_USER3_NOBUILD; - } - } - - return theSuccess; -} -#endif - - - -#ifdef AVH_CLIENT -bool AvHSHUClientTraceWaypoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t* outLocation, WaypointReturnCode* outReturnCode) -{ - vec3_t theStartPos; - vec3_t theEndPos; - int theFoundEntity = -1; - int theEntityToIgnore = -1; - bool theDone = false; - bool theLegalToBuild = false; - - VectorCopy(inStartPos, theStartPos); - VectorCopy(inEndPos, theEndPos); - - vec3_t theNormal; - VectorSubtract(inEndPos, inStartPos, theNormal); - VectorNormalize(theNormal); - - int theNumPlayers = gEngfuncs.GetMaxClients(); - pmtrace_t tr; - - //DebugPoint theDebugPoint(theStartPos[0], theStartPos[1], theStartPos[2]); - //gSquareDebugLocations.push_back(theDebugPoint); - - gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); - gEngfuncs.pEventAPI->EV_PushPMStates(); - gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1); - gEngfuncs.pEventAPI->EV_SetTraceHull(2); - - vec3_t theNewStartPos; - AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); - VectorCopy(theNewStartPos, theStartPos); - - do - { - gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL/*PM_GLASS_IGNORE*/, theEntityToIgnore, &tr ); - - physent_t* pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); - theFoundEntity = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr ); - - // Trace until we hit a worldbrush or non-seethrough - if(!pEntity || (pEntity->iuser3 != AVH_USER3_ALPHA)) - { - // If entity is a "no waypoint" entity we can't build here and we're done - if(pEntity && (pEntity->iuser3 == AVH_USER3_NOBUILD)) - { - if(outReturnCode) - { - *outReturnCode = WAYPOINT_NOBUILD; - } - theDone = true; - } - else - { - // else if texture is NOBUILD, we're done - const char* theTextureHitCStr = gEngfuncs.pEventAPI->EV_TraceTexture(theFoundEntity, tr.endpos, theEndPos); - if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kNoBuildTexture))) - { - if(outReturnCode) - { - *outReturnCode = WAYPOINT_NOBUILD; - } - theDone = true; - } - else if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kSeeThroughTexture))) - { - // Not valid, but allow it to pass through - if(outReturnCode) - { - *outReturnCode = WAYPOINT_SEETHROUGH; - } - } - else - { - // Trace texture sometimes seems to miss entities that the start point lies within. Don't count - // the trace texture if it found the texture of the next entity below it - // else if surface is more flat than vertical - if(tr.plane.normal[2] >= 0.7f) - { - // and if surface isn't under water, lava, in the sky, etc. - int thePointContents = gEngfuncs.PM_PointContents(tr.endpos, NULL); - if(thePointContents == CONTENTS_EMPTY) - { - // and if there's enough room to build - - // we can build here, and we're done - theLegalToBuild = true; - theDone = true; - - if(outReturnCode) - { - *outReturnCode = WAYPOINT_SUCCESS; - } - } - else - { - if(outReturnCode) - { - *outReturnCode = WAYPOINT_CONTENTSFULL; - theDone = true; - } - } - } - else - { - if(outReturnCode) - { - if(theTextureHitCStr) - { - *outReturnCode = WAYPOINT_TOOSTEEP; - } - else - { - *outReturnCode = WAYPOINT_ENTITYHIT; - } - theDone = true; - } - } - } - } - } - - if(theFoundEntity != 0) - { - theEntityToIgnore = theFoundEntity; - } - - if(((tr.fraction >= (1.0f - kFloatTolerance)) || (tr.fraction == 0.0f)) /*&& !tr.startsolid && !tr.allsolid*/) - { - theDone = true; - } - else - { - vec3_t theDiff = theNormal*kHitOffsetAmount; - float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); - if(theLength > kFloatTolerance) - { - VectorAdd(tr.endpos, theDiff, theStartPos); - } - else - { - theDone = true; - } - // Offset a bit so we don't hit again - //VectorMA(tr.endpos, kHitOffsetAmount, theNormal, theStartPos); - } - } while(!theDone); - - gEngfuncs.pEventAPI->EV_PopPMStates(); - - // Always set end location to show where invalid position is - *outLocation = tr.endpos; - - return theLegalToBuild; -} -#endif - -#ifdef AVH_CLIENT -void AvHSHUClientGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint) -{ - vec3_t theStartPos; - vec3_t theEndPos; - VectorCopy(inStartPos, theStartPos); - VectorCopy(inStartPos, outNonSolidPoint); - VectorCopy(inEndPos, theEndPos); - - pmtrace_t tr; - gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL, -1, &tr ); - - // Put this workaround in because a bug in EV_PlayerTrace means that when it starts solid, tr.fraction isn't returned (but it is in UTIL_TraceLine) - if((tr.startsolid) && (tr.fraction == 0.0f)) - { - int theFoundEntity = -1; - bool theDone = false; - - vec3_t theStartToEnd; - VectorSubtract(inEndPos, inStartPos, theStartToEnd); - - gEngfuncs.pEventAPI->EV_SetTraceHull(2); - - float theIncrement = 10.0f/theStartToEnd.Length(); - float theT = 0.0f; - int theNumIterations = 0; - - do - { - theStartPos = inStartPos + theT*theStartToEnd; - gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_WORLD_ONLY, -1, &tr ); - - theNumIterations++; - - // If start point is solid, bisect area and move start point 1/2 between current start and current end - if(tr.startsolid) - { - theT += theIncrement; - } - // else if start point isn't solid, bisect area and move start point back towards original start point - else - { - theDone = true; - } - } while(!theDone && (theNumIterations < 200)); - - // Always set end location to show where invalid position is - if(!theDone) - { - outNonSolidPoint = inStartPos; - } - else - { - outNonSolidPoint = theStartPos; - } - } -} -#endif - -#ifdef AVH_SERVER -void AvHSHUServerGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint) -{ - vec3_t theStartPos; - vec3_t theEndPos; - VectorCopy(inStartPos, theStartPos); - VectorCopy(inStartPos, outNonSolidPoint); - VectorCopy(inEndPos, theEndPos); - - TraceResult tr; - UTIL_TraceLine(theStartPos, theEndPos, ignore_monsters, NULL, &tr); - - bool theDone = false; - - // Put this workaround in because a bug in EV_PlayerTrace means that when it starts solid, tr.fraction isn't returned (but it is in UTIL_TraceLine) - if((tr.fStartSolid) || (tr.flFraction == 0.0f)) - { - int theFoundEntity = -1; - - vec3_t theStartToEnd; - VectorSubtract(inEndPos, inStartPos, theStartToEnd); - - float theIncrement = 10.0f/theStartToEnd.Length(); - float theT = 0.0f; - int theNumIterations = 0; - int thePointContents = 0; - - do - { - theStartPos = inStartPos + theT*theStartToEnd; - thePointContents = UTIL_PointContents(theStartPos); - - theNumIterations++; - - // If start point is solid, bisect area and move start point 1/2 between current start and current end - if((thePointContents == CONTENTS_SOLID) || (thePointContents == CONTENTS_SKY)) - { - theT += theIncrement; - } - // else if start point isn't solid, bisect area and move start point back towards original start point - else - { - theDone = true; - } - } while(!theDone && (theNumIterations < 200)); - - // Always set end location to show where invalid position is - outNonSolidPoint = theStartPos; - } - else - { - VectorCopy(tr.vecEndPos, outNonSolidPoint); - theDone = true; - } - - if(!theDone) - { - // When we don't hit anything, return start point - VectorCopy(inStartPos, outNonSolidPoint); - } - - VectorCopy(outNonSolidPoint, gPMDebugPoint); -} -#endif - -void AvHSHUGetFirstNonSolidPoint(float* inStartPos, float* inEndPos, float* outNonSolidPoint) -{ - vec3_t theStartPos; - vec3_t theEndPos; - - VectorCopy(inStartPos, theStartPos); - VectorCopy(inEndPos, theEndPos); - - vec3_t theNonSolidPoint; - AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNonSolidPoint); - - VectorCopy(theNonSolidPoint, outNonSolidPoint); -} - -void AvHSHUGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint) -{ - #ifdef AVH_CLIENT - AvHSHUClientGetFirstNonSolidPoint(inStartPos, inEndPos, outNonSolidPoint); - - //extern DebugPointListType gSquareDebugLocations; - //DebugPoint theDebugPoint(outNonSolidPoint[0], outNonSolidPoint[1], outNonSolidPoint[2]); - //gSquareDebugLocations.push_back(theDebugPoint); - #endif - - #ifdef AVH_SERVER - AvHSHUServerGetFirstNonSolidPoint(inStartPos, inEndPos, outNonSolidPoint); - #endif -} - -#ifdef AVH_SERVER -bool AvHSHUServerTraceWaypoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t* outLocation, WaypointReturnCode* outReturnCode) -{ - bool theLegalToBuild = false; - bool theDone = false; - edict_t* theEdictToIgnore = NULL; - TraceResult tr; - vec3_t theStartPos; - vec3_t theEndPos; - - VectorCopy(inStartPos, theStartPos); - VectorCopy(inEndPos, theEndPos); - - vec3_t theNormal; - VectorSubtract(inEndPos, inStartPos, theNormal); - VectorNormalize(theNormal); - - vec3_t theNewStartPos; - AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); - VectorCopy(theNewStartPos, theStartPos); - - do - { - UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, theEdictToIgnore, &tr); - - // Trace until we hit a worldbrush or non-seethrough - CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); - CWorld* theWorld = dynamic_cast(theEntityHit); - bool theHitSeethrough = (theEntityHit->pev->iuser3 == AVH_USER3_ALPHA); - if(!theHitSeethrough) - { - // If entity is a "no waypoint" entity we can't build here and we're done - if(theEntityHit && (theEntityHit->pev->iuser3 == AVH_USER3_NOBUILD)) - { - if(outReturnCode) - { - *outReturnCode = WAYPOINT_NOBUILD; - } - theDone = true; - } - else - { - edict_t* theEdict = ENT(theEntityHit->pev); - const char* theTextureHitCStr = TRACE_TEXTURE(theEdict, tr.vecEndPos, theEndPos); - if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kNoBuildTexture))) - { - if(outReturnCode) - { - *outReturnCode = WAYPOINT_NOBUILD; - } - theDone = true; - } - else if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kSeeThroughTexture))) - { - // Do nothing, but allow it to pass through - if(outReturnCode) - { - *outReturnCode = WAYPOINT_SEETHROUGH; - } - } - else - { - // else if surface is more flat than vertical - if(tr.vecPlaneNormal.z >= .7f) - { - // and if surface isn't under water, lava, sky - int thePointContents = UTIL_PointContents(tr.vecEndPos); - if(thePointContents == CONTENTS_EMPTY) - { - // and if there's enough room to build - if(outReturnCode) - { - *outReturnCode = WAYPOINT_SUCCESS; - } - - // we can build here, and we're done - theLegalToBuild = true; - theDone = true; - } - else - { - if(outReturnCode) - { - *outReturnCode = WAYPOINT_CONTENTSFULL; - } - } - } - else - { - if(theTextureHitCStr) - { - *outReturnCode = WAYPOINT_TOOSTEEP; - } - else - { - *outReturnCode = WAYPOINT_ENTITYHIT; - } - theDone = true; - } - } - } - - if(!theWorld) - { - theEdictToIgnore = theEntityHit->edict(); - } - - if(((tr.flFraction >= (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance)) /*&& !tr.fStartSolid && !tr.fAllSolid*/) - { - theDone = true; - } - else - { - vec3_t theDiff = theNormal*kHitOffsetAmount; - float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); - if(theLength > kFloatTolerance) - { - //VectorAdd(tr.vecEndPos, theDiff, theStartPos); - VectorAdd(theStartPos, theDiff, theStartPos); - } - else - { - theDone = true; - } - // Offset a bit so we don't hit again - //VectorMA(tr.vecEndPos, kHitOffsetAmount, theNormal, theStartPos); - } - } - else - { - theEdictToIgnore = theEntityHit->edict(); - if(tr.vecEndPos == theStartPos) - { - vec3_t theDiff = theNormal*kHitOffsetAmount; - float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); - if(theLength > kFloatTolerance) - { - //VectorAdd(tr.vecEndPos, theDiff, theStartPos); - VectorAdd(theStartPos, theDiff, theStartPos); - } - } - else - { - VectorCopy(tr.vecEndPos, theStartPos); - } -// vec3_t theEntityPosition = AvHSHUGetRealLocation(theEntityHit->pev->origin, theEntityHit->pev->mins, theEntityHit->pev->maxs); -// //VectorCopy(theEntityPosition, theStartPos); -// theStartPos[2] = theEntityPosition[2]; - } - } while(!theDone); - - // Always set end location to show where invalid position is - *outLocation = tr.vecEndPos; - - return theLegalToBuild; -} -#endif - -bool AvHSHUTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour) -{ - bool theSuccess = false; - - outPlayerWasHit = false; - outUserThree = 0; - outUserFour = 0; - - // On client, set up players for prediction and do a PM_TraceLine - #ifdef AVH_CLIENT - theSuccess = AvHSUClientTraceTangible(inStartPos, inEndPos, outIndex, outLocation, outTeamNumber, outPlayerWasHit, outUserThree, outUserFour); - #endif - - // On the server, do a UTIL_TraceLine - #ifdef AVH_SERVER - theSuccess = AvHSUServerTraceTangible(inStartPos, inEndPos, outIndex, outLocation, outTeamNumber, outPlayerWasHit, outUserThree, outUserFour); - #endif - - return theSuccess; -} - -bool AvHSHUTraceTangible(const Vector& inPointOfView, const Vector& inNormRay, int* outUserThree, vec3_t* outLocation, AvHTeamNumber* outTeamNumber, bool* outPlayerWasHit) -{ - bool theSuccess = false; - vec3_t theTraceStart; - vec3_t theTraceEnd; - vec3_t theFoundLocation; - int theFoundIndex = -1; - int theUserThree = 0; - int theUserFour = 0; - AvHTeamNumber theTeamOfThingHit; - bool thePlayerHit = false; - - // Offset a little so we don't hit the commander - VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theTraceStart); - VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theTraceEnd); - - if(!outUserThree) - outUserThree = &theUserThree; - - if(!outLocation) - outLocation = &theFoundLocation; - - if(!outTeamNumber) - outTeamNumber = &theTeamOfThingHit; - - if(!outPlayerWasHit) - outPlayerWasHit = &thePlayerHit; - - theSuccess = AvHSHUTraceTangible(theTraceStart, theTraceEnd, theFoundIndex, *outLocation, *outTeamNumber, *outPlayerWasHit, *outUserThree, theUserFour); - - return theSuccess; -} - -bool AvHSHUTraceVerticalTangible(float inX, float inY, float inZ, int& outUserThree, float& outHeight) -{ - bool theSuccess = false; - - // Create a start pos above - vec3_t theStartPos(inX, inY, inZ - 100.0f); - //vec3_t theEndPos(inX, inY, inZ - 800.0f/*-4096.0f*/); - vec3_t theEndPos(inX, inY, (float)kWorldMinimumZ); - -// #ifdef AVH_CLIENT -// extern DebugPointListType gTriDebugLocations; -// bool theDrawDebug = true;//(rand() % 100 == 0); -// -// if(theDrawDebug) -// { -// DebugPoint thePoint; -// thePoint.x = theStartPos.x; -// thePoint.y = theStartPos.y; -// thePoint.z = theStartPos.z; -// gTriDebugLocations.push_back(thePoint); -// -// thePoint.z = theEndPos.z; -// gTriDebugLocations.push_back(thePoint); -// } -// -// #endif - - int theIndex; - vec3_t theEndLocation; - AvHTeamNumber theTeam; - bool thePlayerHit; - int theUserFour; - - theSuccess = AvHSHUTraceTangible(theStartPos, theEndPos, theIndex, theEndLocation, theTeam, thePlayerHit, outUserThree, theUserFour); - if(theSuccess) - { - outHeight = theEndLocation.z; - ASSERT(outHeight <= inZ); - } - - return theSuccess; -} - -const char* AvHSHUGetCommonSoundName(bool inIsAlien, WeaponHUDSound inHUDSound) -{ - const char* theSoundName = NULL; - - switch(inHUDSound) - { - case WEAPON_SOUND_HUD_ON: - theSoundName = inIsAlien ? "common/wpn_hudon-a.wav" : "common/wpn_hudon.wav"; - break; - case WEAPON_SOUND_HUD_OFF: - theSoundName = inIsAlien ? "common/wpn_hudoff-a.wav" : "common/wpn_hudoff.wav"; - break; - case WEAPON_SOUND_MOVE_SELECT: - theSoundName = inIsAlien ? "common/wpn_moveselect-a.wav" : "common/wpn_moveselect.wav"; - break; - case WEAPON_SOUND_SELECT: - theSoundName = inIsAlien ? "common/wpn_select-a.wav" : "common/wpn_select.wav"; - break; - case WEAPON_SOUND_DENYSELECT: - theSoundName = inIsAlien ? "common/wpn_denyselect-a.wav" : "common/wpn_denyselect.wav"; - break; - } - - return theSoundName; -} - - -// Values for fuser1: 0-1 means it's building. 2-3 means it's researching (see kResearchFuser1Base), 3-4 is "energy" (returns both building and researching) -const float kResearchFuser1Base = 2.0f; -const float kEnergyFuser1Base = 3.0f; - -void AvHSHUGetBuildResearchState(int inUser3, int inUser4, float inFuser1, bool& outIsBuilding, bool& outIsResearching, float& outNormalizedPercentage) -{ - outIsBuilding = outIsResearching = false; - - float theStatus = (inFuser1/kNormalizationNetworkFactor); - - if((GetHasUpgrade(inUser4, MASK_BUILDABLE) || (inUser3 == AVH_USER3_WELD)) && !GetHasUpgrade(inUser4, MASK_RECYCLING)) - { - if((theStatus >= 0.0f) && (theStatus <= 1.0f)) - { - outNormalizedPercentage = theStatus; - outIsBuilding = true; - } - if(inUser3 == AVH_USER3_WELD) - { - if(outNormalizedPercentage == -1) - { - outIsBuilding = outIsResearching = false; - } - } - } - else - { - if((theStatus > kResearchFuser1Base) && (theStatus <= (1.0f + kResearchFuser1Base))) - { - outNormalizedPercentage = theStatus - kResearchFuser1Base; - outIsResearching = true; - } - else if((theStatus > kEnergyFuser1Base) && (theStatus <= (1.0f + kEnergyFuser1Base))) - { - // Set "energy" - outNormalizedPercentage = theStatus - kEnergyFuser1Base; - outIsBuilding = outIsResearching = true; - } - else - { - outNormalizedPercentage = 1.0f; - } - } -} - -void AvHSHUSetEnergyState(int inUser3, float &outFuser1, float inNormalizedPercentage) -{ - outFuser1 = (kEnergyFuser1Base + inNormalizedPercentage)*kNormalizationNetworkFactor; -} - -void AvHSHUSetBuildResearchState(int inUser3, int inUser4, float &outFuser1, bool inTrueBuildOrFalseResearch, float inNormalizedPercentage) -{ - //ASSERT(GetHasUpgrade(inUser4, MASK_BUILDABLE) /*|| GetHasUpgrade(inUser4, MASK_SELECTABLE)*/ || (inUser3 == AVH_USER3_WELD)); - - if(inNormalizedPercentage != -1) - { - inNormalizedPercentage = min(inNormalizedPercentage, 1.0f); - inNormalizedPercentage = max(inNormalizedPercentage, 0.0f); - } - - // Build - if(inTrueBuildOrFalseResearch) - { - //ASSERT(GetHasUpgrade(inUser4, MASK_BUILDABLE) || (inUser3 == AVH_USER3_WELD)); - - outFuser1 = inNormalizedPercentage*kNormalizationNetworkFactor; - } - // Research - else - { - outFuser1 = (kResearchFuser1Base + inNormalizedPercentage)*kNormalizationNetworkFactor; - } -} - - -string AvHSHUBuildExecutableScriptName(const string& inScriptName, const string& inCurrentMapName) -{ - string theExecutableScriptName = string(getModDirectory()) + string("/") + kScriptsDirectory + string("/") + inCurrentMapName + string("/") + inScriptName; - - return theExecutableScriptName; -} - -string AvHSHUGetTimeDateString() -{ - string theTimeDateString(""); - - time_t theTimeThingie = time ((time_t *) NULL); - struct tm* theTime = localtime(&theTimeThingie); - - if(theTime) - { - // 04/22/03, 16:59:12 - char theBuffer[512]; - sprintf(theBuffer, "%.2d/%.2d/%.2d, %.2d:%.2d:%.2d", (theTime->tm_mon + 1), theTime->tm_mday, (theTime->tm_year - 100), theTime->tm_hour, theTime->tm_min, theTime->tm_sec); - theTimeDateString = theBuffer; - } - - return theTimeDateString; -} - -bool AvHSHUGetNameOfLocation(const AvHBaseInfoLocationListType& inLocations, vec3_t inLocation, string& outLocation) -{ - bool theSuccess = false; - - // Look at our current position, and see if we lie within of the map locations - for(AvHBaseInfoLocationListType::const_iterator theIter = inLocations.begin(); theIter != inLocations.end(); theIter++) - { - if(theIter->GetIsPointInRegion(inLocation)) - { - outLocation = theIter->GetLocationName(); - theSuccess = true; - break; - } - } - - return theSuccess; -} - -// Also check AvHSUGetAlwaysPlayAlert to make sure some alerts are always triggered -bool AvHSHUGetForceHUDSound(AvHHUDSound inHUDSound) -{ - bool theForceSound = false; - - switch(inHUDSound) - { - case HUD_SOUND_ALIEN_HIVE_COMPLETE: - case HUD_SOUND_ALIEN_HIVE_DYING: - case HUD_SOUND_ALIEN_HIVE_ATTACK: - case HUD_SOUND_MARINE_CCUNDERATTACK: - case HUD_SOUND_SQUAD1: - case HUD_SOUND_SQUAD2: - case HUD_SOUND_SQUAD3: - case HUD_SOUND_SQUAD4: - case HUD_SOUND_SQUAD5: - theForceSound = true; - break; - } - - return theForceSound; -} +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHSharedUtil.cpp $ +// $Date: 2002/11/12 22:39:25 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHSharedUtil.cpp,v $ +// Revision 1.33 2002/11/12 22:39:25 Flayra +// - Logging changes for Psychostats compatibility +// +// Revision 1.32 2002/10/24 21:42:32 Flayra +// - Don't give waypoint to selected troops when scanning +// +// Revision 1.31 2002/10/19 21:19:49 Flayra +// - Debugging info for linux +// +// Revision 1.30 2002/10/16 01:07:26 Flayra +// - Added official sizes that HL supports (ugh) +// - Added utility function for drawing range of ghost building, but it's unused +// +// Revision 1.29 2002/10/03 19:07:40 Flayra +// - Hives can never be blocked by func_resources +// +// Revision 1.28 2002/09/25 20:50:58 Flayra +// - Allow small items to be built on entities (health can be dropped right on players) +// +// Revision 1.27 2002/09/23 22:31:40 Flayra +// - Updated team 0 colors so dictation can be read in readyroom +// - Draw range for prototype lab, observatory and sensory chamber +// - Alien building rings +// - Don't allow non-resource buildings built too close to func_resources +// - Added heavy armor and jetpacks +// +// Revision 1.26 2002/09/09 20:06:43 Flayra +// - New observatory artwork +// +// Revision 1.25 2002/08/31 18:01:03 Flayra +// - Work at VALVe +// +// Revision 1.24 2002/08/16 02:47:06 Flayra +// - Fixed bug where not all the entities were iterated through +// - Support for ring-drawing (but not for all entities) +// +// Revision 1.23 2002/08/09 00:51:11 Flayra +// - Cleaned up useless special casing of tracetangible, fixed some problems where buildings could be built on players +// +// Revision 1.22 2002/08/02 21:50:05 Flayra +// - Made more general, by detecting all entities, not just players +// +// Revision 1.21 2002/07/24 18:45:43 Flayra +// - Linux and scripting changes +// +// Revision 1.20 2002/07/23 17:26:49 Flayra +// - Siege don't require a nearby turret factory, only add built buildings for range detection, build problems on client (point contents failing on rough surfaces) +// +// Revision 1.19 2002/07/08 17:17:44 Flayra +// - Reworked colors, moved functions into server util +// +// Revision 1.18 2002/07/01 21:46:39 Flayra +// - Added support for generic building ranges, fixed morphing problems +// +// Revision 1.17 2002/06/25 18:18:00 Flayra +// - Renamed buildings, better is-area-free detection +// +// Revision 1.16 2002/06/03 16:58:13 Flayra +// - Renamed weapons factory and armory, hive is now a buildable +// +// Revision 1.15 2002/05/28 18:13:43 Flayra +// - Changed "entity blocked by whatever" message to be logged for PR/screenshot purposes +// +// Revision 1.14 2002/05/23 02:33:20 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== + +#ifdef AVH_SERVER +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "types.h" +#endif + +#ifdef AVH_CLIENT +#include "cl_dll/hud.h" +#include "cl_dll/cl_util.h" +#endif + +#include "mod/AvHSharedUtil.h" +#include "mod/AvHSelectionHelper.h" +#include "mod/AvHConstants.h" + +#ifdef AVH_SERVER +#include "mod/AvHPlayer.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHEntities.h" +#include "mod/AvHWeldable.h" +#include "mod/AvHGamerules.h" +#include "dlls/cfuncwall.h" +//#include "common/com_model.h" +//int NS_PointContents(const hull_t *hull, int num, float p[3]); +#endif + +#include "pm_shared/pm_defs.h" +#include "pm_shared/pm_shared.h" + +#ifdef AVH_CLIENT +#include "pm_shared/pm_debug.h" +//extern DebugPointListType gTriDebugLocations; +//extern DebugPointListType gSquareDebugLocations; +#endif + +#include "util/MathUtil.h" +#include "util/STLUtil.h" + +#include "common/vector_util.h" + +#ifdef AVH_CLIENT +#include "cl_dll/eventscripts.h" + +#include "common/r_efx.h" +#include "common/event_api.h" +#include "common/event_args.h" +#include "cl_dll/in_defs.h" +#endif + +#include "common/com_model.h" + +#include "mod/AvHSpecials.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "dlls/turretconst.h" +#include "mod/AvHMarineWeaponConstants.h" +#include "mod/AvHHulls.h" +#include "mod/AvHAlienEquipmentConstants.h" + +#include "mod/CollisionUtil.h" +/* +bool NS_BoxesOverlap(float origin1[3], float size1[3], float origin2[3], float size2[3]); +int NS_PointContents(const hull_t *hull, int num, float p[3]); +int NS_BoxContents(const hull_t *hull, int num, float mins[3], float maxs[3]); +int NS_GetValveHull(int inHull); +void NS_TraceLine(const hull_t* hull, float srcPoint[3], float dstPoint[3], trace_t* trace); +*/ +#include +extern playermove_t* pmove; +vec3_t AvHSHUGetRealLocation(const vec3_t& inLocation, const vec3_t& inMinBox, const vec3_t& inMaxBox); + + +vec3_t gPMDebugPoint; + +int kTeamColors[iNumberOfTeamColors][3] = +{ + { 255, 255, 255 }, // White (default) + +// { 125, 165, 210 }, // Blue (marine 1) + { 0, 153, 255 }, // Blue (marine 1) + +// { 255, 170, 0 }, // HL orange (alien 1) + { 255, 160, 0 }, // HL orange (alien 1) + + { 145, 215, 140 }, // Green (marine 2) + { 200, 90, 70 }, // Red (alien 2) + { 255, 255, 255 } // White (spectator) +}; + +float kFTeamColors[iNumberOfTeamColors][3] = +{ + { kTeamColors[0][0] / 255.0f, kTeamColors[0][1] / 255.0f, kTeamColors[0][2] / 255.0f }, + { kTeamColors[1][0] / 255.0f, kTeamColors[1][1] / 255.0f, kTeamColors[1][2] / 255.0f }, + { kTeamColors[2][0] / 255.0f, kTeamColors[2][1] / 255.0f, kTeamColors[2][2] / 255.0f }, + { kTeamColors[3][0] / 255.0f, kTeamColors[3][1] / 255.0f, kTeamColors[3][2] / 255.0f }, + { kTeamColors[4][0] / 255.0f, kTeamColors[4][1] / 255.0f, kTeamColors[4][2] / 255.0f }, + { kTeamColors[5][0] / 255.0f, kTeamColors[5][1] / 255.0f, kTeamColors[5][2] / 255.0f } +}; + + +// Official allowed sizes +// {0, 0, 0 } { 0, 0, 0 } 0x0x0 +// { -16, -16, -18 } { 16, 16, 18 } 32x32x36 +// { -16, -16, -36 } { 16, 16, 36 } 32x32x72 +// { -32, -32, -32 } { 32, 32, 32 } 64x64x64 + +//#define kBuilding1MinSize Vector(-14.33, -14.84, 0.02) +////#define kBuilding1MaxSize Vector(21.61, 14.86, 66.9686) +//#define kBuilding1MaxSize Vector(14.33, 14.86, 66.9686) +// +//#define kBuilding2MinSize Vector(-25.0, -25.0, 0.02) +//#define kBuilding2MaxSize Vector(25.0, 25.0, 66.9686) + +#define kResourceMinSize Vector(-16.0, -16.0, 0.0) +#define kResourceMaxSize Vector(16.0, 16.0, 66.9486) + +// Tried 100, 110, still jitters. Shrink tower down a bit? +#define kAlienResourceMinSize Vector(-16.0, -16.0, 0.0) +#define kAlienResourceMaxSize Vector(16.0, 16.0, 100.0) // was Vector(16.0, 16.0, 80.7443) + +//physent_t* AvHSUGetEntity(int inPhysIndex) +//{ +// physent_t* theTarget = NULL; +// int i = 0; +// +// if(inPhysIndex > 0) +// { +// #ifdef AVH_CLIENT +// gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); +// +// // Store off the old count +// //gEngfuncs.pEventAPI->EV_PushPMStates(); +// +// // Now add in all of the players. +// gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); +// +// for (i = 0; i < MAX_PHYSENTS; i++) +// //for (i = 0; i < pmove->numphysent; i++) +// { +// physent_t* thePotentialCandidate = gEngfuncs.pEventAPI->EV_GetPhysent(i); +// if ( thePotentialCandidate && (thePotentialCandidate->info == inPhysIndex)) +// { +// theTarget = &pmove->physents[i]; +// break; +// } +// } +// +// //gEngfuncs.pEventAPI->EV_PopPMStates(); +// #endif +// +// // Check phys entities on server +// #ifdef AVH_SERVER +// for (i = 0; i < MAX_PHYSENTS; i++) +// //for (i = 0; i < pmove->numphysent; i++) +// { +// if ( pmove->physents[i].info == inPhysIndex ) +// { +// theTarget = &pmove->physents[i]; +// break; +// } +// } +// +// //CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); +// #endif +// } +// +// return theTarget; +//} + +const char* AvHSHUGetClassNameFromUser3(AvHUser3 inUser3) +{ + const char* theClassName = ""; + + switch(inUser3) + { + case AVH_USER3_MARINE_PLAYER: + theClassName = "soldier"; + break; + + case AVH_USER3_COMMANDER_PLAYER: + theClassName = "commander"; + break; + + case AVH_USER3_ALIEN_PLAYER1: + theClassName = "skulk"; + break; + + case AVH_USER3_ALIEN_PLAYER2: + theClassName = "gorge"; + break; + + case AVH_USER3_ALIEN_PLAYER3: + theClassName = "lerk"; + break; + + case AVH_USER3_ALIEN_PLAYER4: + theClassName = "fade"; + break; + + case AVH_USER3_ALIEN_PLAYER5: + theClassName = "onos"; + break; + + case AVH_USER3_ALIEN_EMBRYO: + theClassName = "gestate"; + break; + } + + return theClassName; +} + + +// Pass in -1 to get all entities with a non-zero iuser3 +void AvHSHUGetEntities(int inUser3, EntityListType& outEntities) +{ + #ifdef AVH_SERVER + FOR_ALL_BASEENTITIES(); + if((theBaseEntity->pev->iuser3 == inUser3) || ((inUser3 == -1) && (theBaseEntity->pev->iuser3 > 0))) + { + outEntities.push_back(theBaseEntity->entindex()); + } + END_FOR_ALL_BASEENTITIES(); + + #endif + + #ifdef AVH_CLIENT + + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + + // Store off the old count + gEngfuncs.pEventAPI->EV_PushPMStates(); + + // Now add in all of the players. + gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); + + physent_t* theEntity = NULL; + int theNumEnts = pmove->numphysent; + for (int i = 0; i < theNumEnts; i++) + { + theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(i); + if((theEntity->iuser3 == inUser3) || ((inUser3 == -1) && (theEntity->iuser3 > 0))) + { + outEntities.push_back(i); + } + } + + gEngfuncs.pEventAPI->EV_PopPMStates(); + #endif +} + +bool AvHSHUGetEntityLocation(int inEntity, vec3_t& outLocation) +{ + bool theSuccess = false; + + #ifdef AVH_SERVER + edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity); + if(pEdict && !pEdict->free) + { + CBaseEntity* theEntity = CBaseEntity::Instance(pEdict); + if(theEntity) + { + outLocation = AvHSHUGetRealLocation(theEntity->pev->origin, theEntity->pev->mins, theEntity->pev->maxs); + theSuccess = true; + } + } + #endif + + #ifdef AVH_CLIENT + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + + // Store off the old count + gEngfuncs.pEventAPI->EV_PushPMStates(); + + // Now add in all of the players. + gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); + + physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity); + + gEngfuncs.pEventAPI->EV_PopPMStates(); + + if(theEntity) + { + outLocation = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs); + theSuccess = true; + } + #endif + + return theSuccess; +} + + +bool AvHSHUGetEntityIUser3(int inEntity, AvHUser3& outIUser3) +{ + bool theSuccess = false; + + #ifdef AVH_SERVER + edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity); + if(pEdict && !pEdict->free) + { + CBaseEntity* theEntity = CBaseEntity::Instance(pEdict); + if(theEntity) + { + outIUser3 = (AvHUser3)theEntity->pev->iuser3; + theSuccess = true; + } + } +#endif + +#ifdef AVH_CLIENT + // Store off the old count + gEngfuncs.pEventAPI->EV_PushPMStates(); + + // Now add in all of the players. + gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1); + + physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity); + if(theEntity) + { + outIUser3 = (AvHUser3)theEntity->iuser3; + theSuccess = true; + } + gEngfuncs.pEventAPI->EV_PopPMStates(); + +#endif + return theSuccess; +} + + +bool AvHSHUGetEntityIUser4(int inEntity, int& outIUser4) +{ + bool theSuccess = false; + + #ifdef AVH_SERVER + edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity); + if(pEdict && !pEdict->free) + { + CBaseEntity* theEntity = CBaseEntity::Instance(pEdict); + if(theEntity) + { + outIUser4 = theEntity->pev->iuser4; + theSuccess = true; + } + } +#endif + +#ifdef AVH_CLIENT + physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity); + if(theEntity) + { + outIUser4 = theEntity->iuser4; + theSuccess = true; + } +#endif + + return theSuccess; +} + + +vec3_t AvHSHUGetRealLocation(const vec3_t& inLocation, const vec3_t& inMinBox, const vec3_t& inMaxBox) +{ + vec3_t outLocation; + VectorCopy(inLocation, outLocation); + + if((outLocation.x == outLocation.y) && (outLocation.y == outLocation.z) && (outLocation.z == 0.0f)) + { + outLocation.x = (inMinBox.x + inMaxBox.x)/2.0f; + outLocation.y = (inMinBox.y + inMaxBox.y)/2.0f; + outLocation.z = (inMinBox.z + inMaxBox.z)/2.0f; + } + return outLocation; +} + +// Returns range of user3, whatever that means. Returns -1 for no meaningful range. +int AvHSHUGetDrawRangeForUser3(AvHUser3 inUser3) +{ + int theRange = -1; + + switch(inUser3) + { + case AVH_USER3_COMMANDER_STATION: + theRange = BALANCE_VAR(kCommandStationBuildDistance); + break; + + case AVH_USER3_FUNC_RESOURCE: + theRange = BALANCE_VAR(kResourceTowerBuildDistanceTolerance); + break; + + case AVH_USER3_TURRET_FACTORY: + case AVH_USER3_ADVANCED_TURRET_FACTORY: + theRange = BALANCE_VAR(kTurretFactoryBuildDistance); + break; + + case AVH_USER3_ARMORY: + case AVH_USER3_ADVANCED_ARMORY: + theRange = BALANCE_VAR(kArmoryBuildDistance); + break; + + case AVH_USER3_TURRET: + theRange = BALANCE_VAR(kTurretRange); + break; + + case AVH_USER3_SIEGETURRET: + // TODO: Figure out a way to return minimum range also? + theRange = BALANCE_VAR(kSiegeTurretRange); + break; + + case AVH_USER3_PROTOTYPE_LAB: + theRange = BALANCE_VAR(kArmorDropRange); + break; + + case AVH_USER3_OBSERVATORY: + theRange = BALANCE_VAR(kObservatoryXYDetectionRadius); + break; + + case AVH_USER3_SENSORY_CHAMBER: + theRange = BALANCE_VAR(kSensoryChamberRange); + break; + } + + return theRange; +} + +int AvHSHUGetDrawRangeForMessageID(AvHMessageID inMessageID) +{ + AvHUser3 theUser3 = AVH_USER3_NONE; + + switch(inMessageID) + { + case BUILD_COMMANDSTATION: + theUser3 = AVH_USER3_COMMANDER_STATION; + break; + + case BUILD_TURRET: + theUser3 = AVH_USER3_TURRET; + break; + + case BUILD_SIEGE: + theUser3 = AVH_USER3_SIEGETURRET; + break; + + case BUILD_OBSERVATORY: + theUser3 = AVH_USER3_OBSERVATORY; + break; + + case BUILD_TURRET_FACTORY: + theUser3 = AVH_USER3_TURRET_FACTORY; + break; + + case BUILD_PROTOTYPE_LAB: + theUser3 = AVH_USER3_PROTOTYPE_LAB; + break; + + case BUILD_ARMORY: + theUser3 = AVH_USER3_ARMORY; + break; + } + + return AvHSHUGetDrawRangeForUser3(theUser3); +} + + +// Specify whether we draw rings for this entity or not, and draw them at different sizes for aesthetics. The scalar might go away when the +// artwork all has the correct bounding boxes. +bool AvHSHUGetDrawRingsForUser3(AvHUser3 inUser3, float& outScalar) +{ + bool theDrawRings = false; + float theScalar = 1.0f; + + switch(inUser3) + { + case AVH_USER3_MARINE_PLAYER: + theScalar = .9f; + theDrawRings = true; + break; + + case AVH_USER3_RESTOWER: + theScalar = 1.6f; + theDrawRings = true; + break; + + case AVH_USER3_INFANTRYPORTAL: + theScalar = 1.35f; + theDrawRings = true; + break; + + case AVH_USER3_ARMORY: + case AVH_USER3_ADVANCED_ARMORY: + theScalar = 1.35f; + theDrawRings = true; + break; + + case AVH_USER3_COMMANDER_STATION: + theScalar = 1.4f; + theDrawRings = true; + break; + + case AVH_USER3_PHASEGATE: + theScalar = 2.0f; + theDrawRings = true; + break; + + case AVH_USER3_OBSERVATORY: + theScalar = .9f; + theDrawRings = true; + break; + + case AVH_USER3_TURRET_FACTORY: + case AVH_USER3_ADVANCED_TURRET_FACTORY: + theScalar = 1.6f; + theDrawRings = true; + break; + + case AVH_USER3_TURRET: + theScalar = .7f; + theDrawRings = true; + break; + + case AVH_USER3_NUKEPLANT: + theScalar = 1.5f; + theDrawRings = true; + break; + + case AVH_USER3_ARMSLAB: + case AVH_USER3_PROTOTYPE_LAB: + case AVH_USER3_CHEMLAB: + case AVH_USER3_MEDLAB: + case AVH_USER3_SIEGETURRET: + theScalar = 1.3f; + theDrawRings = true; + break; + + // Alien buildings + case AVH_USER3_DEFENSE_CHAMBER: + case AVH_USER3_OFFENSE_CHAMBER: + case AVH_USER3_MOVEMENT_CHAMBER: + case AVH_USER3_SENSORY_CHAMBER: + case AVH_USER3_ALIENRESTOWER: + theScalar = 1.3f; + theDrawRings = true; + break; + + case AVH_USER3_ALIEN_PLAYER1: + theScalar = .6f; + theDrawRings = true; + break; + case AVH_USER3_ALIEN_PLAYER2: + theScalar = 1.0f; + theDrawRings = true; + break; + case AVH_USER3_ALIEN_PLAYER3: + theScalar = 1.0; + theDrawRings = true; + break; + case AVH_USER3_ALIEN_PLAYER4: + theScalar = 1.3f; + theDrawRings = true; + break; + case AVH_USER3_ALIEN_PLAYER5: + theScalar = 1.2f; + theDrawRings = true; + break; + } + + if(theDrawRings) + { + outScalar = theScalar; + } + + return theDrawRings; +} + +bool AvHSHUGetBuildRegions(AvHMessageID inMessageID, EntityListType& outEntities, IntList& outRanges, float& outZAdjustment, bool& outSnapToLocation) +{ + bool theFoundTech = false; + + typedef vector User3ListType; + User3ListType theUser3s; + + outZAdjustment = 0.0f; + outSnapToLocation = false; + + switch(inMessageID) + { + case BUILD_RESOURCES: + case ALIEN_BUILD_RESOURCES: + theUser3s.push_back(AVH_USER3_FUNC_RESOURCE); + outZAdjustment = (kFuncResourceMaxSize.z - kFuncResourceMinSize.z); + outSnapToLocation = true; + break; + + case BUILD_INFANTRYPORTAL: + theUser3s.push_back(AVH_USER3_COMMANDER_STATION); + break; + + case BUILD_TURRET: + theUser3s.push_back(AVH_USER3_TURRET_FACTORY); + theUser3s.push_back(AVH_USER3_ADVANCED_TURRET_FACTORY); + break; + + case BUILD_SIEGE: + theUser3s.push_back(AVH_USER3_ADVANCED_TURRET_FACTORY); + break; + + case BUILD_MINES: + case BUILD_WELDER: + case BUILD_SHOTGUN: + theUser3s.push_back(AVH_USER3_ARMORY); + theUser3s.push_back(AVH_USER3_ADVANCED_ARMORY); + break; + + case BUILD_HMG: + case BUILD_GRENADE_GUN: + theUser3s.push_back(AVH_USER3_ADVANCED_ARMORY); + break; + + case BUILD_HEAVY: + case BUILD_JETPACK: + theUser3s.push_back(AVH_USER3_PROTOTYPE_LAB); + break; + } + + if(theUser3s.size() > 0) + { + for(User3ListType::iterator theUser3Iter = theUser3s.begin(); theUser3Iter != theUser3s.end(); theUser3Iter++) + { + EntityListType theEntities; + AvHSHUGetEntities(*theUser3Iter, theEntities); + + for(EntityListType::iterator theEntityIter = theEntities.begin(); theEntityIter != theEntities.end(); theEntityIter++) + { + // Only add buildings that are fully built + int theIuser4 = 0; + if(AvHSHUGetEntityIUser4(*theEntityIter, theIuser4)) + { + if(!GetHasUpgrade(theIuser4, MASK_BUILDABLE)) + { + outEntities.push_back(*theEntityIter); + outRanges.push_back(AvHSHUGetDrawRangeForUser3(*theUser3Iter)); + } + } + } + } + theFoundTech = true; + } + + return theFoundTech; +} + + +// : 0000291 -- allows listed structures to be dropped on resource towers +bool AvHSHUGetIsDroppableOnRTs(AvHMessageID inMessageID) +{ + switch (inMessageID) + { + case BUILD_HEALTH: + case BUILD_AMMO: + case BUILD_MINES: + case BUILD_WELDER: + case BUILD_SHOTGUN: + case BUILD_HMG: + case BUILD_GRENADE_GUN: + case BUILD_CAT: + case BUILD_HEAVY: + case BUILD_JETPACK: + case BUILD_RESOURCES: + case ALIEN_BUILD_RESOURCES: + case ALIEN_BUILD_HIVE: + return true; + default: + return false; + } +} +// : + +bool AvHSHUGetIsMarineStructure(AvHMessageID inMessageID) +{ + + switch (inMessageID) + { + + case BUILD_INFANTRYPORTAL: + case BUILD_RESOURCES: + case BUILD_TURRET_FACTORY: + case BUILD_ARMSLAB: + case BUILD_PROTOTYPE_LAB: + case BUILD_ARMORY: + case BUILD_NUKE_PLANT: + case BUILD_OBSERVATORY: + case BUILD_PHASEGATE: + case BUILD_TURRET: + case BUILD_SIEGE: + case BUILD_COMMANDSTATION: + return true; + default: + return false; + + } + +} + +bool AvHSHUGetIsMarineStructure(AvHUser3 inUser3) +{ + + switch (inUser3) + { + case AVH_USER3_COMMANDER_STATION: + case AVH_USER3_TURRET_FACTORY: + case AVH_USER3_ARMORY: + case AVH_USER3_ADVANCED_ARMORY: + case AVH_USER3_ARMSLAB: + case AVH_USER3_PROTOTYPE_LAB: + case AVH_USER3_OBSERVATORY: + case AVH_USER3_CHEMLAB: + case AVH_USER3_MEDLAB: + case AVH_USER3_NUKEPLANT: + case AVH_USER3_TURRET: + case AVH_USER3_SIEGETURRET: + case AVH_USER3_RESTOWER: + case AVH_USER3_INFANTRYPORTAL: + case AVH_USER3_PHASEGATE: + case AVH_USER3_ADVANCED_TURRET_FACTORY: + return true; + default: + return false; + } + +} + +void AvHSHUGetMinBuildRadiusViolations(AvHMessageID inMessageID, vec3_t& inLocation, EntityListType& outViolations) +{ + + // Enforce a minimum build radius for marine structures. + + if (AvHSHUGetIsMarineStructure(inMessageID)) + { + EntityListType theEntities; + AvHSHUGetEntities(-1, theEntities); + + vec3_t theMinSize, theMaxSize; + + AvHSHUGetSizeForTech(inMessageID, theMinSize, theMaxSize); + float theMaxRadius1 = max(-min(theMinSize.x, theMinSize.y), max(theMaxSize.x, theMaxSize.y)); + + for (EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++) + { + + AvHUser3 theUser3; + AvHSHUGetEntityIUser3(*theIter, theUser3); + + bool theEntityCouldBlock = AvHSHUGetIsMarineStructure(theUser3); + if(inMessageID != BUILD_RESOURCES) + { + theEntityCouldBlock |= (theUser3 == AVH_USER3_FUNC_RESOURCE); + } + + bool theEntityIsSolid = false; + +#ifdef AVH_SERVER + edict_t* theEntity = INDEXENT(*theIter); + + if(theEntity) + theEntityIsSolid = (theEntity->v.solid == SOLID_BBOX); +#endif + +#ifdef AVH_CLIENT + + physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theIter); + + if(theEntity) + theEntityIsSolid = (theEntity->solid == SOLID_BBOX); + +#endif + // : 0000291 + // It's possible to place "on" marines if you're offset a little from center. This code and + // associated changes below and in AvHHudRender.cpp is to enforce a build distance around players + // in the same way as buildings to prevent this exploit. + if (theUser3 == AVH_USER3_MARINE_PLAYER) + { + theEntityIsSolid = true; + theEntityCouldBlock = true; + } + + if (theEntityCouldBlock && theEntityIsSolid) + { + AvHSHUGetSizeForUser3(theUser3, theMinSize, theMaxSize); + float theMaxRadius2 = max(max(theMinSize.x, theMaxSize.x), max(theMinSize.y, theMaxSize.y)); + + vec3_t theLocation; + if(AvHSHUGetEntityLocation(*theIter, theLocation)) + { + vec3_t theXYInLocation = inLocation; + vec3_t theXYTheLocation = theLocation; + + theXYInLocation.z = 0; + theXYTheLocation.z = 0; + + float theDistance = VectorDistance((float*)&theXYInLocation, (float*)&theXYTheLocation); + // : 0000291 + // It's possible to place "on" marines if you're offset a little from center. This code and + // associated changes above and in AvHHudRender.cpp is to enforce a build distance around players + // in the same way as buildings to prevent this exploit. + float theMinMarineBuildDistance; + if (theUser3 == AVH_USER3_MARINE_PLAYER) { + theMinMarineBuildDistance = BALANCE_VAR(kMinMarinePlayerBuildDistance); + } + else + { + theMinMarineBuildDistance = BALANCE_VAR(kMinMarineBuildDistance); + } + // : + if (theDistance < theMinMarineBuildDistance + theMaxRadius1 + theMaxRadius2) + { + outViolations.push_back(*theIter); + } + } + } + } + } +} + +bool AvHSHUGetAreSpecialBuildingRequirementsMet(AvHMessageID inMessageID, vec3_t& inLocation) +{ + bool theRequirementsMet = false; + EntityListType theEntities; + IntList theDistanceRequirements; + float theZAdjustment = 0.0f; + bool theSnapToLocation = false; + + if(inMessageID == ALIEN_BUILD_HIVE) + { + // Look for inactive hives within radius + EntityListType theEntities; + + // Look for a unoccupied hive spot within range + AvHSHUGetEntities(AVH_USER3_HIVE, theEntities); + + for(EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++) + { + vec3_t theLocation; + if(AvHSHUGetEntityLocation(*theIter, theLocation)) + { + // Set z's equal to check 2D distance only + inLocation.z = theLocation.z; + float theDistance = VectorDistance((float*)&inLocation, (float*)&theLocation); + + if(theDistance <= kHiveXYDistanceTolerance) + { + // Make sure this hive isn't already active + #ifdef AVH_SERVER + CBaseEntity* theEntity = AvHSUGetEntityFromIndex(*theIter); + ASSERT(theEntity); + if(theEntity->pev->team == 0) + { + #endif + + theRequirementsMet = true; + inLocation = theLocation; + + #ifdef AVH_SERVER + } + #endif + + break; + } + } + } + } + else if(AvHSHUGetBuildRegions(inMessageID, theEntities, theDistanceRequirements, theZAdjustment, theSnapToLocation)) + { + ASSERT(theEntities.size() == theDistanceRequirements.size()); + vec3_t theBestLocation; + + int i = 0; + float theClosestDistance = kMaxMapDimension; + for(EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++, i++) + { + vec3_t theLocation; + if(AvHSHUGetEntityLocation(*theIter, theLocation)) + { + // Only check xy distance + vec3_t theXYInLocation = inLocation; + vec3_t theXYTheLocation = theLocation; + theXYInLocation.z = 0; + theXYTheLocation.z = 0; + + float theDistance = VectorDistance((float*)&theXYInLocation, (float*)&theXYTheLocation); + + int theDistanceRequirement = theDistanceRequirements[i]; + if((theDistance <= theDistanceRequirement) || (theDistanceRequirement == -1)) + { + // Pick the closest one, in case there are multiples in range + if(theDistance < theClosestDistance) + { + theClosestDistance = theDistance; + VectorCopy(theLocation,theBestLocation); + theRequirementsMet = true; + } + } + } + } + if(theRequirementsMet && theSnapToLocation) + { + inLocation = theBestLocation; + inLocation.z += theZAdjustment; + } + } + else + { + theRequirementsMet = true; + } + + EntityListType theBuildRadiusViolations; + AvHSHUGetMinBuildRadiusViolations(inMessageID, inLocation, theBuildRadiusViolations); + + if (theBuildRadiusViolations.size() > 0) + { + theRequirementsMet = false; + } + + // Anti-llama/newbie tactic: don't allow non-resource buildings to be placed such that they block access to nozzles + // Make sure generic building isn't being placed on top of resource nozzles + // : 0000291 + // allow equipment, rts and hives to be dropped around nodes + if(AvHSHUGetIsDroppableOnRTs(inMessageID) == false) + { + // : + // If building is too close to an empty nozzle, don't allow it + float theResourceBuildingRadius, theTotalMinRadius; + vec3_t theMinSize, theMaxSize, theMinRadius, theFlattenedInLocation, theLocation; + + theFlattenedInLocation[0] = inLocation[0]; + theFlattenedInLocation[1] = inLocation[1]; + theFlattenedInLocation[2] = 0; + + theResourceBuildingRadius = 60; + + EntityListType theEntities; + AvHSHUGetEntities(AVH_USER3_FUNC_RESOURCE,theEntities); + if(AvHSHUGetSizeForTech(inMessageID,theMinSize,theMaxSize)) + { + EntityListType::iterator end = theEntities.end(); + for(EntityListType::iterator current = theEntities.begin(); current < end; ++current) + { + if(AvHSHUGetEntityLocation(*current,theLocation)) + { + //flatten to 2 dimensions + theLocation[2] = 0; + + //space = radius of both buildings combined + theTotalMinRadius = theResourceBuildingRadius; + theTotalMinRadius += max(-min(theMinSize.x,theMinSize.y),max(theMaxSize.x,theMaxSize.y)); + + if(VectorDistance((float*)&theFlattenedInLocation,(float*)&theLocation) < theTotalMinRadius) + { + theRequirementsMet = false; + break; + } + } + } + } + } + + return theRequirementsMet; +} + +bool AvHSHUGetBuildTechClassName(AvHMessageID inMessageID, char*& outClassName) +{ + bool theSuccess = true; + + switch(inMessageID) + { + // Buildings + case BUILD_RESOURCES: + outClassName = kwsResourceTower; + break; + + //case BUILD_REINFORCEMENTS: + // outClassName = kwsInfantryPortal; + // break; + + case BUILD_INFANTRYPORTAL: + outClassName = kwsInfantryPortal; + break; + + case BUILD_COMMANDSTATION: + outClassName = kwsTeamCommand; + break; + + case BUILD_TURRET_FACTORY: + outClassName = kwsTurretFactory; + break; + + case BUILD_ARMSLAB: + outClassName = kwsArmsLab; + break; + + case BUILD_PROTOTYPE_LAB: + outClassName = kwsPrototypeLab; + break; + + case BUILD_ARMORY: + outClassName = kwsArmory; + break; + + case ARMORY_UPGRADE: + outClassName = kwsAdvancedArmory; + break; + + case BUILD_NUKE_PLANT: + outClassName = kwsNukePlant; + break; + + case BUILD_OBSERVATORY: + outClassName = kwsObservatory; + break; + + case BUILD_SCAN: + outClassName = kwsScan; + break; + + case BUILD_PHASEGATE: + outClassName = kwsPhaseGate; + break; + + case BUILD_TURRET: + outClassName = kwsDeployedTurret; + break; + + case BUILD_SIEGE: + outClassName = kwsSiegeTurret; + break; + + // Equipment + case BUILD_HEALTH: + outClassName = kwsHealth; + break; + + case BUILD_CAT: + outClassName = kwsCatalyst; + break; + + case BUILD_JETPACK: + outClassName = kwsJetpack; + break; + + case BUILD_HEAVY: + outClassName = kwsHeavyArmor; + break; + + case BUILD_AMMO: + outClassName = kwsGenericAmmo; + break; + + case BUILD_WELDER: + outClassName = kwsWelder; + break; + + case BUILD_MINES: + outClassName = kwsMine; + break; + + case BUILD_SHOTGUN: + outClassName = kwsShotGun; + break; + + case BUILD_HMG: + outClassName = kwsHeavyMachineGun; + break; + + case BUILD_NUKE: + outClassName = kwsNuke; + break; + + case BUILD_GRENADE_GUN: + outClassName = kwsGrenadeGun; + break; + + //case BUILD_MEDKIT: + // break; + + case ALIEN_BUILD_RESOURCES: + outClassName = kwsAlienResourceTower; + break; + + case ALIEN_BUILD_OFFENSE_CHAMBER: + outClassName = kwsOffenseChamber; + break; + + case ALIEN_BUILD_DEFENSE_CHAMBER: + outClassName = kwsDefenseChamber; + break; + + case ALIEN_BUILD_SENSORY_CHAMBER: + outClassName = kwsSensoryChamber; + break; + + case ALIEN_BUILD_MOVEMENT_CHAMBER: + outClassName = kwsMovementChamber; + break; + + case ALIEN_BUILD_HIVE: + outClassName = kesTeamHive; + break; + + default: + theSuccess = false; + break; + } + + return theSuccess; +} + +bool AvHSHUGetResearchTechName(AvHMessageID inResearchID, char*& outResearchTechName) +{ + bool theSuccess = true; + + switch(inResearchID) + { + case RESEARCH_ELECTRICAL: + outResearchTechName = "research_electrical"; + break; + + case RESEARCH_ARMOR_ONE: + outResearchTechName = "research_armorl1"; + break; + + case RESEARCH_ARMOR_TWO: + outResearchTechName = "research_armorl2"; + break; + + case RESEARCH_ARMOR_THREE: + outResearchTechName = "research_armorl3"; + break; + + case RESEARCH_WEAPONS_ONE: + outResearchTechName = "research_weaponsl1"; + break; + + case RESEARCH_WEAPONS_TWO: + outResearchTechName = "research_weaponsl2"; + break; + + case RESEARCH_WEAPONS_THREE: + outResearchTechName = "research_weaponsl3"; + break; + + case ARMORY_UPGRADE: + outResearchTechName = "research_advarmory"; + break; + + case TURRET_FACTORY_UPGRADE: + outResearchTechName = "research_advturretfactory"; + break; + + case RESEARCH_JETPACKS: + outResearchTechName = "research_jetpacks"; + break; + + case RESEARCH_HEAVYARMOR: + outResearchTechName = "research_heavyarmor"; + break; + + case RESEARCH_DISTRESSBEACON: + outResearchTechName = "research_distressbeacon"; + break; + + case RESEARCH_HEALTH: + outResearchTechName = "research_health"; + break; + + case RESEARCH_CATALYSTS: + outResearchTechName = "research_catalysts"; + break; + + case MESSAGE_CANCEL: + outResearchTechName = "research_cancel"; + break; + + case RESEARCH_MOTIONTRACK: + outResearchTechName = "research_motiontracking"; + break; + + case RESEARCH_PHASETECH: + outResearchTechName = "research_phasetech"; + break; + + case RESEARCH_GRENADES: + outResearchTechName = "research_grenades"; + break; + + default: + theSuccess = false; + break; + } + + return theSuccess; +} + + +bool AvHSHUGetCenterPositionForGroup(int inGroupNumber, float* inPlayerOrigin, float* outCenterPosition) +{ + bool theSuccess = false; + vec3_t thePosition; + float theX, theY; + + #ifdef AVH_CLIENT + theSuccess = gHUD.GetCenterPositionForGroup(inGroupNumber, thePosition); + theX = thePosition[0]; + theY = thePosition[1]; + #endif + + #ifdef AVH_SERVER + + // Loop through players, find the closest player to inPlayerOrigin, to see which player is being predicted. Is there a better way? + AvHPlayer* theClosestPlayer = NULL; + float theClosestDistance = 10000; + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + float theDistance = VectorDistance(theEntity->pev->origin, inPlayerOrigin); + if(theDistance < theClosestDistance) + { + theClosestPlayer = theEntity; + theClosestDistance = theDistance; + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + if(theClosestPlayer) + { + theSuccess = theClosestPlayer->GetCenterPositionForGroup(inGroupNumber, theX, theY); + } + #endif + + if(theSuccess) + { + outCenterPosition[0] = theX; + outCenterPosition[1] = theY; + } + + return theSuccess; +} + + +bool AvHSHUGetIsBuilding(AvHMessageID inMessageID) +{ + bool theIsBuilding = false; + + switch(inMessageID) + { + // Buildings + case BUILD_RESOURCES: + case BUILD_INFANTRYPORTAL: + case BUILD_COMMANDSTATION: + case BUILD_TURRET_FACTORY: + case BUILD_ARMSLAB: + case BUILD_PROTOTYPE_LAB: + case BUILD_ARMORY: + case BUILD_NUKE_PLANT: + case BUILD_OBSERVATORY: + case BUILD_PHASEGATE: + case BUILD_TURRET: + case BUILD_SIEGE: + + // Alien buildings + case ALIEN_BUILD_OFFENSE_CHAMBER: + case ALIEN_BUILD_DEFENSE_CHAMBER: + case ALIEN_BUILD_SENSORY_CHAMBER: + case ALIEN_BUILD_MOVEMENT_CHAMBER: + case ALIEN_BUILD_HIVE: + theIsBuilding = true; + break; + } + + return theIsBuilding; +} + +bool AvHSHUGetIsBuildTech(AvHMessageID inMessageID) +{ + bool theIsBuildTech = false; + + switch(inMessageID) + { + // Buildings + case BUILD_RESOURCES: + case BUILD_INFANTRYPORTAL: + case BUILD_COMMANDSTATION: + case BUILD_TURRET_FACTORY: + case BUILD_ARMSLAB: + case BUILD_PROTOTYPE_LAB: + case BUILD_ARMORY: + //case UPGRADE_ADVANCED_WEAPON_FACTORY: + case BUILD_NUKE_PLANT: + case BUILD_OBSERVATORY: + case BUILD_SCAN: + case BUILD_PHASEGATE: + case BUILD_TURRET: + case BUILD_SIEGE: + case BUILD_HEAVY: + case BUILD_JETPACK: + + // Equipment + case BUILD_AMMO: + case BUILD_HEALTH: + case BUILD_CAT: + case BUILD_WELDER: + case BUILD_MINES: + case BUILD_SHOTGUN: + case BUILD_HMG: + case BUILD_NUKE: + case BUILD_GRENADE_GUN: + //case BUILD_MEDKIT: + //case BUILD_STIMPACK: + + // Alien buildings + case ALIEN_BUILD_OFFENSE_CHAMBER: + case ALIEN_BUILD_DEFENSE_CHAMBER: + case ALIEN_BUILD_SENSORY_CHAMBER: + case ALIEN_BUILD_MOVEMENT_CHAMBER: + theIsBuildTech = true; + break; + } + + return theIsBuildTech; +} + +bool AvHSHUGetIsWeaponFocusable(AvHWeaponID inWeaponID) +{ + bool theIsFocusable = false; + + switch(inWeaponID) + { + case AVH_WEAPON_BITE: + case AVH_WEAPON_SPIT: + case AVH_WEAPON_BITE2: + case AVH_WEAPON_SWIPE: + case AVH_WEAPON_CLAWS: + theIsFocusable = true; + break; + } + + return theIsFocusable; +} + +bool AvHSHUGetDoesTechCostEnergy(AvHMessageID inMessageID) +{ + bool theTechCostsEnergy = false; + + switch(inMessageID) + { + case BUILD_SCAN: + theTechCostsEnergy = true; + break; + } + + return theTechCostsEnergy; +} + +bool AvHSHUGetIsCombatModeTech(AvHMessageID inMessageID) +{ + bool theIsCombatModeTech = false; + + switch(inMessageID) + { + case BUILD_SHOTGUN: + case BUILD_GRENADE_GUN: + case BUILD_HMG: + case BUILD_WELDER: + case BUILD_MINES: + case BUILD_JETPACK: + case BUILD_HEAVY: + case BUILD_SCAN: + theIsCombatModeTech = true; + break; + } + + return theIsCombatModeTech; +} + +bool AvHSHUGetIsResearchTech(AvHMessageID inMessageID) +{ + bool theIsResearchTech = false; + + switch(inMessageID) + { + case RESEARCH_ELECTRICAL: + case RESEARCH_ARMOR_ONE: + case RESEARCH_ARMOR_TWO: + case RESEARCH_ARMOR_THREE: + case RESEARCH_WEAPONS_ONE: + case RESEARCH_WEAPONS_TWO: + case RESEARCH_WEAPONS_THREE: + case TURRET_FACTORY_UPGRADE: + case RESEARCH_JETPACKS: + case RESEARCH_HEAVYARMOR: + case RESEARCH_DISTRESSBEACON: + case RESEARCH_HEALTH: + case RESEARCH_CATALYSTS: + case MESSAGE_CANCEL: + case RESEARCH_MOTIONTRACK: + case RESEARCH_PHASETECH: + case RESEARCH_GRENADES: + + case RESOURCE_UPGRADE: + case ARMORY_UPGRADE: + theIsResearchTech = true; + break; + } + + return theIsResearchTech; +} + + + + +//Here's TFC's code that checks whether a player's allowed to build a sentry +//or not. +//I can't remember if there was any good reason why we used +//UTIL_FindEntitiesInSphere() +//instead of UTIL_EntitiesInBox(). +// +////========================================================================= +//// Returns 0 if the area around obj is safe to build in +//int CBaseEntity::CheckArea( CBaseEntity *pIgnore ) +//{ +// TraceResult tr; +// Vector vecOrg = pev->origin; +// +// // Check the origin +// int iContents = UTIL_PointContents(vecOrg); +// if ( iContents != CONTENT_EMPTY && iContents != CONTENT_WATER ) +// return CAREA_BLOCKED; +// +// Vector vecIgnoreOrg = pIgnore->pev->origin; +// // Get the player's origin irrelevant of crouching +// if ( pIgnore->pev->flags & FL_DUCKING ) +// { +// vecIgnoreOrg = vecIgnoreOrg + (VEC_DUCK_HULL_MIN - +// VEC_HULL_MIN); +// } +// // Trace a hull +// UTIL_TraceHull( vecIgnoreOrg, pev->origin, ignore_monsters, +// large_hull, edict(), &tr ); +// CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); +// if (tr.flFraction != 1 || tr.fAllSolid == 1) +// return CAREA_BLOCKED; +// +// // Check for solid entities in the area +// CBaseEntity *pEnt = NULL; +// while ( (pEnt = UTIL_FindEntityInSphere( pEnt, pev->origin, 48 )) != +// NULL ) +// { +// // If it's not the engineer, and its a solid entity, fail +// if (pEnt != pIgnore && pEnt != this && pEnt->pev->solid > +// SOLID_TRIGGER) +// return CAREA_BLOCKED; +// } +// +// // Cycle through all the Nobuild zones in the map and make sure this +// isn't in one of them +// CBaseEntity *pNoBuild = UTIL_FindEntityByClassname( NULL, +// "func_nobuild" ); +// while ( pNoBuild ) +// { +// // Check to see if we're building in this zone +// if ( vecOrg.x >= pNoBuild->pev->mins.x && vecOrg.y >= +// pNoBuild->pev->mins.y && vecOrg.z >= pNoBuild->pev->mins.z && +// vecOrg.x <= pNoBuild->pev->maxs.x && +// vecOrg.y <= pNoBuild->pev->maxs.y && vecOrg.z <= pNoBuild->pev->maxs.z ) +// return CAREA_NOBUILD; +// +// pNoBuild = UTIL_FindEntityByClassname( pNoBuild, +// "func_nobuild" ); +// } +// +// // Check below +// UTIL_TraceLine( vecOrg, vecOrg + Vector(0,0,-64), +// dont_ignore_monsters, edict(), &tr ); +// if ( tr.flFraction == 1.0 ) +// return CAREA_BLOCKED; +// +// return CAREA_CLEAR; +//} + +//bool AvHSHUGetIsEnoughRoom(const vec3_t& inCenter, const vec3_t& inMin, const vec3_t& inMax) +//{ +// bool theEnoughRoom = false; +// +// // Do a traceline from center to center + min +// // Hit nothing? +// // Do a traceline from center to center + max +// // Hit nothing? +// // Success +// theEnoughRoom = true; +// +// return theEnoughRoom; +//} + +bool AvHSHUGetIsGroupMessage(AvHMessageID inMessageID) +{ + bool theIsGroupMessage = false; + + switch(inMessageID) + { + case GROUP_CREATE_1: + case GROUP_CREATE_2: + case GROUP_CREATE_3: + case GROUP_CREATE_4: + case GROUP_CREATE_5: + case GROUP_SELECT_1: + case GROUP_SELECT_2: + case GROUP_SELECT_3: + case GROUP_SELECT_4: + case GROUP_SELECT_5: + theIsGroupMessage = true; + } + + return theIsGroupMessage; +} + +bool AvHSHUGetSizeForTech(AvHMessageID inMessageID, Vector& outMinSize, Vector& outMaxSize, bool inGetSizeToPlace) +{ + bool theSuccess = false; + + // Onos-sized + //const int theOnosHeightNeededToSpawn = HULL3_MAXZ - HULL3_MINZ + kRespawnFudgeFactorHeight; + //const int theOnosWidthNeededToSpawn = HULL3_MAXY - HULL3_MINY + kRespawnFudgeFactorHeight; + + // Marine-sized + const int theMarineHeightNeededToSpawn = HULL0_MAXZ - HULL0_MINZ + kRespawnFudgeFactorHeight; + + switch(inMessageID) + { + case BUILD_INFANTRYPORTAL: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 14.49); + + // Make sure there is room above it for players to spawn + if(inGetSizeToPlace) + { + outMaxSize.z += theMarineHeightNeededToSpawn; + } + + theSuccess = true; + break; + + case BUILD_PHASEGATE: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 14.49); + + // Make sure there is remove above it for players to spawn + if(inGetSizeToPlace) + { + //outMinSize.x = outMinSize.y = -theOnosWidthNeededToSpawn; + //outMaxSize.x = outMaxSize.y = theOnosWidthNeededToSpawn; + //outMaxSize.z += theOnosHeightNeededToSpawn; + outMaxSize.z += theMarineHeightNeededToSpawn; + } + + theSuccess = true; + break; + + case BUILD_RESOURCES: + outMinSize = kResourceMinSize; + outMaxSize = kResourceMaxSize; + theSuccess = true; + break; + + case ALIEN_BUILD_RESOURCES: + outMinSize = kAlienResourceMinSize; + outMaxSize = kAlienResourceMaxSize; + theSuccess = true; + break; + + default: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 66.9486); + theSuccess = true; + break; + + case BUILD_ARMSLAB: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 72.0 /*66.9486*/); + theSuccess = true; + break; + + case BUILD_TURRET: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 42.0); + theSuccess = true; + break; + + case ALIEN_BUILD_OFFENSE_CHAMBER: + case ALIEN_BUILD_DEFENSE_CHAMBER: + case ALIEN_BUILD_SENSORY_CHAMBER: + case ALIEN_BUILD_MOVEMENT_CHAMBER: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 44.0); + theSuccess = true; + break; + + case BUILD_COMMANDSTATION: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 73.0 /*70.34*/); + theSuccess = true; + break; + + case BUILD_TURRET_FACTORY: + outMinSize = Vector(-16, -16, 0); + //outMaxSize = Vector(16.0, 16.0, 55.68); + outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/); + theSuccess = true; + break; + + case BUILD_ARMORY: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/); + theSuccess = true; + break; + + case BUILD_PROTOTYPE_LAB: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 73.0 /*67.7443*/); + theSuccess = true; + break; + + case BUILD_OBSERVATORY: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 80.7443); + theSuccess = true; + break; + + case BUILD_SIEGE: + outMinSize = Vector(-16, -16, 0); + outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/ /*50.6678*/); + theSuccess = true; + break; + + case BUILD_HEALTH: + outMinSize = kHealthMinSize; + outMaxSize = kHealthMaxSize; + theSuccess = true; + break; + + case BUILD_CAT: + outMinSize = kCatalystMinSize; + outMaxSize = kCatalystMaxSize; + theSuccess = true; + break; + + case BUILD_HEAVY: + outMinSize = kHeavyMinSize; + outMaxSize = kHeavyMaxSize; + theSuccess = true; + break; + + case BUILD_JETPACK: + outMinSize = kJetpackMinSize; + outMaxSize = kJetpackMaxSize; + theSuccess = true; + break; + + case BUILD_AMMO: + outMinSize = kAmmoMinSize; + outMaxSize = kAmmoMaxSize; + theSuccess = true; + break; + + case BUILD_MINES: + outMinSize = kWeaponMinSize; + outMaxSize = Vector(16, 16, 40); + theSuccess = true; + break; + + case BUILD_SHOTGUN: + case BUILD_HMG: + case BUILD_WELDER: + case BUILD_NUKE: + case BUILD_GRENADE_GUN: + outMinSize = kWeaponMinSize; + outMaxSize = kWeaponMaxSize; + theSuccess = true; + break; + + case ALIEN_BUILD_HIVE: + outMinSize = kHiveMinSize; + outMaxSize = kHiveMaxSize; + theSuccess = true; + break; + } + + return theSuccess; +} + +bool AvHSHUGetSizeForPlayerUser3(AvHUser3 inUser3, Vector& outMinSize, Vector& outMaxSize, bool inDucking) +{ + bool theSuccess = false; + + // Now set size + switch(inUser3) + { + case AVH_USER3_NONE: + case AVH_USER3_MARINE_PLAYER: + case AVH_USER3_COMMANDER_PLAYER: + case AVH_USER3_ALIEN_PLAYER4: + // Get simple case working first + if(inDucking) + { + outMinSize = HULL1_MIN; + outMaxSize = HULL1_MAX; + } + else + { + outMinSize = HULL0_MIN; + outMaxSize = HULL0_MAX; + } + theSuccess = true; + break; + + case AVH_USER3_ALIEN_PLAYER1: + case AVH_USER3_ALIEN_PLAYER2: + case AVH_USER3_ALIEN_PLAYER3: + case AVH_USER3_ALIEN_EMBRYO: + outMinSize = HULL1_MIN; + outMaxSize = HULL1_MAX; + theSuccess = true; + break; + + case AVH_USER3_ALIEN_PLAYER5: + if(inDucking) + { + outMinSize = HULL0_MIN; + outMaxSize = HULL0_MAX; + } + else + { + outMinSize = HULL3_MIN; + outMaxSize = HULL3_MAX; + } + theSuccess = true; + break; + } + + return theSuccess; +} + +bool AvHSHUGetSizeForUser3(AvHUser3 inUser3, Vector& outMinSize, Vector& outMaxSize) +{ + bool theSuccess = false; + + // If it's a player, get max size he can be (assuming he's not ducking) + theSuccess = AvHSHUGetSizeForPlayerUser3(inUser3, outMinSize, outMaxSize, false); + if(!theSuccess) + { + AvHMessageID theMessageID = MESSAGE_NULL; + + // Convert it to a AvHMessageID if possible + if(AvHSHUUser3ToMessageID(inUser3, theMessageID)) + { + theSuccess = AvHSHUGetSizeForTech(theMessageID, outMinSize, outMaxSize); + } + } + + return theSuccess; +} + +bool AvHSHUUser3ToMessageID(AvHUser3 inUser3, AvHMessageID& outMessageID) +{ + AvHMessageID theMessageID = MESSAGE_NULL; + bool theSuccess = false; + + switch(inUser3) + { + case AVH_USER3_FUNC_RESOURCE: + case AVH_USER3_RESTOWER: + theMessageID = BUILD_RESOURCES; + break; + + case AVH_USER3_COMMANDER_STATION: + theMessageID = BUILD_COMMANDSTATION; + break; + + case AVH_USER3_TURRET_FACTORY: + case AVH_USER3_ADVANCED_TURRET_FACTORY: + theMessageID = BUILD_TURRET_FACTORY; + break; + + case AVH_USER3_ARMORY: + case AVH_USER3_ADVANCED_ARMORY: + theMessageID = BUILD_ARMORY; + break; + + case AVH_USER3_ARMSLAB: + theMessageID = BUILD_ARMSLAB; + break; + + case AVH_USER3_PROTOTYPE_LAB: + theMessageID = BUILD_PROTOTYPE_LAB; + break; + + case AVH_USER3_OBSERVATORY: + theMessageID = BUILD_OBSERVATORY; + break; + + case AVH_USER3_TURRET: + theMessageID = BUILD_TURRET; + break; + + case AVH_USER3_SIEGETURRET: + theMessageID = BUILD_SIEGE; + break; + + case AVH_USER3_INFANTRYPORTAL: + theMessageID = BUILD_INFANTRYPORTAL; + break; + + case AVH_USER3_PHASEGATE: + theMessageID = BUILD_PHASEGATE; + break; + + case AVH_USER3_HEAVY: + theMessageID = BUILD_HEAVY; + break; + + case AVH_USER3_JETPACK: + theMessageID = BUILD_JETPACK; + break; + + case AVH_USER3_DEFENSE_CHAMBER: + theMessageID = ALIEN_BUILD_DEFENSE_CHAMBER; + break; + + case AVH_USER3_MOVEMENT_CHAMBER: + theMessageID = ALIEN_BUILD_MOVEMENT_CHAMBER; + break; + + case AVH_USER3_OFFENSE_CHAMBER: + theMessageID = ALIEN_BUILD_OFFENSE_CHAMBER; + break; + + case AVH_USER3_SENSORY_CHAMBER: + theMessageID = ALIEN_BUILD_SENSORY_CHAMBER; + break; + + case AVH_USER3_ALIENRESTOWER: + theMessageID = ALIEN_BUILD_RESOURCES; + break; + + case AVH_USER3_HIVE: + theMessageID = ALIEN_BUILD_HIVE; + break; + + case AVH_USER3_ALIEN_PLAYER1: + theMessageID = ALIEN_LIFEFORM_ONE; + break; + + case AVH_USER3_ALIEN_PLAYER2: + theMessageID = ALIEN_LIFEFORM_TWO; + break; + + case AVH_USER3_ALIEN_PLAYER3: + theMessageID = ALIEN_LIFEFORM_THREE; + break; + + case AVH_USER3_ALIEN_PLAYER4: + theMessageID = ALIEN_LIFEFORM_FOUR; + break; + + case AVH_USER3_ALIEN_PLAYER5: + theMessageID = ALIEN_LIFEFORM_FIVE; + break; + } + + if(theMessageID != MESSAGE_NULL) + { + outMessageID = theMessageID; + theSuccess = true; + } + + return theSuccess; +} + +bool AvHSHUMessageIDToUser3(AvHMessageID inMessageID, AvHUser3& outUser3) +{ + bool theSuccess = false; + + AvHUser3 theUser3 = AVH_USER3_NONE; + + switch(inMessageID) + { + case BUILD_RESOURCES: + theUser3 = AVH_USER3_RESTOWER; + break; + + case BUILD_COMMANDSTATION: + theUser3 = AVH_USER3_COMMANDER_STATION; + break; + + case BUILD_TURRET_FACTORY: + theUser3 = AVH_USER3_TURRET_FACTORY; + break; + + case TURRET_FACTORY_UPGRADE: + theUser3 = AVH_USER3_ADVANCED_TURRET_FACTORY; + break; + + case BUILD_ARMORY: + theUser3 = AVH_USER3_ARMORY; + break; + + case ARMORY_UPGRADE: + theUser3 = AVH_USER3_ADVANCED_ARMORY; + break; + + case BUILD_ARMSLAB: + theUser3 = AVH_USER3_ARMSLAB; + break; + + case BUILD_PROTOTYPE_LAB: + theUser3 = AVH_USER3_PROTOTYPE_LAB; + break; + + case BUILD_OBSERVATORY: + theUser3 = AVH_USER3_OBSERVATORY; + break; + + case BUILD_TURRET: + theUser3 = AVH_USER3_TURRET; + break; + + case BUILD_SIEGE: + theUser3 = AVH_USER3_SIEGETURRET; + break; + + case BUILD_INFANTRYPORTAL: + theUser3 = AVH_USER3_INFANTRYPORTAL; + break; + + case BUILD_PHASEGATE: + theUser3 = AVH_USER3_PHASEGATE; + break; + + case BUILD_HEAVY: + theUser3 = AVH_USER3_HEAVY; + break; + + case BUILD_JETPACK: + theUser3 = AVH_USER3_JETPACK; + break; + + // Menus + case MENU_BUILD: + theUser3 = AVH_USER3_MENU_BUILD; + break; + case MENU_BUILD_ADVANCED: + theUser3 = AVH_USER3_MENU_BUILD_ADVANCED; + break; + case MENU_ASSIST: + theUser3 = AVH_USER3_MENU_ASSIST; + break; + case MENU_EQUIP: + theUser3 = AVH_USER3_MENU_EQUIP; + break; + + // Weapons + case BUILD_MINES: + theUser3 = AVH_USER3_MINE; + break; + + // Lifeforms + case ALIEN_LIFEFORM_ONE: + theUser3 = AVH_USER3_ALIEN_PLAYER1; + break; + + case ALIEN_LIFEFORM_TWO: + theUser3 = AVH_USER3_ALIEN_PLAYER2; + break; + + case ALIEN_LIFEFORM_THREE: + theUser3 = AVH_USER3_ALIEN_PLAYER3; + break; + + case ALIEN_LIFEFORM_FOUR: + theUser3 = AVH_USER3_ALIEN_PLAYER4; + break; + + case ALIEN_LIFEFORM_FIVE: + theUser3 = AVH_USER3_ALIEN_PLAYER5; + break; + } + + if(theUser3 != AVH_USER3_NONE) + { + outUser3 = theUser3; + theSuccess = true; + } + + return theSuccess; +} + + +float AvHSHUGetTime() +{ + float theTime = 0; + + #ifdef AVH_SERVER + theTime = gpGlobals->time; + #else + theTime = gEngfuncs.GetClientTime(); + #endif + + return theTime; +} + + + +// Note, all these models must be precached already +char* AvHSHUGetBuildTechModelName(AvHMessageID inMessageID) +{ + char* theModelName = NULL; + + switch(inMessageID) + { + case BUILD_RESOURCES: + theModelName = kResourceTowerModel; + break; + + //case BUILD_REINFORCEMENTS: + // theModelName = kInfantryPortalModel; + // break; + + case BUILD_INFANTRYPORTAL: + theModelName = kInfantryPortalModel; + break; + + case BUILD_COMMANDSTATION: + theModelName = kCommandStationModel; + break; + + case BUILD_TURRET_FACTORY: + theModelName = kTurretFactoryModel; + break; + + case BUILD_ARMSLAB: + theModelName = kArmsLabModel; + break; + + case BUILD_PROTOTYPE_LAB: + theModelName = kPrototypeLabModel; + break; + + case BUILD_ARMORY: + theModelName = kArmoryModel; + break; + + case ARMORY_UPGRADE: + theModelName = kAdvancedWeaponFactoryModel; + break; + + case BUILD_OBSERVATORY: + theModelName = kObservatoryModel; + break; + + case BUILD_SCAN: + theModelName = kScanModel; + break; + + case BUILD_PHASEGATE: + theModelName = kPhaseGateModel; + break; + + case BUILD_TURRET: + theModelName = kDeployedTurretModel; + break; + + case BUILD_NUKE: + theModelName = kNukeModel; + break; + + case BUILD_SIEGE: + theModelName = kSiegeTurretModel; + //theModelName = kDeployedTurretModel; + break; + + case BUILD_CAT: + theModelName = kCatalystModel; + break; + + case BUILD_HEALTH: + theModelName = kHealthModel; + break; + + case BUILD_HEAVY: + theModelName = kHeavyModel; + break; + + case BUILD_JETPACK: + theModelName = kJetpackModel; + break; + + case BUILD_AMMO: + theModelName = kAmmoModel; + break; + + case BUILD_WELDER: + theModelName = kWelderWModel; + break; + + case BUILD_MINES: + theModelName = kTripmineW2Model; + break; + + case BUILD_SHOTGUN: + theModelName = kSGWModel; + break; + + case BUILD_HMG: + theModelName = kHMGWModel; + break; + + case BUILD_GRENADE_GUN: + theModelName = kGGWModel; + break; + + // Alien buildings + case ALIEN_BUILD_HIVE: + theModelName = kHiveModel; + break; + + case ALIEN_BUILD_RESOURCES: + theModelName = kAlienResourceTowerModel; + break; + + case ALIEN_BUILD_OFFENSE_CHAMBER: + theModelName = kOffenseChamberModel; + break; + + case ALIEN_BUILD_DEFENSE_CHAMBER: + theModelName = kDefenseChamberModel; + break; + + case ALIEN_BUILD_SENSORY_CHAMBER: + theModelName = kSensoryChamberModel; + break; + + case ALIEN_BUILD_MOVEMENT_CHAMBER: + theModelName = kMovementChamberModel; + break; + } + + return theModelName; +} + +bool AvHSHUGetBuildTechRange(AvHMessageID inMessageID, float& outRange) +{ + bool theSuccess = false; + + // switch(inMessageID) + // { + // case BUILD_CAMERA: + // outRange = kResourceTowerSightRange; + // theSuccess = true; + // break; + // case BUILD_PHASEGATE: + // break; + // case BUILD_TURRET: + // outRange = TURRET_RANGE; + // theSuccess = true; + // break; + // case BUILD_SIEGE: + // outRange = kSiegeTurretMaxRange; + // theSuccess = true; + // break; + // } + + return theSuccess; +} + +bool AvHSHUTraceLineIsAreaFree(Vector& inStart, Vector& inEnd, edict_t* inIgnoreEntity, bool inIgnorePlayers) +{ + bool theAreaIsFree = true; + +#ifdef AVH_SERVER + TraceResult theTR; + bool theIsDone = false; + + // Do tracelines between the corners, to make sure there's no geometry inside the box + int theNumIters = 0; + + IGNORE_MONSTERS theIgnoreMonsters = dont_ignore_monsters; + + if (inIgnorePlayers) + { + theIgnoreMonsters = ignore_monsters; + } + + while(!theIsDone) + { + UTIL_TraceLine(inStart, inEnd, theIgnoreMonsters, inIgnoreEntity, &theTR); + if(theTR.flFraction != 1.0f) + { + CBaseEntity* theEntity = CBaseEntity::Instance(ENT(theTR.pHit)); + if(theEntity && (theEntity->pev->solid != SOLID_BBOX) && (theEntity->pev->solid != SOLID_SLIDEBOX) && (theEntity->pev->solid != SOLID_BSP) && (inIgnorePlayers != !!theEntity->IsPlayer()) ) + { + VectorCopy(theTR.vecEndPos, inStart); + } + else + { + theIsDone = true; + theAreaIsFree = false; + } + } + else + { + theIsDone = true; + } + + if(theNumIters++ > 50) + { + theIsDone = true; + theAreaIsFree = false; + } + } +#endif + +// int theIndex; +// vec3_t theEndLocation; +// AvHTeamNumber theTeam; +// bool thePlayerHit; +// int theUserThree; +// int theUserFour; +// +// if(AvHSHUTraceTangible(inStart, inEnd, theIndex, theEndLocation, theTeam, thePlayerHit, theUserThree, theUserFour)) +// { +// if(theIndex >= 0) +// { +// theAreaIsFree = false; +// } +// } + + return theAreaIsFree; +} + +float AvHTraceLineAgainstWorld(Vector& vecStart, Vector& vecEnd) +{ + +#ifdef AVH_SERVER + + TraceResult tr; + UTIL_TraceLine(vecStart, vecEnd, ignore_monsters, dont_ignore_glass, NULL, &tr); + + return tr.flFraction; + +#endif + +#ifdef AVH_CLIENT + + // This may not be the most efficient way, but it seems to get the job done. + + float theFraction = 1; + + for (int i = 0; i < kMaxEntities && theFraction > 0; ++i) + { + + cl_entity_t* theEntity = gEngfuncs.GetEntityByIndex(i); + + if (theEntity != NULL && theEntity->model != NULL && theEntity->model->type == mod_brush) + { + + vec3_t localStart; + vec3_t localEnd; + + // Translate the start and end into the model's frame of reference. + + VectorSubtract(vecStart, theEntity->origin, localStart); + VectorSubtract(vecEnd, theEntity->origin, localEnd); + + // Rotate the start and end into the model's frame of reference. + + if (theEntity->angles[0] || + theEntity->angles[1] || + theEntity->angles[2]) + { + + vec3_t forward; + vec3_t right; + vec3_t up; + + AngleVectors(theEntity->angles, forward, right, up); + + vec3_t temp; + + VectorCopy(localStart, temp); + localStart[0] = DotProduct(temp, forward); + localStart[1] = -DotProduct(temp, right); + localStart[2] = DotProduct(temp, up); + + VectorCopy(localEnd, temp); + localEnd[0] = DotProduct(temp, forward); + localEnd[1] = -DotProduct(temp, right); + localEnd[2] = DotProduct(temp, up); + + } + + trace_t tr; + NS_TraceLine(&theEntity->model->hulls[0], localStart, localEnd, &tr); + + if (tr.fraction < theFraction) + { + theFraction = tr.fraction; + } + + } + + } + + return theFraction; + +#endif + +} + + +bool AvHSHUGetCanBeBuiltOnPlayers(AvHMessageID inMessageID) +{ + bool theCanBeBuiltOnPlayers = false; + + switch(inMessageID) + { + case ALIEN_BUILD_HIVE: + case BUILD_SCAN: + case BUILD_AMMO: + case BUILD_HEALTH: + case BUILD_CAT: + case BUILD_MINES: + case BUILD_WELDER: + case BUILD_SHOTGUN: + case BUILD_HMG: + case BUILD_GRENADE_GUN: + case BUILD_HEAVY: + case BUILD_JETPACK: + theCanBeBuiltOnPlayers = true; + break; + } + + return theCanBeBuiltOnPlayers; +} + +bool AvHSHUGetIsSiteValidForBuild(AvHMessageID inMessageID, Vector* inLocation, int inIgnoredEntityIndex) +{ + //TODO: - check entity returned by client side commander trace for drop validity before we ever get this far + + bool theSuccess = false; + + // Check that there's enough room for building + vec3_t theMinSize, theMaxSize; + if(AvHSHUGetSizeForTech(inMessageID, theMinSize, theMaxSize, true)) + { + + // If it's a build with special placement requirements, check that here (this could modify the location also) + if(AvHSHUGetAreSpecialBuildingRequirementsMet(inMessageID, *inLocation)) + { + + // Assume mappers place hives correctly (it's such a showstopper if aliens can't create a hive because it's too close to a wall) + // KGP 4/28/04 - add res towers to hives for the same reason -- if a mapper didn't give room, it's a showstopper. + bool theIgnorePlayers = AvHSHUGetCanBeBuiltOnPlayers(inMessageID); + float theSlopeTangent = theIgnorePlayers ? kMaxEquipmentDropSlope : kMaxBuildingDropSlope; + bool theSkipDropCheck = false; + switch(inMessageID) + { + case BUILD_RESOURCES: + theSkipDropCheck = true; + break; + case ALIEN_BUILD_RESOURCES: + theSkipDropCheck = true; + break; + case ALIEN_BUILD_HIVE: + theSkipDropCheck = true; + break; + } + if(theSkipDropCheck || AvHSHUGetCanDropItem(*inLocation, theMinSize, theMaxSize, theSlopeTangent, inIgnoredEntityIndex, theIgnorePlayers)) + { + //TODO: - better check for entity below drop, since it's often off center. + // Check to make sure building isn't being created on top of another building + vec3_t thePositionBelowBuild = *inLocation; + thePositionBelowBuild.z = -kMaxMapDimension; + + int theIndex; + vec3_t theEndLocation; + AvHTeamNumber theTeam; + bool thePlayerHit; + int theUserThree; + int theUserFour; + + // Don't allow building on any entities, except allow resource towers on top of func_resources + + //TODO : use full list instead of physents, which isn't accounting for items the commander can't see + bool theHitSomething = AvHSHUTraceTangible(*inLocation, thePositionBelowBuild, theIndex, theEndLocation, theTeam, thePlayerHit, theUserThree, theUserFour); + + //res building case + if(inMessageID == BUILD_RESOURCES || inMessageID == ALIEN_BUILD_RESOURCES) + { +#ifdef AVH_SERVER //on server, return false if occupied + FOR_ALL_ENTITIES(kesFuncResource,AvHFuncResource*) + if(!theEntity->GetIsOccupied()) // open for use + { + if(VectorDistance(theEntity->pev->origin,*inLocation) < 20) //small enough that we don't check the wrong nozzle + { + theSuccess = (!theHitSomething || theIndex <= 0 || theUserThree == AVH_USER3_FUNC_RESOURCE); + break; + } + } + END_FOR_ALL_ENTITIES(kesFuncResource) +#else //on client the occupied function isn't available + theSuccess = (!theHitSomething || theIndex <= 0 || theUserThree == AVH_USER3_FUNC_RESOURCE); +#endif + } + else if ( inMessageID == ALIEN_BUILD_HIVE ) + { + theSuccess = true; + //theSuccess = ( !theHitSomething || ( *inLocation[2] - theEndLocation[2] > 40.0f ) ); +//#ifdef AVH_SERVER +// ALERT(at_console, "theHitSomething=%d theSuccess=%d\n", theHitSomething, theSuccess); +// ALERT(at_console, "%f\n", *inLocation[2] - theEndLocation[2]); +// ALERT(at_console, "{%f, %f, %f} : { %f, %f, %f }\n", +// inLocation[0], inLocation[1], inLocation[2], +// theEndLocation[0], theEndLocation[1], theEndLocation[2]); +//#endif + } + else if(!theHitSomething || (theIgnorePlayers && thePlayerHit) || theIndex <= 0) + { + // THEN it's a legal build spot and the building shows up green. + theSuccess = true; + } + } + } + } + return theSuccess; +} + +int AvHSHUGetPointContents(vec3_t inPoint) +{ + int thePointContents = 0; + + #ifdef AVH_SERVER + thePointContents = UTIL_PointContents(inPoint); + #endif + + #ifdef AVH_CLIENT + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + gEngfuncs.pEventAPI->EV_PushPMStates(); + gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1); + gEngfuncs.pEventAPI->EV_SetTraceHull(2); + + thePointContents = gEngfuncs.PM_PointContents(inPoint, NULL); + + gEngfuncs.pEventAPI->EV_PopPMStates(); + #endif + + return thePointContents; +} + + /* + * Check to see if the build or item is overlapping the world. + */ +bool AvHSHUGetIsVolumeContentNonsolid(vec3_t inCenter, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers) +{ + + float theMins[3], theMaxs[3]; + VectorAdd(inCenter,inMinSize,theMins); + VectorAdd(inCenter,inMaxSize,theMaxs); + + bool theIsNonsolid = true; //innocent until proven guilty + + extern playermove_t *pmove; + if (pmove != NULL && pmove->physents[0].model != NULL) + { + const hull_t* theHull = &pmove->physents[0].model->hulls[NS_GetValveHull(2)]; + int theContents = NS_BoxContents(theHull,theHull->firstclipnode,theMins,theMaxs); + theIsNonsolid = (theContents != CONTENT_SOLID); + } + else //fallback if we're just starting a round + { + int theContents = AvHSHUGetPointContents(inCenter); + if(theContents != CONTENT_SOLID) + { + //trace between each corner pair once looking for obstructions - 28 total comparisons + const static int MIN = 0; + const static int MAX = 1; + const static int NUM_CORNERS = 8; + int theIndex; + Vector theCorners[NUM_CORNERS]; + + for(int XPos = MIN; XPos <= MAX; ++XPos) + { + for(int YPos = MIN; YPos <= MAX; ++YPos) + { + for(int ZPos = MIN; ZPos <= MAX; ++ZPos) + { + theIndex = XPos+YPos*2+ZPos*4; + theCorners[theIndex].x = inCenter.x + ((XPos == MIN) ? inMinSize.x : inMaxSize.x); + theCorners[theIndex].y = inCenter.y + ((YPos == MIN) ? inMinSize.y : inMaxSize.y); + theCorners[theIndex].z = inCenter.z + ((ZPos == MIN) ? inMinSize.z : inMaxSize.z); + } + } + } + + for(int startCorner = 0; startCorner < NUM_CORNERS; ++startCorner) + { + for(int endCorner = startCorner+1; endCorner < NUM_CORNERS; ++endCorner) + { + if(!AvHSHUTraceLineIsAreaFree(theCorners[startCorner],theCorners[endCorner],inIgnoreEntity,inIgnorePlayers)) + { + theIsNonsolid = false; + break; + } + } + if(!theIsNonsolid) + { + break; + } + } + } + else + { + theIsNonsolid = false; + } + } + return theIsNonsolid; +} + +bool AvHSHUGetIsAreaFree(vec3_t inCenter, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers) +{ + bool theAreaIsFree = AvHSHUGetIsVolumeContentNonsolid(inCenter,inMinSize,inMaxSize,inIgnoreEntity,inIgnorePlayers); + + if(theAreaIsFree) + { + theAreaIsFree = !AvHSHUGetEntitiesBlocking(inCenter, inMinSize, inMaxSize, inIgnoreEntity, inIgnorePlayers); + } + + return theAreaIsFree; +} + +bool AvHSHUGetEntitiesBlocking(Vector& inOrigin, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers) +{ + bool theEntBlocking = false; + + typedef vector< pair > PhysEntListType; + PhysEntListType theEntList; + + const int kSearchRadius = 800; + + // Populate phys ent list + #ifdef AVH_SERVER + CBaseEntity* theEntity = NULL; + while ((theEntity = UTIL_FindEntityInSphere(theEntity, inOrigin, kSearchRadius)) != NULL) + { + + // If entity is visible non-world object, add it + if((theEntity->pev->team != TEAM_IND) && (theEntity->pev->solid != SOLID_NOT && theEntity->pev->solid != SOLID_TRIGGER)) + { + + AvHPlayer* thePlayer = dynamic_cast(theEntity); + + if(theEntity->edict() != inIgnoreEntity && !(thePlayer != NULL && inIgnorePlayers)) + { + theEntList.push_back(make_pair(theEntity->pev->iuser3, theEntity->pev->origin)); + } + } + } + #endif + + #ifdef AVH_CLIENT + // Haven't implemented this, so depend on it not being passed in + ASSERT(inIgnoreEntity == NULL); + + for(int i = 0; i < pmove->numphysent; i++) + { + physent_t* thePhysEnt = pmove->physents + i; + if((thePhysEnt->team != TEAM_IND) && (thePhysEnt->solid != SOLID_NOT && thePhysEnt->solid != SOLID_TRIGGER)) + { + theEntList.push_back(make_pair(thePhysEnt->iuser3, thePhysEnt->origin)); + } + } + #endif + + Vector theAbsMax; + VectorAdd(inOrigin, inMaxSize, theAbsMax); + + Vector theAbsMin; + VectorAdd(inOrigin, inMinSize, theAbsMin); + + Vector theOrigin1; + VectorAdd(theAbsMax, theAbsMin, theOrigin1); + theOrigin1 = theOrigin1*.5f; + + Vector theSize1; + VectorSubtract(theAbsMax, theAbsMin, theSize1); + theSize1 = theSize1*.5f; + + // Do collision on list, making sure none are too close to inOrigin + for(PhysEntListType::iterator theIter = theEntList.begin(); theIter != theEntList.end(); theIter++) + { + // Get size for tech + Vector theMinSize, theMaxSize; + if(AvHSHUGetSizeForUser3(AvHUser3(theIter->first), theMinSize, theMaxSize)) + { + Vector theEntOrigin = theIter->second; + + Vector theAbsMax; + VectorAdd(theEntOrigin, theMaxSize, theAbsMax); + + Vector theAbsMin; + VectorAdd(theEntOrigin, inMinSize, theAbsMin); + + Vector theOrigin2; + VectorAdd(theAbsMax, theAbsMin, theOrigin2); + theOrigin2 = theOrigin2*.5f; + + Vector theSize2; + VectorSubtract(theAbsMax, theAbsMin, theSize2); + theSize2 = theSize2*.5f; + + // Do simple box collision here + if(NS_BoxesOverlap((float*)theOrigin1, (float*)theSize1, (float*)theOrigin2, (float*)theSize2)) + { + theEntBlocking = true; + break; + } + } + } + + return theEntBlocking; +} + + + +void AvHSHUMakeViewFriendlyKillerName(string& ioKillerName) +{ + string theOutputName = ioKillerName; + int theStrLen = (int)ioKillerName.length(); + + if(!strncmp(ioKillerName.c_str(), "weapon_", 7)) + { + theOutputName = ioKillerName.substr(7); + } + else if(!strncmp(ioKillerName.c_str(), "monster_", 8)) + { + theOutputName = ioKillerName.substr(8); + } + else if(!strncmp(ioKillerName.c_str(), "func_", 5)) + { + theOutputName = ioKillerName.substr(5); + } + + ioKillerName = theOutputName; +} + +bool AvHSHUGetCanDropItem(vec3_t& ioCenter, Vector& inMinSize, Vector& inMaxSize, float inMaxSlopeTangent, int inIgnoreIndex, bool inIgnorePlayers) +{ + + float theMaxXWidth = max(-inMinSize[0],inMaxSize[0]); + float theMaxYWidth = max(-inMinSize[1],inMaxSize[1]); + float theRadius = sqrt(theMaxXWidth*theMaxXWidth + theMaxYWidth * theMaxYWidth); + float theHeight = inMaxSize[2] - inMinSize[2]; + //adjust origin to be base + float theOrigin[3] = { ioCenter[0], ioCenter[1], ioCenter[2] + inMinSize[2] }; + + CollisionChecker Checker(pmove,kHLPointHullIndex,CollisionChecker::HULL_TYPE_ALL,inIgnorePlayers,CollisionChecker::IGNORE_NONE,inIgnoreIndex); + bool theCanDropItem = (Checker.GetContentsInCylinder(theOrigin,theRadius,theHeight) != CONTENTS_SOLID); + + + if(!theCanDropItem) //can't place it -- can we drop it? + { + + float theMaxDropHeight = theRadius * inMaxSlopeTangent; + + float theDropOrigin[3] = { theOrigin[0], theOrigin[1], theOrigin[2] + theMaxDropHeight }; + theCanDropItem = (Checker.GetContentsInCylinder(theDropOrigin,theRadius,theHeight) != CONTENTS_SOLID); + + if(theCanDropItem) //can drop it -- get as low to ground as possible for drop + { + + float theBestDropHeight = theMaxDropHeight; + float theShiftFraction = 1.0f; + for(float theShiftAdjust = 0.5f; theShiftAdjust > 0.05f; theShiftAdjust /= 2) + { + theShiftFraction += theShiftAdjust * (theCanDropItem ? -1 : 1); //try lower if last was a success + theDropOrigin[2] = theMaxDropHeight; + theDropOrigin[2] *= theShiftFraction; + theDropOrigin[2] += theOrigin[2]; + + theCanDropItem = (Checker.GetContentsInCylinder(theDropOrigin,theRadius,theHeight) != CONTENTS_SOLID); + if(theCanDropItem) + { + theBestDropHeight = theShiftFraction; + theBestDropHeight *= theMaxDropHeight; + } + } + + //adjust center to best position + ioCenter[2] += theBestDropHeight; + theCanDropItem = true; + } + } + + return theCanDropItem; +} + +bool AvHSHUTraceAndGetIsSiteValidForBuild(AvHMessageID inMessageID, const Vector& inPointOfView, const Vector& inNormRay, Vector* outLocation) +{ + bool theSuccess = false; + + // TODO: Check if area is within the mapextents + + int theUser3; + bool theTraceSuccess = AvHSHUTraceTangible(inPointOfView, inNormRay, &theUser3, outLocation); + + // : 0000291 + // ignore trace for scans (removed due to cost being paid when drop failed) + //if (inMessageID == BUILD_SCAN) + //{ + // theSuccess = true; + //} + //else + // : + if(theTraceSuccess) + { + // : 0000291 + if((inMessageID == BUILD_SCAN) || (AvHSHUGetIsSiteValidForBuild(inMessageID, outLocation))) + // : + { + theSuccess = true; + } + + } + return theSuccess; +} + +#ifdef AVH_CLIENT +bool AvHSHUGetEntityAtRay(const Vector& inPointOfView, const Vector& inNormRay, int& outEntIndex) +{ + bool theSuccess = false; + + // Offset starting position a little so we don't select ourselves + Vector theStartPosition; + VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theStartPosition); + + Vector theEndPos; + VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theEndPos); + + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + + // Store off the old count + gEngfuncs.pEventAPI->EV_PushPMStates(); + + // Now add in all of the players. + int theNumPlayers = gEngfuncs.GetMaxClients(); + gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 ); + + bool theDone = false; + int theEntityIndex = -1; + + vec3_t theNewStartPos; + AvHSHUGetFirstNonSolidPoint(theStartPosition, theEndPos, theNewStartPos); + VectorCopy(theNewStartPos, theStartPosition); + + do + { + pmtrace_t tr; + gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); + gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPosition, theEndPos, PM_NORMAL, theEntityIndex, &tr ); + + physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); + theEntityIndex = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr ); + + if(pEntity) + { + int theUser3 = pEntity->iuser3; + if(theEntityIndex > 0) + { + outEntIndex = theEntityIndex; + theSuccess = true; + theDone = true; + } + } + + if((tr.fraction >= (1.0f - kFloatTolerance)) || (tr.fraction < kFloatTolerance)) + { + theDone = true; + } + else + { + VectorCopy(tr.endpos, theStartPosition); + } + + } while(!theDone); + + gEngfuncs.pEventAPI->EV_PopPMStates(); + + return theSuccess; +} +#endif + +#ifdef AVH_SERVER +bool AvHSHUGetEntityAtRay(const Vector& inPointOfView, const Vector& inNormRay, int& outEntIndex) +{ + TraceResult tr; + Vector theStartPos; + Vector theEndPos; + bool theSuccess = false; + bool theDone = false; + + VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theStartPos); + VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theEndPos); + + CBaseEntity* theEntityHit = NULL; + edict_t* theEdictToIgnore = NULL; + + vec3_t theNewStartPos; + AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); + VectorCopy(theNewStartPos, theStartPos); + + do + { + UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, ignore_glass, theEdictToIgnore, &tr); + + theEntityHit = CBaseEntity::Instance(tr.pHit); + if(theEntityHit) + { + if(theEntityHit->entindex() > 0) + { + outEntIndex = theEntityHit->entindex(); + theSuccess = true; + theDone = true; + } + } + + + if((tr.flFraction > (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance)) + { + theDone = true; + } + else + { + if(theEntityHit) + { + theEdictToIgnore = ENT(theEntityHit->pev); + } + VectorCopy(tr.vecEndPos, theStartPos); + } + } while(!theDone); + + return theSuccess; +} +#endif + +const AvHMapExtents& AvHSHUGetMapExtents() +{ + #ifdef AVH_CLIENT + const AvHMapExtents& theMapExtents = gHUD.GetMapExtents(); + #endif + + #ifdef AVH_SERVER + const AvHMapExtents& theMapExtents = GetGameRules()->GetMapExtents(); + #endif + + return theMapExtents; +} + + +#ifdef AVH_CLIENT +bool AvHSUClientTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour) +{ + physent_t* theEntity = NULL; + vec3_t theStartPos; + vec3_t theEndPos; + int theFoundEntity = -1; + bool theSuccess = false; + bool theDone = false; + + VectorCopy(inStartPos, theStartPos); + VectorCopy(inEndPos, theEndPos); + + vec3_t theNormal; + VectorSubtract(inEndPos, inStartPos, theNormal); + VectorNormalize(theNormal); + + outIndex = -1; + + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + + // Store off the old count + gEngfuncs.pEventAPI->EV_PushPMStates(); + + // Now add in all of the players. + int theNumPlayers = gEngfuncs.GetMaxClients(); + gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 ); + + vec3_t theNewStartPos; + AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); + VectorCopy(theNewStartPos, theStartPos); + + do + { + pmtrace_t tr; + gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); + gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL/*PM_GLASS_IGNORE*/, theFoundEntity, &tr ); + + physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); + theFoundEntity = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr ); + + if(pEntity) + { + if(pEntity->iuser3 != AVH_USER3_NONE) + { + VectorCopy(tr.endpos, outLocation); + outIndex = theFoundEntity; + theEntity = pEntity; + outUserThree = pEntity->iuser3; + outUserFour = pEntity->iuser4; + outTeamNumber = (AvHTeamNumber)(pEntity->team); + + if(pEntity->player) + { + outPlayerWasHit = true; + } + + theSuccess = true; + theDone = true; + } + } + + if(tr.fraction >= (1.0f - kFloatTolerance)) + { + theDone = true; + } + else + { + vec3_t theDiff = theNormal*kHitOffsetAmount; + float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); + if(theLength > kFloatTolerance) + { + VectorAdd(tr.endpos, theDiff, theStartPos); + } + else + { + theDone = true; + } + // Offset a bit so we don't hit again + //VectorMA(tr.endpos, kHitOffsetAmount, theNormal, theStartPos); + } + } while(!theDone); + + gEngfuncs.pEventAPI->EV_PopPMStates(); + + // If we didn't hit any special targets, see if it's a valid area to build or be ordered to + if(!theSuccess) + { + WaypointReturnCode theReturnCode = WAYPOINT_SUCCESS; + theSuccess = AvHSHUClientTraceWaypoint(inStartPos, inEndPos, &outLocation, &theReturnCode); + bool theWaypointTooSteep = (theReturnCode == WAYPOINT_TOOSTEEP); + + if(theSuccess || theWaypointTooSteep) + { + outUserThree = AVH_USER3_WAYPOINT; + outPlayerWasHit = false; + outIndex = -1; + outTeamNumber = TEAM_IND; + theSuccess = true; + } + + // Treat too steep as success, but mark it as nobuild + if(theWaypointTooSteep) + { + outUserThree = AVH_USER3_NOBUILD; + } + } + + return theSuccess; +} +#endif + +#ifdef AVH_SERVER +bool AvHSUServerTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour) +{ + bool theSuccess = false; + bool theDone = false; + edict_t* theEdictToIgnore = NULL; + vec3_t theStartPos; + vec3_t theEndPos; + + VectorCopy(inStartPos, theStartPos); + VectorCopy(inEndPos, theEndPos); + + vec3_t theNormal; + VectorSubtract(inEndPos, inStartPos, theNormal); + VectorNormalize(theNormal); + +// vec3_t theNewStartPos; +// AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); +// VectorCopy(theNewStartPos, theStartPos); + + do + { + TraceResult tr; + UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, theEdictToIgnore, &tr); + CBaseEntity* theEntityHit = NULL; + + // Return the entity in special way + if(tr.flFraction < 1 && tr.pHit) + { + theEntityHit = CBaseEntity::Instance(tr.pHit); + AvHPlayer* thePlayer = dynamic_cast(theEntityHit); + AvHWeldable* theWeldable = dynamic_cast(theEntityHit); + + AvHBasePlayerWeapon* theWeapon = dynamic_cast(theEntityHit); + + if(AvHSUGetIsDebugging()) + { + // Create an entity where the trace hit + Vector theAngles(0, 0, 0); + + CBaseEntity* pEnt = CBaseEntity::Create(kwsDebugEntity, tr.vecEndPos, theAngles); + ASSERT(pEnt); + pEnt->pev->movetype = MOVETYPE_FLY; + pEnt->pev->solid = SOLID_NOT; + } + + if(theEntityHit && (theEntityHit->pev->iuser3 != AVH_USER3_NONE)) + { + // Don't hit seethroughs + if(theEntityHit->pev->iuser3 != AVH_USER3_ALPHA) + { + outIndex = ENTINDEX(tr.pHit); + VectorCopy(tr.vecEndPos, outLocation); + outTeamNumber = (AvHTeamNumber)theEntityHit->pev->team; + outUserThree = theEntityHit->pev->iuser3; + outUserFour = theEntityHit->pev->iuser4; + + if(thePlayer) + { + outPlayerWasHit = true; + } + + theSuccess = true; + theDone = true; + } + } + + theEdictToIgnore = theEntityHit->edict(); + } + + if((tr.flFraction >= (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance)) + { + theDone = true; + } + else + { + vec3_t theDiff = theNormal*kHitOffsetAmount; + float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); + if(theLength > kFloatTolerance) + { + VectorAdd(theStartPos, theDiff, theStartPos); + } + else + { + theDone = true; + } + // Offset a bit so we don't hit again + //VectorMA(tr.vecEndPos, kHitOffsetAmount, theNormal, theStartPos); + } + } while(!theDone); + + if(!theSuccess) + { + WaypointReturnCode theReturnCode = WAYPOINT_SUCCESS; + theSuccess = AvHSHUServerTraceWaypoint(inStartPos, inEndPos, &outLocation, &theReturnCode); + bool theWaypointTooSteep = (theReturnCode == WAYPOINT_TOOSTEEP); + if(theSuccess || theWaypointTooSteep) + { + outUserThree = AVH_USER3_WAYPOINT; + outPlayerWasHit = false; + outIndex = -1; + outTeamNumber = TEAM_IND; + theSuccess = true; + } + // Treat too steep as success, but mark it as nobuild + if(theWaypointTooSteep) + { + outUserThree = AVH_USER3_NOBUILD; + } + } + + return theSuccess; +} +#endif + + + +#ifdef AVH_CLIENT +bool AvHSHUClientTraceWaypoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t* outLocation, WaypointReturnCode* outReturnCode) +{ + vec3_t theStartPos; + vec3_t theEndPos; + int theFoundEntity = -1; + int theEntityToIgnore = -1; + bool theDone = false; + bool theLegalToBuild = false; + + VectorCopy(inStartPos, theStartPos); + VectorCopy(inEndPos, theEndPos); + + vec3_t theNormal; + VectorSubtract(inEndPos, inStartPos, theNormal); + VectorNormalize(theNormal); + + int theNumPlayers = gEngfuncs.GetMaxClients(); + pmtrace_t tr; + + //DebugPoint theDebugPoint(theStartPos[0], theStartPos[1], theStartPos[2]); + //gSquareDebugLocations.push_back(theDebugPoint); + + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + gEngfuncs.pEventAPI->EV_PushPMStates(); + gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1); + gEngfuncs.pEventAPI->EV_SetTraceHull(2); + + vec3_t theNewStartPos; + AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); + VectorCopy(theNewStartPos, theStartPos); + + do + { + gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL/*PM_GLASS_IGNORE*/, theEntityToIgnore, &tr ); + + physent_t* pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); + theFoundEntity = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr ); + + // Trace until we hit a worldbrush or non-seethrough + if(!pEntity || (pEntity->iuser3 != AVH_USER3_ALPHA)) + { + // If entity is a "no waypoint" entity we can't build here and we're done + if(pEntity && (pEntity->iuser3 == AVH_USER3_NOBUILD)) + { + if(outReturnCode) + { + *outReturnCode = WAYPOINT_NOBUILD; + } + theDone = true; + } + else + { + // else if texture is NOBUILD, we're done + const char* theTextureHitCStr = gEngfuncs.pEventAPI->EV_TraceTexture(theFoundEntity, tr.endpos, theEndPos); + if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kNoBuildTexture))) + { + if(outReturnCode) + { + *outReturnCode = WAYPOINT_NOBUILD; + } + theDone = true; + } + else if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kSeeThroughTexture))) + { + // Not valid, but allow it to pass through + if(outReturnCode) + { + *outReturnCode = WAYPOINT_SEETHROUGH; + } + } + else + { + // Trace texture sometimes seems to miss entities that the start point lies within. Don't count + // the trace texture if it found the texture of the next entity below it + // else if surface is more flat than vertical + if(tr.plane.normal[2] >= 0.7f) + { + // and if surface isn't under water, lava, in the sky, etc. + int thePointContents = gEngfuncs.PM_PointContents(tr.endpos, NULL); + if(thePointContents == CONTENTS_EMPTY) + { + // and if there's enough room to build + + // we can build here, and we're done + theLegalToBuild = true; + theDone = true; + + if(outReturnCode) + { + *outReturnCode = WAYPOINT_SUCCESS; + } + } + else + { + if(outReturnCode) + { + *outReturnCode = WAYPOINT_CONTENTSFULL; + theDone = true; + } + } + } + else + { + if(outReturnCode) + { + if(theTextureHitCStr) + { + *outReturnCode = WAYPOINT_TOOSTEEP; + } + else + { + *outReturnCode = WAYPOINT_ENTITYHIT; + } + theDone = true; + } + } + } + } + } + + if(theFoundEntity != 0) + { + theEntityToIgnore = theFoundEntity; + } + + if(((tr.fraction >= (1.0f - kFloatTolerance)) || (tr.fraction == 0.0f)) /*&& !tr.startsolid && !tr.allsolid*/) + { + theDone = true; + } + else + { + vec3_t theDiff = theNormal*kHitOffsetAmount; + float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); + if(theLength > kFloatTolerance) + { + VectorAdd(tr.endpos, theDiff, theStartPos); + } + else + { + theDone = true; + } + // Offset a bit so we don't hit again + //VectorMA(tr.endpos, kHitOffsetAmount, theNormal, theStartPos); + } + } while(!theDone); + + gEngfuncs.pEventAPI->EV_PopPMStates(); + + // Always set end location to show where invalid position is + *outLocation = tr.endpos; + + return theLegalToBuild; +} +#endif + +#ifdef AVH_CLIENT +void AvHSHUClientGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint) +{ + vec3_t theStartPos; + vec3_t theEndPos; + VectorCopy(inStartPos, theStartPos); + VectorCopy(inStartPos, outNonSolidPoint); + VectorCopy(inEndPos, theEndPos); + + pmtrace_t tr; + gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL, -1, &tr ); + + // Put this workaround in because a bug in EV_PlayerTrace means that when it starts solid, tr.fraction isn't returned (but it is in UTIL_TraceLine) + if((tr.startsolid) && (tr.fraction == 0.0f)) + { + int theFoundEntity = -1; + bool theDone = false; + + vec3_t theStartToEnd; + VectorSubtract(inEndPos, inStartPos, theStartToEnd); + + gEngfuncs.pEventAPI->EV_SetTraceHull(2); + + float theIncrement = 10.0f/theStartToEnd.Length(); + float theT = 0.0f; + int theNumIterations = 0; + + do + { + theStartPos = inStartPos + theT*theStartToEnd; + gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_WORLD_ONLY, -1, &tr ); + + theNumIterations++; + + // If start point is solid, bisect area and move start point 1/2 between current start and current end + if(tr.startsolid) + { + theT += theIncrement; + } + // else if start point isn't solid, bisect area and move start point back towards original start point + else + { + theDone = true; + } + } while(!theDone && (theNumIterations < 200)); + + // Always set end location to show where invalid position is + if(!theDone) + { + outNonSolidPoint = inStartPos; + } + else + { + outNonSolidPoint = theStartPos; + } + } +} +#endif + +#ifdef AVH_SERVER +void AvHSHUServerGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint) +{ + vec3_t theStartPos; + vec3_t theEndPos; + VectorCopy(inStartPos, theStartPos); + VectorCopy(inStartPos, outNonSolidPoint); + VectorCopy(inEndPos, theEndPos); + + TraceResult tr; + UTIL_TraceLine(theStartPos, theEndPos, ignore_monsters, NULL, &tr); + + bool theDone = false; + + // Put this workaround in because a bug in EV_PlayerTrace means that when it starts solid, tr.fraction isn't returned (but it is in UTIL_TraceLine) + if((tr.fStartSolid) || (tr.flFraction == 0.0f)) + { + int theFoundEntity = -1; + + vec3_t theStartToEnd; + VectorSubtract(inEndPos, inStartPos, theStartToEnd); + + float theIncrement = 10.0f/theStartToEnd.Length(); + float theT = 0.0f; + int theNumIterations = 0; + int thePointContents = 0; + + do + { + theStartPos = inStartPos + theT*theStartToEnd; + thePointContents = UTIL_PointContents(theStartPos); + + theNumIterations++; + + // If start point is solid, bisect area and move start point 1/2 between current start and current end + if((thePointContents == CONTENTS_SOLID) || (thePointContents == CONTENTS_SKY)) + { + theT += theIncrement; + } + // else if start point isn't solid, bisect area and move start point back towards original start point + else + { + theDone = true; + } + } while(!theDone && (theNumIterations < 200)); + + // Always set end location to show where invalid position is + outNonSolidPoint = theStartPos; + } + else + { + VectorCopy(tr.vecEndPos, outNonSolidPoint); + theDone = true; + } + + if(!theDone) + { + // When we don't hit anything, return start point + VectorCopy(inStartPos, outNonSolidPoint); + } + + VectorCopy(outNonSolidPoint, gPMDebugPoint); +} +#endif + +void AvHSHUGetFirstNonSolidPoint(float* inStartPos, float* inEndPos, float* outNonSolidPoint) +{ + vec3_t theStartPos; + vec3_t theEndPos; + + VectorCopy(inStartPos, theStartPos); + VectorCopy(inEndPos, theEndPos); + + vec3_t theNonSolidPoint; + AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNonSolidPoint); + + VectorCopy(theNonSolidPoint, outNonSolidPoint); +} + +void AvHSHUGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint) +{ + #ifdef AVH_CLIENT + AvHSHUClientGetFirstNonSolidPoint(inStartPos, inEndPos, outNonSolidPoint); + + //extern DebugPointListType gSquareDebugLocations; + //DebugPoint theDebugPoint(outNonSolidPoint[0], outNonSolidPoint[1], outNonSolidPoint[2]); + //gSquareDebugLocations.push_back(theDebugPoint); + #endif + + #ifdef AVH_SERVER + AvHSHUServerGetFirstNonSolidPoint(inStartPos, inEndPos, outNonSolidPoint); + #endif +} + +#ifdef AVH_SERVER +bool AvHSHUServerTraceWaypoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t* outLocation, WaypointReturnCode* outReturnCode) +{ + bool theLegalToBuild = false; + bool theDone = false; + edict_t* theEdictToIgnore = NULL; + TraceResult tr; + vec3_t theStartPos; + vec3_t theEndPos; + + VectorCopy(inStartPos, theStartPos); + VectorCopy(inEndPos, theEndPos); + + vec3_t theNormal; + VectorSubtract(inEndPos, inStartPos, theNormal); + VectorNormalize(theNormal); + + vec3_t theNewStartPos; + AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos); + VectorCopy(theNewStartPos, theStartPos); + + do + { + UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, theEdictToIgnore, &tr); + + // Trace until we hit a worldbrush or non-seethrough + CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); + CWorld* theWorld = dynamic_cast(theEntityHit); + bool theHitSeethrough = (theEntityHit->pev->iuser3 == AVH_USER3_ALPHA); + if(!theHitSeethrough) + { + // If entity is a "no waypoint" entity we can't build here and we're done + if(theEntityHit && (theEntityHit->pev->iuser3 == AVH_USER3_NOBUILD)) + { + if(outReturnCode) + { + *outReturnCode = WAYPOINT_NOBUILD; + } + theDone = true; + } + else + { + edict_t* theEdict = ENT(theEntityHit->pev); + const char* theTextureHitCStr = TRACE_TEXTURE(theEdict, tr.vecEndPos, theEndPos); + if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kNoBuildTexture))) + { + if(outReturnCode) + { + *outReturnCode = WAYPOINT_NOBUILD; + } + theDone = true; + } + else if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kSeeThroughTexture))) + { + // Do nothing, but allow it to pass through + if(outReturnCode) + { + *outReturnCode = WAYPOINT_SEETHROUGH; + } + } + else + { + // else if surface is more flat than vertical + if(tr.vecPlaneNormal.z >= .7f) + { + // and if surface isn't under water, lava, sky + int thePointContents = UTIL_PointContents(tr.vecEndPos); + if(thePointContents == CONTENTS_EMPTY) + { + // and if there's enough room to build + if(outReturnCode) + { + *outReturnCode = WAYPOINT_SUCCESS; + } + + // we can build here, and we're done + theLegalToBuild = true; + theDone = true; + } + else + { + if(outReturnCode) + { + *outReturnCode = WAYPOINT_CONTENTSFULL; + } + } + } + else + { + if(theTextureHitCStr) + { + *outReturnCode = WAYPOINT_TOOSTEEP; + } + else + { + *outReturnCode = WAYPOINT_ENTITYHIT; + } + theDone = true; + } + } + } + + if(!theWorld) + { + theEdictToIgnore = theEntityHit->edict(); + } + + if(((tr.flFraction >= (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance)) /*&& !tr.fStartSolid && !tr.fAllSolid*/) + { + theDone = true; + } + else + { + vec3_t theDiff = theNormal*kHitOffsetAmount; + float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); + if(theLength > kFloatTolerance) + { + //VectorAdd(tr.vecEndPos, theDiff, theStartPos); + VectorAdd(theStartPos, theDiff, theStartPos); + } + else + { + theDone = true; + } + // Offset a bit so we don't hit again + //VectorMA(tr.vecEndPos, kHitOffsetAmount, theNormal, theStartPos); + } + } + else + { + theEdictToIgnore = theEntityHit->edict(); + if(tr.vecEndPos == theStartPos) + { + vec3_t theDiff = theNormal*kHitOffsetAmount; + float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z); + if(theLength > kFloatTolerance) + { + //VectorAdd(tr.vecEndPos, theDiff, theStartPos); + VectorAdd(theStartPos, theDiff, theStartPos); + } + } + else + { + VectorCopy(tr.vecEndPos, theStartPos); + } +// vec3_t theEntityPosition = AvHSHUGetRealLocation(theEntityHit->pev->origin, theEntityHit->pev->mins, theEntityHit->pev->maxs); +// //VectorCopy(theEntityPosition, theStartPos); +// theStartPos[2] = theEntityPosition[2]; + } + } while(!theDone); + + // Always set end location to show where invalid position is + *outLocation = tr.vecEndPos; + + return theLegalToBuild; +} +#endif + +bool AvHSHUTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour) +{ + bool theSuccess = false; + + outPlayerWasHit = false; + outUserThree = 0; + outUserFour = 0; + + // On client, set up players for prediction and do a PM_TraceLine + #ifdef AVH_CLIENT + theSuccess = AvHSUClientTraceTangible(inStartPos, inEndPos, outIndex, outLocation, outTeamNumber, outPlayerWasHit, outUserThree, outUserFour); + #endif + + // On the server, do a UTIL_TraceLine + #ifdef AVH_SERVER + theSuccess = AvHSUServerTraceTangible(inStartPos, inEndPos, outIndex, outLocation, outTeamNumber, outPlayerWasHit, outUserThree, outUserFour); + #endif + + return theSuccess; +} + +bool AvHSHUTraceTangible(const Vector& inPointOfView, const Vector& inNormRay, int* outUserThree, vec3_t* outLocation, AvHTeamNumber* outTeamNumber, bool* outPlayerWasHit) +{ + bool theSuccess = false; + vec3_t theTraceStart; + vec3_t theTraceEnd; + vec3_t theFoundLocation; + int theFoundIndex = -1; + int theUserThree = 0; + int theUserFour = 0; + AvHTeamNumber theTeamOfThingHit; + bool thePlayerHit = false; + + // Offset a little so we don't hit the commander + VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theTraceStart); + VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theTraceEnd); + + if(!outUserThree) + outUserThree = &theUserThree; + + if(!outLocation) + outLocation = &theFoundLocation; + + if(!outTeamNumber) + outTeamNumber = &theTeamOfThingHit; + + if(!outPlayerWasHit) + outPlayerWasHit = &thePlayerHit; + + theSuccess = AvHSHUTraceTangible(theTraceStart, theTraceEnd, theFoundIndex, *outLocation, *outTeamNumber, *outPlayerWasHit, *outUserThree, theUserFour); + + return theSuccess; +} + +bool AvHSHUTraceVerticalTangible(float inX, float inY, float inZ, int& outUserThree, float& outHeight) +{ + bool theSuccess = false; + + // Create a start pos above + vec3_t theStartPos(inX, inY, inZ - 100.0f); + //vec3_t theEndPos(inX, inY, inZ - 800.0f/*-4096.0f*/); + vec3_t theEndPos(inX, inY, (float)kWorldMinimumZ); + +// #ifdef AVH_CLIENT +// extern DebugPointListType gTriDebugLocations; +// bool theDrawDebug = true;//(rand() % 100 == 0); +// +// if(theDrawDebug) +// { +// DebugPoint thePoint; +// thePoint.x = theStartPos.x; +// thePoint.y = theStartPos.y; +// thePoint.z = theStartPos.z; +// gTriDebugLocations.push_back(thePoint); +// +// thePoint.z = theEndPos.z; +// gTriDebugLocations.push_back(thePoint); +// } +// +// #endif + + int theIndex; + vec3_t theEndLocation; + AvHTeamNumber theTeam; + bool thePlayerHit; + int theUserFour; + + theSuccess = AvHSHUTraceTangible(theStartPos, theEndPos, theIndex, theEndLocation, theTeam, thePlayerHit, outUserThree, theUserFour); + if(theSuccess) + { + outHeight = theEndLocation.z; + ASSERT(outHeight <= inZ); + } + + return theSuccess; +} + +const char* AvHSHUGetCommonSoundName(bool inIsAlien, WeaponHUDSound inHUDSound) +{ + const char* theSoundName = NULL; + + switch(inHUDSound) + { + case WEAPON_SOUND_HUD_ON: + theSoundName = inIsAlien ? "common/wpn_hudon-a.wav" : "common/wpn_hudon.wav"; + break; + case WEAPON_SOUND_HUD_OFF: + theSoundName = inIsAlien ? "common/wpn_hudoff-a.wav" : "common/wpn_hudoff.wav"; + break; + case WEAPON_SOUND_MOVE_SELECT: + theSoundName = inIsAlien ? "common/wpn_moveselect-a.wav" : "common/wpn_moveselect.wav"; + break; + case WEAPON_SOUND_SELECT: + theSoundName = inIsAlien ? "common/wpn_select-a.wav" : "common/wpn_select.wav"; + break; + case WEAPON_SOUND_DENYSELECT: + theSoundName = inIsAlien ? "common/wpn_denyselect-a.wav" : "common/wpn_denyselect.wav"; + break; + } + + return theSoundName; +} + + +// Values for fuser1: 0-1 means it's building. 2-3 means it's researching (see kResearchFuser1Base), 3-4 is "energy" (returns both building and researching) +const float kResearchFuser1Base = 2.0f; +const float kEnergyFuser1Base = 3.0f; + +void AvHSHUGetBuildResearchState(int inUser3, int inUser4, float inFuser1, bool& outIsBuilding, bool& outIsResearching, float& outNormalizedPercentage) +{ + outIsBuilding = outIsResearching = false; + + float theStatus = (inFuser1/kNormalizationNetworkFactor); + + if((GetHasUpgrade(inUser4, MASK_BUILDABLE) || (inUser3 == AVH_USER3_WELD)) && !GetHasUpgrade(inUser4, MASK_RECYCLING)) + { + if((theStatus >= 0.0f) && (theStatus <= 1.0f)) + { + outNormalizedPercentage = theStatus; + outIsBuilding = true; + } + if(inUser3 == AVH_USER3_WELD) + { + if(outNormalizedPercentage == -1) + { + outIsBuilding = outIsResearching = false; + } + } + } + else + { + if((theStatus > kResearchFuser1Base) && (theStatus <= (1.0f + kResearchFuser1Base))) + { + outNormalizedPercentage = theStatus - kResearchFuser1Base; + outIsResearching = true; + } + else if((theStatus > kEnergyFuser1Base) && (theStatus <= (1.0f + kEnergyFuser1Base))) + { + // Set "energy" + outNormalizedPercentage = theStatus - kEnergyFuser1Base; + outIsBuilding = outIsResearching = true; + } + else + { + outNormalizedPercentage = 1.0f; + } + } +} + +void AvHSHUSetEnergyState(int inUser3, float &outFuser1, float inNormalizedPercentage) +{ + outFuser1 = (kEnergyFuser1Base + inNormalizedPercentage)*kNormalizationNetworkFactor; +} + +void AvHSHUSetBuildResearchState(int inUser3, int inUser4, float &outFuser1, bool inTrueBuildOrFalseResearch, float inNormalizedPercentage) +{ + //ASSERT(GetHasUpgrade(inUser4, MASK_BUILDABLE) /*|| GetHasUpgrade(inUser4, MASK_SELECTABLE)*/ || (inUser3 == AVH_USER3_WELD)); + + if(inNormalizedPercentage != -1) + { + inNormalizedPercentage = min(inNormalizedPercentage, 1.0f); + inNormalizedPercentage = max(inNormalizedPercentage, 0.0f); + } + + // Build + if(inTrueBuildOrFalseResearch) + { + //ASSERT(GetHasUpgrade(inUser4, MASK_BUILDABLE) || (inUser3 == AVH_USER3_WELD)); + + outFuser1 = inNormalizedPercentage*kNormalizationNetworkFactor; + } + // Research + else + { + outFuser1 = (kResearchFuser1Base + inNormalizedPercentage)*kNormalizationNetworkFactor; + } +} + + +string AvHSHUBuildExecutableScriptName(const string& inScriptName, const string& inCurrentMapName) +{ + string theExecutableScriptName = string(getModDirectory()) + string("/") + kScriptsDirectory + string("/") + inCurrentMapName + string("/") + inScriptName; + + return theExecutableScriptName; +} + +string AvHSHUGetTimeDateString() +{ + string theTimeDateString(""); + + time_t theTimeThingie = time ((time_t *) NULL); + struct tm* theTime = localtime(&theTimeThingie); + + if(theTime) + { + // 04/22/03, 16:59:12 + char theBuffer[512]; + sprintf(theBuffer, "%.2d/%.2d/%.2d, %.2d:%.2d:%.2d", (theTime->tm_mon + 1), theTime->tm_mday, (theTime->tm_year - 100), theTime->tm_hour, theTime->tm_min, theTime->tm_sec); + theTimeDateString = theBuffer; + } + + return theTimeDateString; +} + +bool AvHSHUGetNameOfLocation(const AvHBaseInfoLocationListType& inLocations, vec3_t inLocation, string& outLocation) +{ + bool theSuccess = false; + + // Look at our current position, and see if we lie within of the map locations + for(AvHBaseInfoLocationListType::const_iterator theIter = inLocations.begin(); theIter != inLocations.end(); theIter++) + { + if(theIter->GetIsPointInRegion(inLocation)) + { + outLocation = theIter->GetLocationName(); + theSuccess = true; + break; + } + } + + return theSuccess; +} + +// Also check AvHSUGetAlwaysPlayAlert to make sure some alerts are always triggered +bool AvHSHUGetForceHUDSound(AvHHUDSound inHUDSound) +{ + bool theForceSound = false; + + switch(inHUDSound) + { + case HUD_SOUND_ALIEN_HIVE_COMPLETE: + case HUD_SOUND_ALIEN_HIVE_DYING: + case HUD_SOUND_ALIEN_HIVE_ATTACK: + case HUD_SOUND_ALIEN_ENEMY_APPROACHES: + case HUD_SOUND_MARINE_CCUNDERATTACK: + case HUD_SOUND_SQUAD1: + case HUD_SOUND_SQUAD2: + case HUD_SOUND_SQUAD3: + case HUD_SOUND_SQUAD4: + case HUD_SOUND_SQUAD5: + theForceSound = true; + break; + } + + return theForceSound; +} diff --git a/main/source/mod/AvHSoundListManager.cpp b/main/source/mod/AvHSoundListManager.cpp index ff27739..277c0ea 100644 --- a/main/source/mod/AvHSoundListManager.cpp +++ b/main/source/mod/AvHSoundListManager.cpp @@ -1,166 +1,166 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHSoundListManager.cpp $ -// $Date: 2002/11/22 21:28:17 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHSoundListManager.cpp,v $ -// Revision 1.10 2002/11/22 21:28:17 Flayra -// - mp_consistency changes -// -// Revision 1.9 2002/09/09 20:06:56 Flayra -// - Added custom attention parameter -// -// Revision 1.8 2002/07/26 01:51:57 Flayra -// - Linux support for FindFirst/FindNext -// -// Revision 1.7 2002/07/25 16:58:00 flayra -// - Linux changes -// -// Revision 1.6 2002/07/01 21:48:10 Flayra -// - Added debug code, added document headers -// -//=============================================================================== -#include "mod/AvHSoundListManager.h" -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "dlls/cbase.h" -#include "mod/AvHConstants.h" -#include "util/STLUtil.h" - -#define kMaxNumberLists 200 - -AvHSoundListManager::AvHSoundListManager() -{ - // Needed so strings are allocated and moved around after being precached - this->mSoundList.resize(kMaxNumberLists); -} - -bool AvHSoundListManager::BuildSoundList(const string& inDirName, CStringList& outList) -{ - bool theSuccess = false; - - string theBaseDirectoryName = string(getModDirectory()) + string("/") + string(kSoundDirectory) + string("/"); - string theFullDirName = inDirName; - if(BuildFileList(theBaseDirectoryName, theFullDirName, "*.wav", outList)) - { - theSuccess = true; - } - - return theSuccess; -} - -void AvHSoundListManager::Clear() -{ - this->mSoundList.clear(); -} - -SoundListType::iterator AvHSoundListManager::GetSoundList(const string& inKey) -{ - // TODO: lower case inDirName and save as theKey - string theKey = inKey; - - // Check if we have a sound list with this name - SoundListType::iterator theIterator; - for(theIterator = this->mSoundList.begin(); theIterator != this->mSoundList.end(); theIterator++) - { - if(theIterator->first == theKey) - { - break; - } - } - - return theIterator; -} - -bool AvHSoundListManager::PrecacheSoundList(const string& inDirName) -{ - bool theReturnValue = false; - - // Check if we have a sound list with this name - SoundListType::iterator theIter = this->GetSoundList(inDirName); - - // If so, return false - if(theIter == this->mSoundList.end()) - { - // If not, read list of all sound files in this directory, add this to the list with theKey as a key - CStringList theNewSoundList; - if(this->BuildSoundList(inDirName, theNewSoundList)) - { - // Save entry - this->mSoundList.push_back(make_pair(inDirName, theNewSoundList)); - - // NOW, precache each sound because it remembers the pointer, so it has to be the final version - SoundListType::iterator theIter = this->GetSoundList(inDirName); - ASSERT(theIter != this->mSoundList.end()); - - // Get number of entries - CStringList& theList = theIter->second; - int theNumEntries = theList.size(); - for(int i = 0; i < theNumEntries; i++) - { - CString& theSoundToPrecache = theIter->second[i]; - - int iString = ALLOC_STRING((char*)theSoundToPrecache);//voogru: We cant do "(char*)theSoundToPrecache" directly cause it causes some wierd problems. - PRECACHE_UNMODIFIED_SOUND((char*)STRING(iString)); - - // Success - theReturnValue = true; - } - } - else - { - int a = 0; - } - } - return theReturnValue; -} - -bool AvHSoundListManager::PlaySoundInList(const string& inDirName, CBaseEntity* inEntity, int inChannel, float inVolume, float inAttenuation) -{ - bool theReturnValue = false; - - // Check if we have a sound list with this name - SoundListType::iterator theIter = this->GetSoundList(inDirName); - - // If not, return false - if(theIter != this->mSoundList.end()) - { - // Get number of entries - CStringList& theList = theIter->second; - int theNumEntries = theList.size(); - if(theNumEntries > 0) - { - // Pick one at random - int theOneToPlay = RANDOM_LONG(0, theNumEntries - 1); - - // Play it! - CString& theSoundToPlay = theIter->second[theOneToPlay]; - char* theCStrToPlay = (char*)theSoundToPlay; - if(theCStrToPlay) - { - //UTIL_LogPrintf("AvHSoundListManager::Playing sound: %s\n", theCStrToPlay); - - EMIT_SOUND(ENT(inEntity->pev), inChannel, theCStrToPlay, inVolume, inAttenuation); - //int theRandomChannel = RANDOM_LONG(0, 6); - //EMIT_SOUND_DYN(ENT(inEntity->pev), , (char*)theSoundToPlay, 1.0, ATTN_NORM, 0, 95); - - theReturnValue = true; - } - else - { - int a = 0; - } - } - } - - return theReturnValue; -} - +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHSoundListManager.cpp $ +// $Date: 2002/11/22 21:28:17 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHSoundListManager.cpp,v $ +// Revision 1.10 2002/11/22 21:28:17 Flayra +// - mp_consistency changes +// +// Revision 1.9 2002/09/09 20:06:56 Flayra +// - Added custom attention parameter +// +// Revision 1.8 2002/07/26 01:51:57 Flayra +// - Linux support for FindFirst/FindNext +// +// Revision 1.7 2002/07/25 16:58:00 flayra +// - Linux changes +// +// Revision 1.6 2002/07/01 21:48:10 Flayra +// - Added debug code, added document headers +// +//=============================================================================== +#include "mod/AvHSoundListManager.h" +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "dlls/cbase.h" +#include "mod/AvHConstants.h" +#include "util/STLUtil.h" + +#define kMaxNumberLists 200 + +AvHSoundListManager::AvHSoundListManager() +{ + // Needed so strings are allocated and moved around after being precached + this->mSoundList.resize(kMaxNumberLists); +} + +bool AvHSoundListManager::BuildSoundList(const string& inDirName, CStringList& outList) +{ + bool theSuccess = false; + + string theBaseDirectoryName = string(getModDirectory()) + string("/") + string(kSoundDirectory) + string("/"); + string theFullDirName = inDirName; + if(BuildFileList(theBaseDirectoryName, theFullDirName, "*.wav", outList)) + { + theSuccess = true; + } + + return theSuccess; +} + +void AvHSoundListManager::Clear() +{ + this->mSoundList.clear(); +} + +SoundListType::iterator AvHSoundListManager::GetSoundList(const string& inKey) +{ + // TODO: lower case inDirName and save as theKey + string theKey = inKey; + + // Check if we have a sound list with this name + SoundListType::iterator theIterator; + for(theIterator = this->mSoundList.begin(); theIterator != this->mSoundList.end(); theIterator++) + { + if(theIterator->first == theKey) + { + break; + } + } + + return theIterator; +} + +bool AvHSoundListManager::PrecacheSoundList(const string& inDirName) +{ + bool theReturnValue = false; + + // Check if we have a sound list with this name + SoundListType::iterator theIter = this->GetSoundList(inDirName); + + // If so, return false + if(theIter == this->mSoundList.end()) + { + // If not, read list of all sound files in this directory, add this to the list with theKey as a key + CStringList theNewSoundList; + if(this->BuildSoundList(inDirName, theNewSoundList)) + { + // Save entry + this->mSoundList.push_back(make_pair(inDirName, theNewSoundList)); + + // NOW, precache each sound because it remembers the pointer, so it has to be the final version + SoundListType::iterator theIter = this->GetSoundList(inDirName); + ASSERT(theIter != this->mSoundList.end()); + + // Get number of entries + CStringList& theList = theIter->second; + int theNumEntries = theList.size(); + for(int i = 0; i < theNumEntries; i++) + { + CString& theSoundToPrecache = theIter->second[i]; + + int iString = ALLOC_STRING((char*)theSoundToPrecache);//: We cant do "(char*)theSoundToPrecache" directly cause it causes some wierd problems. + PRECACHE_UNMODIFIED_SOUND((char*)STRING(iString)); + + // Success + theReturnValue = true; + } + } + else + { + int a = 0; + } + } + return theReturnValue; +} + +bool AvHSoundListManager::PlaySoundInList(const string& inDirName, CBaseEntity* inEntity, int inChannel, float inVolume, float inAttenuation) +{ + bool theReturnValue = false; + + // Check if we have a sound list with this name + SoundListType::iterator theIter = this->GetSoundList(inDirName); + + // If not, return false + if(theIter != this->mSoundList.end()) + { + // Get number of entries + CStringList& theList = theIter->second; + int theNumEntries = theList.size(); + if(theNumEntries > 0) + { + // Pick one at random + int theOneToPlay = RANDOM_LONG(0, theNumEntries - 1); + + // Play it! + CString& theSoundToPlay = theIter->second[theOneToPlay]; + char* theCStrToPlay = (char*)theSoundToPlay; + if(theCStrToPlay) + { + //UTIL_LogPrintf("AvHSoundListManager::Playing sound: %s\n", theCStrToPlay); + + EMIT_SOUND(ENT(inEntity->pev), inChannel, theCStrToPlay, inVolume, inAttenuation); + //int theRandomChannel = RANDOM_LONG(0, 6); + //EMIT_SOUND_DYN(ENT(inEntity->pev), , (char*)theSoundToPlay, 1.0, ATTN_NORM, 0, 95); + + theReturnValue = true; + } + else + { + int a = 0; + } + } + } + + return theReturnValue; +} + diff --git a/main/source/mod/AvHSpecials.cpp b/main/source/mod/AvHSpecials.cpp index c69345c..97ca8ca 100644 --- a/main/source/mod/AvHSpecials.cpp +++ b/main/source/mod/AvHSpecials.cpp @@ -640,6 +640,7 @@ void AvHRemoveUpgradeInCategory(AvHAlienUpgradeCategory inUpgradeCategory, int& } else if(theNumUpgradesInCategory == 3) { + SetUpgradeMask(&inUser4, MASK_UPGRADE_10); SetUpgradeMask(&inUser4, MASK_UPGRADE_11, false); } break; @@ -673,6 +674,7 @@ void AvHRemoveUpgradeInCategory(AvHAlienUpgradeCategory inUpgradeCategory, int& } else if(theNumUpgradesInCategory == 3) { + SetUpgradeMask(&inUser4, MASK_UPGRADE_12); SetUpgradeMask(&inUser4, MASK_UPGRADE_13, false); } break; @@ -689,6 +691,7 @@ void AvHRemoveUpgradeInCategory(AvHAlienUpgradeCategory inUpgradeCategory, int& } else if(theNumUpgradesInCategory == 3) { + SetUpgradeMask(&inUser4, MASK_UPGRADE_14); SetUpgradeMask(&inUser4, MASK_UPGRADE_15, false); } break; diff --git a/main/source/mod/AvHSpitGun.cpp b/main/source/mod/AvHSpitGun.cpp index 4242f75..a18f790 100644 --- a/main/source/mod/AvHSpitGun.cpp +++ b/main/source/mod/AvHSpitGun.cpp @@ -56,6 +56,7 @@ #include "common/vector_util.h" #include "mod/AvHAlienWeaponConstants.h" #include "mod/AvHPlayerUpgrade.h" +#include "mod/AvHHive.h" #ifdef AVH_SERVER #include "mod/AvHGamerules.h" @@ -144,6 +145,11 @@ void AvHSpit::SpitTouch(CBaseEntity* pOther) // Kill it off this->SpitDeath(); } + AvHHive *hive=dynamic_cast(pOther); + if ( hive && hive->GetIsSpawning() ) { + CBaseEntity *pOwner = CBaseEntity::Instance( this->pev->owner ); + hive->TeleportUse(pOwner, pOwner, USE_ON, 0); + } } #endif diff --git a/main/source/mod/AvHSpores.cpp b/main/source/mod/AvHSpores.cpp index 15206c4..ad8ad68 100644 --- a/main/source/mod/AvHSpores.cpp +++ b/main/source/mod/AvHSpores.cpp @@ -1,355 +1,355 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHSpores.cpp $ -// $Date: 2002/11/22 21:28:17 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHSpores.cpp,v $ -// Revision 1.13 2002/11/22 21:28:17 Flayra -// - mp_consistency changes -// -// Revision 1.12 2002/09/23 22:33:21 Flayra -// - Spores no longer hurt buildings -// -// Revision 1.11 2002/08/16 02:48:10 Flayra -// - New damage model -// -// Revision 1.10 2002/07/24 19:09:18 Flayra -// - Linux issues -// -// Revision 1.9 2002/07/24 18:55:52 Flayra -// - Linux case sensitivity stuff -// -// Revision 1.8 2002/07/24 18:45:43 Flayra -// - Linux and scripting changes -// -// Revision 1.7 2002/06/25 17:49:01 Flayra -// - Some refactoring, new view model artwork, removed old code -// -// Revision 1.6 2002/06/03 16:39:10 Flayra -// - Added different deploy times (this should be refactored a bit more) -// -// Revision 1.5 2002/05/23 02:32:57 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "mod/AvHAlienWeapons.h" -#include "mod/AvHPlayer.h" - -#ifdef AVH_CLIENT -#include "cl_dll/eventscripts.h" -#include "cl_dll/in_defs.h" -#include "cl_dll/wrect.h" -#include "cl_dll/cl_dll.h" -#endif - -#include "common/hldm.h" -#include "common/event_api.h" -#include "common/event_args.h" -#include "common/vector_util.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHPlayerUpgrade.h" - -#ifdef AVH_SERVER -#include "mod/AvHGamerules.h" -#endif - -#include "mod/AvHAlienWeaponConstants.h" - -LINK_ENTITY_TO_CLASS(kwSporeGun, AvHSpore); - -#ifdef AVH_SERVER -LINK_ENTITY_TO_CLASS(kwSporeProjectile, AvHSporeProjectile); -extern int gSporeCloudEventID; - -AvHSporeProjectile::AvHSporeProjectile() -{ - this->mTimeHit = -1; - this->mDamage = 0.0f; -} - -void AvHSporeProjectile::Precache(void) -{ - CBaseEntity::Precache(); - - PRECACHE_UNMODIFIED_MODEL(kSporeSprite); - PRECACHE_UNMODIFIED_MODEL(kClientSporeSprite); -} - -void AvHSporeProjectile::SetDamage(float inDamage) -{ - this->mDamage = inDamage; -} - -void AvHSporeProjectile::Spawn(void) -{ - this->Precache(); - CBaseEntity::Spawn(); - - this->pev->movetype = MOVETYPE_FLY; - this->pev->classname = MAKE_STRING(kwsSporeProjectile); - - SET_MODEL(ENT(this->pev), kClientSporeSprite); - this->pev->solid = SOLID_BBOX; - - if(!GetGameRules()->GetDrawInvisibleEntities()) - { - this->pev->effects = EF_NODRAW; - } - else - { - this->pev->frame = 0; - this->pev->scale = 0.5; - this->pev->rendermode = kRenderTransAlpha; - this->pev->renderamt = 255; - } - - UTIL_SetSize(this->pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - //UTIL_SetSize(this->pev, Vector( -50, -50, -50), Vector(50, 50, 50)); - - SetTouch(&AvHSporeProjectile::SporeTouch); -} - -void AvHSporeProjectile::SporeCloudThink() -{ - // Apply damage to all enemy players in radius - CBaseEntity* theEntity = NULL; - while ( (theEntity = UTIL_FindEntityInSphere( theEntity, this->pev->origin, BALANCE_VAR(kSporeCloudRadius))) != NULL ) - { - // Only hurt players - AvHPlayer* thePlayer = dynamic_cast(theEntity); - if(thePlayer) - { - ASSERT(this->pev->team != 0); - if(theEntity->pev->team != this->pev->team) - { - // Don't do damage to heavy armor - // puzl: 1019 - // Spores can't damage commanders - if(!thePlayer->GetHasHeavyArmor() && !thePlayer->GetIsInTopDownMode() ) - { - // Spores don't stack, so don't do damage too often - float theTimeOfLastSporeDamage = thePlayer->GetTimeOfLastSporeDamage(); - float kTolerance = .05f; - if((theTimeOfLastSporeDamage == -1) || (gpGlobals->time > (theTimeOfLastSporeDamage + BALANCE_VAR(kSporeCloudThinkInterval) - kTolerance))) - { - // Make sure a direct line can be traced from spores to target - TraceResult theTraceResult; - UTIL_TraceLine(this->pev->origin, theEntity->pev->origin, ignore_monsters, dont_ignore_glass, theEntity->edict(), &theTraceResult); - - if(theTraceResult.flFraction == 1.0f) - { - theEntity->TakeDamage(this->pev, VARS(this->pev->owner), this->mDamage, NS_DMG_NORMAL); - - thePlayer->SetTimeOfLastSporeDamage(gpGlobals->time); - } - else - { - // What did we hit? - CBaseEntity* theEntity = CBaseEntity::Instance(theTraceResult.pHit); - } - } - } - - // TODO: Fire event? - } - } - } - - // Is it time to expire? - if(gpGlobals->time > (this->mTimeHit + BALANCE_VAR(kSporeCloudTime))) - { - // if so, remove entity - SetThink(&AvHSporeProjectile::SUB_Remove); - this->pev->nextthink = gpGlobals->time + 0.01f; - } - else - { - // if not, set next think - this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kSporeCloudThinkInterval); - } -} - -void AvHSporeProjectile::SporeTouch(CBaseEntity *pOther) -{ - // Never hit the player who fired it - if(pOther != CBaseEntity::Instance(this->pev->owner)) - { - // If not in tourny mode, pass through friends - //if(GetGameRules()->GetIsTournamentMode() || (pOther->pev->team != this->pev->team)) - //{ - // Tell the projectile to stop moving - VectorCopy(g_vecZero, this->pev->velocity); - - this->mTimeHit = gpGlobals->time; - SetThink(&AvHSporeProjectile::SporeCloudThink); - this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kSporeCloudThinkInterval); - - SetTouch(NULL); - - // Fire spore cloud event - PLAYBACK_EVENT_FULL(0, this->edict(), gSporeCloudEventID, 0, pOther->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - //} - } -} -#endif - - - -int AvHSpore::GetBarrelLength() const -{ - return kSporeBarrelLength; -} - -float AvHSpore::GetRateOfFire() const -{ - return BALANCE_VAR(kSporeROF); -} - -bool AvHSpore::GetFiresUnderwater() const -{ - return true; -} - -bool AvHSpore::GetIsDroppable() const -{ - return false; -} - -void AvHSpore::Init() -{ - this->mRange = kSporeRange; - this->mDamage = BALANCE_VAR(kSporeDamage); -} - -int AvHSpore::GetDamageType() const -{ - return NS_DMG_NORMAL; -} - -int AvHSpore::GetIdleAnimation() const -{ - return 2; -} - -int AvHSpore::GetShootAnimation() const -{ - return 5; -} - -int AvHSpore::GetDeployAnimation() const -{ - // Look at most recently used weapon and see if we can transition from it - int theDeployAnimation = -1; - - AvHWeaponID thePreviousID = this->GetPreviousWeaponID(); - - switch(thePreviousID) - { - case AVH_WEAPON_BITE2: - theDeployAnimation = 9; - break; - case AVH_WEAPON_SPIKE: - theDeployAnimation = 8; - break; - } - - return theDeployAnimation; -} - -float AvHSpore::GetDeployTime() const -{ - return .6f; -} - -char* AvHSpore::GetViewModel() const -{ - return kLevel3ViewModel; -} - -void AvHSpore::Precache(void) -{ - AvHAlienWeapon::Precache(); - - PRECACHE_UNMODIFIED_SOUND(kSporeFireSound); - PRECACHE_UNMODIFIED_SOUND(kSporeCloudSound); - - this->mEvent = PRECACHE_EVENT(1, kSporeShootEventName); -} - -void AvHSpore::Spawn() -{ - AvHAlienWeapon::Spawn(); - - Precache(); - - this->m_iId = AVH_WEAPON_SPORES; - - // Set our class name - this->pev->classname = MAKE_STRING(kwsSporeGun); - - SET_MODEL(ENT(this->pev), kNullModel); - - FallInit();// get ready to fall down. - -} - -bool AvHSpore::UsesAmmo(void) const -{ - return false; -} - -void AvHSpore::FireProjectiles(void) -{ - #ifdef AVH_SERVER - // Make sure we have enough points to shoot this thing - AvHPlayer* thePlayer = dynamic_cast(this->m_pPlayer); - ASSERT(thePlayer); - - //if(thePlayer->GetResources() > kSporePointCost) - //{ - // // Decrement kSporePointCost points - // thePlayer->SetResources(thePlayer->GetResources() - kSporePointCost); - - // Create hidden projectile that creates a huge spore cloud where it hits - AvHSporeProjectile* theSpore = GetClassPtr((AvHSporeProjectile*)NULL ); - theSpore->Spawn(); - - UTIL_MakeVectors(this->m_pPlayer->pev->v_angle); - - Vector vecAiming = gpGlobals->v_forward; - Vector vecSrc = this->m_pPlayer->GetGunPosition( ) + vecAiming; - - UTIL_SetOrigin(theSpore->pev, vecSrc); - -// // This needs to be the same as in EV_SporeGun -// Vector theBaseVelocity; -// VectorScale(this->pev->velocity, kSporeParentVelocityScalar, theBaseVelocity); -// -// Vector theStartVelocity; -// VectorMA(theBaseVelocity, kSporeVelocity, vecAiming, theStartVelocity); -// -// VectorCopy(theStartVelocity, theSpore->pev->velocity); - - VectorScale(vecAiming, kShootCloudVelocity, theSpore->pev->velocity); - - // Set owner - theSpore->pev->owner = ENT(this->m_pPlayer->pev); - - // Set spore's team :) - theSpore->pev->team = this->m_pPlayer->pev->team; - - // Set the spore damage - float theDamage = this->mDamage*AvHPlayerUpgrade::GetAlienRangedDamageUpgrade(this->m_pPlayer->pev->iuser4); - theSpore->SetDamage(theDamage); - //} - #endif -} - +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHSpores.cpp $ +// $Date: 2002/11/22 21:28:17 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHSpores.cpp,v $ +// Revision 1.13 2002/11/22 21:28:17 Flayra +// - mp_consistency changes +// +// Revision 1.12 2002/09/23 22:33:21 Flayra +// - Spores no longer hurt buildings +// +// Revision 1.11 2002/08/16 02:48:10 Flayra +// - New damage model +// +// Revision 1.10 2002/07/24 19:09:18 Flayra +// - Linux issues +// +// Revision 1.9 2002/07/24 18:55:52 Flayra +// - Linux case sensitivity stuff +// +// Revision 1.8 2002/07/24 18:45:43 Flayra +// - Linux and scripting changes +// +// Revision 1.7 2002/06/25 17:49:01 Flayra +// - Some refactoring, new view model artwork, removed old code +// +// Revision 1.6 2002/06/03 16:39:10 Flayra +// - Added different deploy times (this should be refactored a bit more) +// +// Revision 1.5 2002/05/23 02:32:57 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "mod/AvHAlienWeapons.h" +#include "mod/AvHPlayer.h" + +#ifdef AVH_CLIENT +#include "cl_dll/eventscripts.h" +#include "cl_dll/in_defs.h" +#include "cl_dll/wrect.h" +#include "cl_dll/cl_dll.h" +#endif + +#include "common/hldm.h" +#include "common/event_api.h" +#include "common/event_args.h" +#include "common/vector_util.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHPlayerUpgrade.h" + +#ifdef AVH_SERVER +#include "mod/AvHGamerules.h" +#endif + +#include "mod/AvHAlienWeaponConstants.h" + +LINK_ENTITY_TO_CLASS(kwSporeGun, AvHSpore); + +#ifdef AVH_SERVER +LINK_ENTITY_TO_CLASS(kwSporeProjectile, AvHSporeProjectile); +extern int gSporeCloudEventID; + +AvHSporeProjectile::AvHSporeProjectile() +{ + this->mTimeHit = -1; + this->mDamage = 0.0f; +} + +void AvHSporeProjectile::Precache(void) +{ + CBaseEntity::Precache(); + + PRECACHE_UNMODIFIED_MODEL(kSporeSprite); + PRECACHE_UNMODIFIED_MODEL(kClientSporeSprite); +} + +void AvHSporeProjectile::SetDamage(float inDamage) +{ + this->mDamage = inDamage; +} + +void AvHSporeProjectile::Spawn(void) +{ + this->Precache(); + CBaseEntity::Spawn(); + + this->pev->movetype = MOVETYPE_FLY; + this->pev->classname = MAKE_STRING(kwsSporeProjectile); + + SET_MODEL(ENT(this->pev), kClientSporeSprite); + this->pev->solid = SOLID_BBOX; + + if(!GetGameRules()->GetDrawInvisibleEntities()) + { + this->pev->effects = EF_NODRAW; + } + else + { + this->pev->frame = 0; + this->pev->scale = 0.5; + this->pev->rendermode = kRenderTransAlpha; + this->pev->renderamt = 255; + } + + UTIL_SetSize(this->pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + //UTIL_SetSize(this->pev, Vector( -50, -50, -50), Vector(50, 50, 50)); + + SetTouch(&AvHSporeProjectile::SporeTouch); +} + +void AvHSporeProjectile::SporeCloudThink() +{ + // Apply damage to all enemy players in radius + CBaseEntity* theEntity = NULL; + while ( (theEntity = UTIL_FindEntityInSphere( theEntity, this->pev->origin, BALANCE_VAR(kSporeCloudRadius))) != NULL ) + { + // Only hurt players + AvHPlayer* thePlayer = dynamic_cast(theEntity); + if(thePlayer) + { + ASSERT(this->pev->team != 0); + if(theEntity->pev->team != this->pev->team) + { + // Don't do damage to heavy armor + // : 1019 + // Spores can't damage commanders + if(!thePlayer->GetHasHeavyArmor() && !thePlayer->GetIsInTopDownMode() ) + { + // Spores don't stack, so don't do damage too often + float theTimeOfLastSporeDamage = thePlayer->GetTimeOfLastSporeDamage(); + float kTolerance = .05f; + if((theTimeOfLastSporeDamage == -1) || (gpGlobals->time > (theTimeOfLastSporeDamage + BALANCE_VAR(kSporeCloudThinkInterval) - kTolerance))) + { + // Make sure a direct line can be traced from spores to target + TraceResult theTraceResult; + UTIL_TraceLine(this->pev->origin, theEntity->pev->origin, ignore_monsters, dont_ignore_glass, theEntity->edict(), &theTraceResult); + + if(theTraceResult.flFraction == 1.0f) + { + theEntity->TakeDamage(this->pev, VARS(this->pev->owner), this->mDamage, NS_DMG_NORMAL); + + thePlayer->SetTimeOfLastSporeDamage(gpGlobals->time); + } + else + { + // What did we hit? + CBaseEntity* theEntity = CBaseEntity::Instance(theTraceResult.pHit); + } + } + } + + // TODO: Fire event? + } + } + } + + // Is it time to expire? + if(gpGlobals->time > (this->mTimeHit + BALANCE_VAR(kSporeCloudTime))) + { + // if so, remove entity + SetThink(&AvHSporeProjectile::SUB_Remove); + this->pev->nextthink = gpGlobals->time + 0.01f; + } + else + { + // if not, set next think + this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kSporeCloudThinkInterval); + } +} + +void AvHSporeProjectile::SporeTouch(CBaseEntity *pOther) +{ + // Never hit the player who fired it + if(pOther != CBaseEntity::Instance(this->pev->owner)) + { + // If not in tourny mode, pass through friends + //if(GetGameRules()->GetIsTournamentMode() || (pOther->pev->team != this->pev->team)) + //{ + // Tell the projectile to stop moving + VectorCopy(g_vecZero, this->pev->velocity); + + this->mTimeHit = gpGlobals->time; + SetThink(&AvHSporeProjectile::SporeCloudThink); + this->pev->nextthink = gpGlobals->time + BALANCE_VAR(kSporeCloudThinkInterval); + + SetTouch(NULL); + + // Fire spore cloud event + PLAYBACK_EVENT_FULL(0, this->edict(), gSporeCloudEventID, 0, pOther->pev->origin, (float *)&g_vecZero, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + //} + } +} +#endif + + + +int AvHSpore::GetBarrelLength() const +{ + return kSporeBarrelLength; +} + +float AvHSpore::GetRateOfFire() const +{ + return BALANCE_VAR(kSporeROF); +} + +bool AvHSpore::GetFiresUnderwater() const +{ + return true; +} + +bool AvHSpore::GetIsDroppable() const +{ + return false; +} + +void AvHSpore::Init() +{ + this->mRange = kSporeRange; + this->mDamage = BALANCE_VAR(kSporeDamage); +} + +int AvHSpore::GetDamageType() const +{ + return NS_DMG_NORMAL; +} + +int AvHSpore::GetIdleAnimation() const +{ + return 2; +} + +int AvHSpore::GetShootAnimation() const +{ + return 5; +} + +int AvHSpore::GetDeployAnimation() const +{ + // Look at most recently used weapon and see if we can transition from it + int theDeployAnimation = -1; + + AvHWeaponID thePreviousID = this->GetPreviousWeaponID(); + + switch(thePreviousID) + { + case AVH_WEAPON_BITE2: + theDeployAnimation = 9; + break; + case AVH_WEAPON_SPIKE: + theDeployAnimation = 8; + break; + } + + return theDeployAnimation; +} + +float AvHSpore::GetDeployTime() const +{ + return .6f; +} + +char* AvHSpore::GetViewModel() const +{ + return kLevel3ViewModel; +} + +void AvHSpore::Precache(void) +{ + AvHAlienWeapon::Precache(); + + PRECACHE_UNMODIFIED_SOUND(kSporeFireSound); + PRECACHE_UNMODIFIED_SOUND(kSporeCloudSound); + + this->mEvent = PRECACHE_EVENT(1, kSporeShootEventName); +} + +void AvHSpore::Spawn() +{ + AvHAlienWeapon::Spawn(); + + Precache(); + + this->m_iId = AVH_WEAPON_SPORES; + + // Set our class name + this->pev->classname = MAKE_STRING(kwsSporeGun); + + SET_MODEL(ENT(this->pev), kNullModel); + + FallInit();// get ready to fall down. + +} + +bool AvHSpore::UsesAmmo(void) const +{ + return false; +} + +void AvHSpore::FireProjectiles(void) +{ + #ifdef AVH_SERVER + // Make sure we have enough points to shoot this thing + AvHPlayer* thePlayer = dynamic_cast(this->m_pPlayer); + ASSERT(thePlayer); + + //if(thePlayer->GetResources() > kSporePointCost) + //{ + // // Decrement kSporePointCost points + // thePlayer->SetResources(thePlayer->GetResources() - kSporePointCost); + + // Create hidden projectile that creates a huge spore cloud where it hits + AvHSporeProjectile* theSpore = GetClassPtr((AvHSporeProjectile*)NULL ); + theSpore->Spawn(); + + UTIL_MakeVectors(this->m_pPlayer->pev->v_angle); + + Vector vecAiming = gpGlobals->v_forward; + Vector vecSrc = this->m_pPlayer->GetGunPosition( ) + vecAiming; + + UTIL_SetOrigin(theSpore->pev, vecSrc); + +// // This needs to be the same as in EV_SporeGun +// Vector theBaseVelocity; +// VectorScale(this->pev->velocity, kSporeParentVelocityScalar, theBaseVelocity); +// +// Vector theStartVelocity; +// VectorMA(theBaseVelocity, kSporeVelocity, vecAiming, theStartVelocity); +// +// VectorCopy(theStartVelocity, theSpore->pev->velocity); + + VectorScale(vecAiming, kShootCloudVelocity, theSpore->pev->velocity); + + // Set owner + theSpore->pev->owner = ENT(this->m_pPlayer->pev); + + // Set spore's team :) + theSpore->pev->team = this->m_pPlayer->pev->team; + + // Set the spore damage + float theDamage = this->mDamage*AvHPlayerUpgrade::GetAlienRangedDamageUpgrade(this->m_pPlayer->pev->iuser4); + theSpore->SetDamage(theDamage); + //} + #endif +} + diff --git a/main/source/mod/AvHSprites.h b/main/source/mod/AvHSprites.h index e4fed1b..bcacbd3 100644 --- a/main/source/mod/AvHSprites.h +++ b/main/source/mod/AvHSprites.h @@ -50,6 +50,7 @@ #define kJetpackSprite "jetpack" #define kAlienEnergySprite "a-energy" +#define kAlienCloakSprite "a-cloak" #define kAlienResourceSprite "a-resources" #define kCombatExperienceSprite "experience" @@ -92,6 +93,20 @@ #define kTeammateOrderSprite "sprites/query.spr" +#define kExperienceBarSprite "sprites/640experience.spr" +#define kProgressBarSprite "sprites/640progress.spr" + +#define PROGRESS_BAR_ALIEN 0 +#define PROGRESS_BAR_GESTATE PROGRESS_BAR_ALIEN +#define PROGRESS_BAR_DEVOUR PROGRESS_BAR_ALIEN + +#define PROGRESS_BAR_MARINE 2 +#define PROGRESS_BAR_RESEARCH PROGRESS_BAR_MARINE +#define PROGRESS_BAR_RECYCLE PROGRESS_BAR_MARINE +#define PROGRESS_BAR_WELD PROGRESS_BAR_MARINE + +#define PROGRESS_BAR_DEFAULT PROGRESS_BAR_MARINE + //#define kOverwatchAimSprite "sprites/overwatch-aim.spr" #define kMembraneSprite "sprites/membrane.spr" diff --git a/main/source/mod/AvHTeam.cpp b/main/source/mod/AvHTeam.cpp index 0f78850..232c04d 100644 --- a/main/source/mod/AvHTeam.cpp +++ b/main/source/mod/AvHTeam.cpp @@ -1,2821 +1,2783 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHTeam.cpp $ -// $Date: 2002/11/26 20:35:00 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHTeam.cpp,v $ -// Revision 1.55 2002/11/26 20:35:00 Flayra -// - Hurt fix -// -// Revision 1.54 2002/11/26 15:58:31 Flayra -// - When the alien team no longer has any growing or active hives, all aliens slowly take damage. This is done to speed up the end game and prevent hiding llamas. -// -// Revision 1.53 2002/11/22 23:26:59 Flayra -// - Don't play kill lingering aliens with cheats on (when testing) -// -// Revision 1.52 2002/11/22 21:15:37 Flayra -// - Remove owner of entities when player leaves the team -// - Do damage to aliens without hives -// - Fixed bug where a vote could affect a commander that logged in after a vote had been started. -// -// Revision 1.51 2002/11/15 23:31:26 Flayra -// - Added "ready" verification for tourny mode -// -// Revision 1.50 2002/11/12 02:29:33 Flayra -// - Added better standardized logging -// -// Revision 1.49 2002/11/06 03:08:56 Flayra -// - Undid +1 for resources, it's too drastic -// -// Revision 1.48 2002/11/06 02:23:49 Flayra -// - Removed faulty minimum trickle that was giving 1 extra resource point to both teams -// -// Revision 1.47 2002/11/06 01:39:04 Flayra -// - Show true resources going to team -// -// Revision 1.46 2002/11/03 04:52:55 Flayra -// - Hard-coded resources -// -// Revision 1.45 2002/10/24 21:43:29 Flayra -// - Alien easter eggs -// - Voting fix -// -// Revision 1.44 2002/10/19 21:28:38 Flayra -// - Debugging info for linux -// -// Revision 1.43 2002/10/19 21:09:56 Flayra -// - Debugging info for linux -// -// Revision 1.42 2002/10/19 20:56:06 Flayra -// - Debugging info for linux -// -// Revision 1.41 2002/10/19 20:19:26 Flayra -// - Debugging info for linux -// -// Revision 1.40 2002/10/19 20:04:14 Flayra -// - Debugging info for linux -// -// Revision 1.39 2002/10/18 22:23:04 Flayra -// - Added beginnings of alien easter eggs -// - Added "we need builders" alert -// -// Revision 1.38 2002/10/04 18:03:23 Flayra -// - Fixed bug where extra resources were sometimes being wasted on the alien team -// -// Revision 1.37 2002/10/03 19:09:55 Flayra -// - New resource model -// - Orders refactoring -// - Tech tree changes -// - Aliens always get initial points when joining -// - Play gamestart sound -// - New trait available trigger -// - Moved voting stuff to server variables -// - Slowed down hints -// -// Revision 1.36 2002/09/25 20:51:31 Flayra -// - Play commander-idle sounds less often -// -// Revision 1.35 2002/09/23 22:35:43 Flayra -// - Removed hive donation and put in new system for builders -// - Updated tech tree (jetpacks, heavy armor, moved phase) -// - Resource towers set as built on game start -// - Added tons of commander hints (low resource alerts, tell commander about pressing jump key, commander ejected, select troops and give orders, don't just sit there, etc.) -// -// Revision 1.34 2002/09/09 20:08:26 Flayra -// - Added commander voting -// - Added hive info -// - Changed how commander scoring works -// -// Revision 1.33 2002/08/31 18:01:03 Flayra -// - Work at VALVe -// -// Revision 1.32 2002/08/02 21:45:20 Flayra -// - Reworked alerts. -// -// Revision 1.31 2002/07/28 19:21:27 Flayra -// - Balance changes after/during RC4a -// -// Revision 1.30 2002/07/26 23:08:25 Flayra -// - Added numerical feedback -// -// Revision 1.29 2002/07/25 16:58:00 flayra -// - Linux changes -// -// Revision 1.28 2002/07/24 18:55:53 Flayra -// - Linux case sensitivity stuff -// -// Revision 1.27 2002/07/23 17:32:23 Flayra -// - Resource model overhaul (closed system, random initial points), tech tree changes (new marine upgrades), free resource tower at nearest func_resource -// -// Revision 1.26 2002/07/10 14:45:26 Flayra -// - Fixed victory condition bug (bug #171) -// -// Revision 1.25 2002/07/08 17:19:42 Flayra -// - Added handicapping, map validity checking, reinforcements happen independently of teams now -// -// Revision 1.24 2002/07/01 21:24:07 Flayra -// - Brought siege back, removed alpha male resource model -// -// Revision 1.23 2002/06/25 18:21:33 Flayra -// - New enabled/disabled system for alien weapons, better victory detection, updated tech tree (removed old junk, added armory upgrade), fixed bug where a commander that leaves the game hogged the station, update resources less often (optimization) -// -// Revision 1.22 2002/06/03 17:00:33 Flayra -// - Removed mines and siege temporarily while being worked on, removed old code, removed redundant hive class name -// -// Revision 1.21 2002/05/28 18:09:51 Flayra -// - Track number of webs for team, fix for premature victory condition (again), fixed bug where alien upgrades weren't being cleared between levels, causing eventual overflows for alien players after many games, recycling support, spawn in command center when game starts -// -// Revision 1.20 2002/05/23 02:32:57 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "dlls/cbase.h" -#include "mod/AvHTeam.h" -#include "mod/AvHPlayer.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHServerVariables.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "game_shared/teamconst.h" -#include "mod/AvHBuildable.h" -#include "mod/AvHServerUtil.h" -#include "mod/AvHSharedUtil.h" -#include "mod/AvHTitles.h" -#include "mod/AvHPlayerUpgrade.h" -#include "util/MathUtil.h" -#include "mod/AvHNetworkMessages.h" -#include "mod/AvHNexusServer.h" - -extern int gPhaseInEventID; -extern cvar_t avh_votecasttime; -extern cvar_t avh_votedowntime; -extern cvar_t avh_votepercentneeded; -extern cvar_t avh_minvotesneeded; -extern cvar_t avh_initialalienpoints; -extern cvar_t avh_eastereggchance; - -AvHTeam::AvHTeam(AvHTeamNumber inTeamNumber) -{ - this->Init(); - this->mTeamNumber = inTeamNumber; - this->mResearchManager.SetTeamNumber(inTeamNumber); -} - -void AvHTeam::Init() -{ - this->mTeamType = AVH_CLASS_TYPE_UNDEFINED; - this->mTeamName = kInvalidTeamName; - this->mTeamNumber = TEAM_IND; - this->mCommander = -1; - this->mCommanderWhenVoteStarted = -1; - - this->mPlayerList.clear(); - this->mResourceTowerList.clear(); - - this->mLastResourceUpdateTime = -1; - this->mLastCommandScoreUpdateTime = -1; - this->mLastServerPlayerDataUpdateTime = -1; - this->mLastPlayerUpdateTime = -1; - this->mLastInjectionTime = 0; - this->mLastHiveSpawnTime = 0; - - // Alerts - this->mAlertList.clear(); - this->mTimeOfLastTeamHint = -1; - this->mTimeLastHintUpdate = -1; - - this->mTeamResources = 0; - this->mHandicap = 1.0f; - - this->mResearchManager.Reset(); - - this->mAlienUpgrades.clear(); - this->mTeamWideUpgrades = 0; - - this->mNumWebStrands = 0; - this->mIsReady = false; - - this->mTotalResourcesGathered = 0; - this->mClientTotalResourcesGathered = 0; - - this->mMenuTechSlots = 0; - - this->mTimeReinforcementWaveComplete = -1; -} - -bool AvHTeam::AddPlayer(int inPlayerIndex) -{ - bool theSuccess = false; - - if(!this->GetIsPlayerOnTeam(inPlayerIndex)) - { - // Add player - this->mPlayerList.push_back(inPlayerIndex); - - theSuccess = true; - } - - return theSuccess; -} - -bool AvHTeam::AddResourceTower(int inResourceTowerIndex) -{ - bool theSuccess = false; - - EntityListType::const_iterator theIter = std::find(this->mResourceTowerList.begin(), this->mResourceTowerList .end(), inResourceTowerIndex); - if(theIter == this->mResourceTowerList.end()) - { - this->mResourceTowerList.push_back(inResourceTowerIndex); - theSuccess = true; - } - - return theSuccess; -} - -bool AvHTeam::RemoveResourceTower(int inResourceTowerIndex) -{ - bool theSuccess = false; - - EntityListType::iterator theIter = std::find(this->mResourceTowerList.begin(), this->mResourceTowerList .end(), inResourceTowerIndex); - if(theIter != this->mResourceTowerList.end()) - { - this->mResourceTowerList.erase(theIter); - theSuccess = true; - } - - return theSuccess; -} - -bool AvHTeam::GetIsReady() const -{ - return this->mIsReady; -} - -int AvHTeam::GetMenuTechSlots() const -{ - return this->mMenuTechSlots; -} - -void AvHTeam::SetIsReady(bool bIsReady) -{ - this->mIsReady = bIsReady; -} - -int AvHTeam::GetNumBuiltCommandStations() const -{ - return this->mNumBuiltCommandStations; -} - -int AvHTeam::GetNumActiveHives() const -{ - return this->mNumActiveHives; -} - -float AvHTeam::GetMaxResources(AvHUser3 inUser3) const -{ - float theMaxResources = -1; - - if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - //if(inUser3 == AVH_USER3_ALIEN_PLAYER2) - //{ - // Needed so builders can create hives (also means that having more builders diverts resources from combat/offense) - theMaxResources = kMaxAlienResources; - //} - //else - //{ - // int theNumActiveHives = max(this->GetNumActiveHives(), 1); - // theMaxResources = (theNumActiveHives/(float)kMaxAlienHives)*kMaxAlienResources; - //} - } - - return theMaxResources; -} - - -bool AvHTeam::GetTeamHasAbilityToRespawn() const -{ - bool theAbilityToRespawn = false; - - // If game hasn't started - if(!GetGameRules()->GetGameStarted()) - { - // return true - theAbilityToRespawn = true; - } - // else if we're a marine team - else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) - { - // Do we have any built infantry portals? - FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) - if(theEntity->GetIsBuilt()) - { - AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); - if(theTeamNumber == this->mTeamNumber) - { - theAbilityToRespawn = true; - break; - } - } - END_FOR_ALL_ENTITIES(kwsInfantryPortal); - } - // else if we're an alien team - else if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - // Do we have any active hives? - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - if(theEntity->GetIsActive()) - { - AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); - if(theTeamNumber == this->mTeamNumber) - { - theAbilityToRespawn = true; - break; - } - } - END_FOR_ALL_ENTITIES(kesTeamHive); - } - - return theAbilityToRespawn; -} - -bool AvHTeam::GetHasAtLeastOneActivePlayer(bool* outHasAtLeastOnePlayerOnTeam) const -{ - bool theHasAtLeastOneActivePlayer = false; - - // Loop through all players on team - for(EntityListType::const_iterator theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) - { - const AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); - ASSERT(thePlayer); - - if(outHasAtLeastOnePlayerOnTeam) - { - *outHasAtLeastOnePlayerOnTeam = true; - } - - if((thePlayer->IsAlive() && !thePlayer->GetIsBeingDigested()) || (thePlayer->GetUser3() == AVH_USER3_COMMANDER_PLAYER)) - { - theHasAtLeastOneActivePlayer = true; - break; - } - } - - return theHasAtLeastOneActivePlayer; -} - -bool AvHTeam::GetHasTeamLost() const -{ - bool theTeamHasLost = false; - - bool theHasAtLeastOnePlayerOnTeam = false; - bool theHasAtLeastOneActivePlayer = this->GetHasAtLeastOneActivePlayer(&theHasAtLeastOnePlayerOnTeam); - - if(GetGameRules()->GetIsCombatMode()) - { - if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - if((this->GetNumActiveHives() == 0) || !theHasAtLeastOnePlayerOnTeam) - { - theTeamHasLost = true; - } - } - else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) - { - if((this->GetNumBuiltCommandStations() == 0) || !theHasAtLeastOnePlayerOnTeam) - { - theTeamHasLost = true; - } - } - } - else - { - // We need at least one alive player, OR, at the ability to respawn - bool theHasAbilityToRespawn = this->GetTeamHasAbilityToRespawn(); - if((!theHasAtLeastOneActivePlayer && !theHasAbilityToRespawn) || !theHasAtLeastOnePlayerOnTeam) - { - theTeamHasLost = true; - } - } - - if(theTeamHasLost) - { - //UTIL_LogPrintf("Team %d has lost. theAtLeastOneAlivePlayer: %d, theAbilityToRespawn: %d, theHasAtLeastOnePlayerOnTeam: %d\n", this->mTeamNumber, theHasAtLeastOneAlivePlayer, theHasAbilityToRespawn, theHasAtLeastOnePlayerOnTeam); - UTIL_LogPrintf("Team %d has lost.\n", this->mTeamNumber); - - //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - // UTIL_LogPrintf(" Victory player info: ent: %d, team: %d, role: %d, playmode: %d\n", theEntity->entindex(), theEntity->pev->team, (int)theEntity->GetRole(), (int)theEntity->GetPlayMode()); - //END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - } - - return theTeamHasLost; -} - -HiveInfoListType AvHTeam::GetHiveInfoList() const -{ - return this->mHiveInfoList; -} - - -const AvHResearchManager& AvHTeam::GetResearchManager() const -{ - return this->mResearchManager; -} - -AvHResearchManager& AvHTeam::GetResearchManager() -{ - return this->mResearchManager; -} - -float AvHTeam::GetTotalResourcesGathered() const -{ - return this->mTotalResourcesGathered; -} - -void AvHTeam::AddResourcesGathered(float inResources) -{ - this->mTotalResourcesGathered += inResources; -} - -bool AvHTeam::GetIsPlayerOnTeam(int inPlayerIndex) const -{ - bool theSuccess = false; - - EntityListType::const_iterator theIter = std::find(this->mPlayerList.begin(), this->mPlayerList .end(), inPlayerIndex); - if(theIter != this->mPlayerList.end()) - { - theSuccess = true; - } - - return theSuccess; -} - -AvHPlayer* AvHTeam::GetPlayerFromIndex(int inPlayerIndex) -{ - AvHPlayer* thePlayer = NULL; - - CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex)); - thePlayer = dynamic_cast(theBaseEntity); - - return thePlayer; -} - -const AvHPlayer* AvHTeam::GetPlayerFromIndex(int inPlayerIndex) const -{ - const AvHPlayer* thePlayer = NULL; - const CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex)); - thePlayer = dynamic_cast(theBaseEntity); - - return thePlayer; -} - -void AvHTeam::GetOrders(OrderListType& outOrderList) const -{ - outOrderList = this->mOrderList; -} - -bool AvHTeam::GetDoesPlayerHaveOrder(int inPlayerIndex) -{ - bool thePlayerHasOrders = false; - - for(OrderListType::iterator theIter = this->mOrderList.begin(); theIter != this->mOrderList.end(); theIter++) - { - EntityInfo theReceiver = theIter->GetReceiver(); - if(theReceiver == inPlayerIndex) - { - thePlayerHasOrders = true; - } - } - - return thePlayerHasOrders; -} - -//void AvHTeam::GetPlayersCompletingOrders(const EntityListType& outPlayerList) const -//{ -// outPlayerList = this->mPlayersCompletingOrder; -//} - -void AvHTeam::SetOrder(const AvHOrder& inOrder) -{ - AvHChangeOrder(this->mOrderList, inOrder); -} - -void AvHTeam::AddTechNode(AvHMessageID inMessageID, AvHTechID inTechID, AvHTechID inPrereq1, AvHTechID inPrereq2, bool inAllowMultiples, bool inResearched) -{ - int theCost = GetGameRules()->GetCostForMessageID(inMessageID); - int theBuildTime = GetGameRules()->GetBuildTimeForMessageID(inMessageID); - - this->mResearchManager.AddTechNode(inMessageID, inTechID, inPrereq1, inPrereq2, theCost, theBuildTime, inResearched, inAllowMultiples); -} - -void AvHTeam::InitializeTechNodes() -{ - // Clear them first - this->mResearchManager.Reset(); - //this->mUpgradeCosts.clear(); - this->mTechSlotManager.Clear(); - - // Depending on game mode, set tech tree - - if(GetGameRules()->GetIsCombatMode()) - { - this->InitializeCombatTechNodes(); - } - else - { - // else regular NS - if(this->mTeamType == AVH_CLASS_TYPE_MARINE) - { - this->AddTechNode(MESSAGE_NULL, TECH_COMMAND_CENTER, TECH_NULL, TECH_NULL, true, true); - this->AddTechNode(BUILD_RECYCLE, TECH_NULL, TECH_NULL); - this->AddTechNode(BUILD_COMMANDSTATION, TECH_NULL, TECH_NULL); - - this->AddTechNode(BUILD_INFANTRYPORTAL, TECH_INFANTRYPORTAL, TECH_COMMAND_CENTER); - //this->AddTechNode(RESEARCH_FASTER_REINFORCEMENTS, TECH_RESEARCH_FASTER_REINFORCEMENTS, TECH_INFANTRYPORTAL, TECH_OBSERVATORY, false, false); - - this->AddTechNode(MESSAGE_CANCEL, TECH_NULL, TECH_NULL, TECH_NULL, true); - - this->AddTechNode(BUILD_AMMO, TECH_NULL, TECH_NULL); - this->AddTechNode(BUILD_HEALTH, TECH_NULL, TECH_NULL); - this->AddTechNode(BUILD_CAT, TECH_NULL, TECH_RESEARCH_CATALYSTS, TECH_NULL, true, false); - this->AddTechNode(BUILD_RESOURCES, TECH_COMMAND_CENTER, TECH_NULL, TECH_NULL, true, false); - this->AddTechNode(BUILD_HEALTH, TECH_COMMAND_CENTER, TECH_NULL); - - // CC - //this->AddTechNode(RESEARCH_HEALTH, TECH_RESEARCH_HEALTH, TECH_COMMAND_CENTER, TECH_NULL, false); - - // Turret factory route - this->AddTechNode(BUILD_TURRET_FACTORY, TECH_TURRET_FACTORY); - this->AddTechNode(BUILD_TURRET, TECH_NULL, TECH_TURRET_FACTORY); - this->AddTechNode(RESEARCH_ELECTRICAL, TECH_RESEARCH_ELECTRICAL, TECH_TURRET_FACTORY, TECH_NULL, true); - - this->AddTechNode(TURRET_FACTORY_UPGRADE, TECH_ADVANCED_TURRET_FACTORY, TECH_TURRET_FACTORY, TECH_NULL, true); - this->AddTechNode(BUILD_SIEGE, TECH_NULL, TECH_ADVANCED_TURRET_FACTORY); - - // Arms lab - this->AddTechNode(BUILD_ARMSLAB, TECH_ARMSLAB, TECH_ARMORY); - - this->AddTechNode(RESEARCH_ARMOR_ONE, TECH_RESEARCH_ARMOR_ONE, TECH_ARMSLAB, TECH_NULL, false); - this->AddTechNode(RESEARCH_ARMOR_TWO, TECH_RESEARCH_ARMOR_TWO, TECH_RESEARCH_ARMOR_ONE, TECH_NULL, false); - this->AddTechNode(RESEARCH_ARMOR_THREE, TECH_RESEARCH_ARMOR_THREE, TECH_RESEARCH_ARMOR_TWO, TECH_NULL, false); - - this->AddTechNode(RESEARCH_WEAPONS_ONE, TECH_RESEARCH_WEAPONS_ONE, TECH_ARMSLAB, TECH_NULL, false); - this->AddTechNode(RESEARCH_WEAPONS_TWO, TECH_RESEARCH_WEAPONS_TWO, TECH_RESEARCH_WEAPONS_ONE, TECH_NULL, false); - this->AddTechNode(RESEARCH_WEAPONS_THREE, TECH_RESEARCH_WEAPONS_THREE, TECH_RESEARCH_WEAPONS_TWO, TECH_NULL, false); - this->AddTechNode(RESEARCH_CATALYSTS, TECH_RESEARCH_CATALYSTS, TECH_ARMSLAB, TECH_NULL, false, false); - - this->AddTechNode(RESEARCH_HEAVYARMOR, TECH_RESEARCH_HEAVYARMOR, TECH_PROTOTYPE_LAB, TECH_NULL, false); - - // Prototype lab - this->AddTechNode(BUILD_PROTOTYPE_LAB, TECH_PROTOTYPE_LAB, TECH_ARMSLAB, TECH_ADVANCED_ARMORY); - this->AddTechNode(RESEARCH_JETPACKS, TECH_RESEARCH_JETPACKS, TECH_PROTOTYPE_LAB, TECH_NULL, false, false); - - // Armor add-ons - this->AddTechNode(BUILD_JETPACK, TECH_NULL, TECH_RESEARCH_JETPACKS, TECH_PROTOTYPE_LAB); - this->AddTechNode(BUILD_HEAVY, TECH_NULL, TECH_RESEARCH_HEAVYARMOR, TECH_PROTOTYPE_LAB); - - // Weapon factory route - this->AddTechNode(BUILD_ARMORY, TECH_ARMORY); - this->AddTechNode(ARMORY_UPGRADE, TECH_ADVANCED_ARMORY, TECH_ARMORY); - this->AddTechNode(RESEARCH_GRENADES, TECH_RESEARCH_GRENADES, TECH_ARMORY, TECH_NULL, false, false); - //this->AddTechNode(BUILD_NUKE_PLANT, TECH_NUKE_PLANT, TECH_ADVANCED_ARMORY); - - // Observatory/knowledge/phase gate route - this->AddTechNode(BUILD_OBSERVATORY, TECH_OBSERVATORY, TECH_ARMORY); - this->AddTechNode(BUILD_SCAN, TECH_NULL, TECH_OBSERVATORY); - this->AddTechNode(RESEARCH_MOTIONTRACK, TECH_RESEARCH_MOTIONTRACK, TECH_OBSERVATORY, TECH_NULL, false); - this->AddTechNode(RESEARCH_DISTRESSBEACON, TECH_RESEARCH_DISTRESSBEACON, TECH_OBSERVATORY, TECH_NULL); - this->AddTechNode(RESEARCH_PHASETECH, TECH_RESEARCH_PHASETECH, TECH_INFANTRYPORTAL, TECH_OBSERVATORY, false); - this->AddTechNode(BUILD_PHASEGATE, TECH_NULL, TECH_OBSERVATORY, TECH_RESEARCH_PHASETECH); - - // Weapons - this->AddTechNode(BUILD_SHOTGUN, TECH_NULL, TECH_ARMORY); - this->AddTechNode(BUILD_WELDER, TECH_NULL, TECH_ARMORY); - this->AddTechNode(BUILD_MINES, TECH_NULL, TECH_ARMORY); - - this->AddTechNode(BUILD_HMG, TECH_NULL, TECH_ADVANCED_ARMORY); - this->AddTechNode(BUILD_GRENADE_GUN, TECH_NULL, TECH_ADVANCED_ARMORY, TECH_NULL); - //this->AddTechNode(BUILD_NUKE, TECH_NULL, TECH_NUKE_PLANT, TECH_ADVANCED_ARMORY); - - // Specials - //this->AddTechNode(BUILD_MEDKIT, TECH_NULL, TECH_MEDLAB); - - // Add tech to allow resource adjustment (allow it to be researched multiple times) - //this->AddTechNode(RESOURCE_UPGRADE, TECH_NULL, TECH_NULL, TECH_NULL); - - // Initialize tech slots for marines - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_COMMANDER_STATION, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_TURRET_FACTORY, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, TURRET_FACTORY_UPGRADE, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ADVANCED_TURRET_FACTORY, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ARMORY, ARMORY_UPGRADE, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_GRENADES, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ADVANCED_ARMORY, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_GRENADES, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ARMSLAB, RESEARCH_WEAPONS_ONE, RESEARCH_WEAPONS_TWO, RESEARCH_WEAPONS_THREE, RESEARCH_CATALYSTS, RESEARCH_ARMOR_ONE, RESEARCH_ARMOR_TWO, RESEARCH_ARMOR_THREE, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_PROTOTYPE_LAB, RESEARCH_JETPACKS, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_HEAVYARMOR, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_OBSERVATORY, BUILD_SCAN, RESEARCH_PHASETECH, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_DISTRESSBEACON, RESEARCH_MOTIONTRACK, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_TURRET, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_SIEGETURRET, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_RESTOWER, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_INFANTRYPORTAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_PHASEGATE, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MARINE_PLAYER)); - - // Initialize tech slots for top-level menus. Note that the data for these four menus is stored in the command station iuser1 - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_BUILD, BUILD_RESOURCES, BUILD_INFANTRYPORTAL, BUILD_ARMORY, BUILD_COMMANDSTATION, BUILD_TURRET_FACTORY, BUILD_TURRET, BUILD_SIEGE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_BUILD_ADVANCED, BUILD_OBSERVATORY, BUILD_ARMSLAB, BUILD_PROTOTYPE_LAB, BUILD_PHASEGATE)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_ASSIST, BUILD_AMMO, BUILD_HEALTH, BUILD_CAT)); - this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_EQUIP, BUILD_MINES, BUILD_SHOTGUN, BUILD_HMG, BUILD_GRENADE_GUN, BUILD_WELDER, BUILD_JETPACK, BUILD_HEAVY)); - //this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_ORDERS, BUILD_HEALTH)); - } - else - { - // These upgrades are per alien - this->AddTechNode(ALIEN_LIFEFORM_ONE, TECH_SKULK, TECH_NULL, TECH_NULL, true, true); - this->AddTechNode(ALIEN_LIFEFORM_TWO, TECH_GORGE, TECH_NULL, TECH_NULL, true, true); - this->AddTechNode(ALIEN_LIFEFORM_THREE, TECH_LERK, TECH_SKULK, TECH_NULL, true, true); - this->AddTechNode(ALIEN_LIFEFORM_FOUR, TECH_FADE, TECH_LERK, TECH_NULL, true, true); - this->AddTechNode(ALIEN_LIFEFORM_FIVE, TECH_ONOS, TECH_FADE, TECH_NULL, true, true); - - // Only gorge can build - this->AddTechNode(ALIEN_BUILD_RESOURCES, TECH_ALIEN_RESOURCE_NODE, TECH_GORGE, TECH_NULL, true, false); - this->AddTechNode(ALIEN_BUILD_HIVE, TECH_HIVE, TECH_GORGE, TECH_NULL, true, false); - this->AddTechNode(ALIEN_BUILD_MOVEMENT_CHAMBER, TECH_MOVEMENT_CHAMBER, TECH_GORGE, TECH_NULL, true, false); - this->AddTechNode(ALIEN_BUILD_DEFENSE_CHAMBER, TECH_DEFENSE_CHAMBER, TECH_GORGE, TECH_NULL, true, false); - this->AddTechNode(ALIEN_BUILD_SENSORY_CHAMBER, TECH_SENSORY_CHAMBER, TECH_GORGE, TECH_NULL, true, false); - this->AddTechNode(ALIEN_BUILD_OFFENSE_CHAMBER, TECH_OFFENSE_CHAMBER, TECH_GORGE, TECH_NULL, true, false); - - // Upgrades - this->AddTechNode(ALIEN_EVOLUTION_ONE, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); - this->AddTechNode(ALIEN_EVOLUTION_TWO, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); - this->AddTechNode(ALIEN_EVOLUTION_THREE, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); - - this->AddTechNode(ALIEN_EVOLUTION_SEVEN, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); - this->AddTechNode(ALIEN_EVOLUTION_EIGHT, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); - this->AddTechNode(ALIEN_EVOLUTION_NINE, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); - - this->AddTechNode(ALIEN_EVOLUTION_TEN, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); - this->AddTechNode(ALIEN_EVOLUTION_ELEVEN, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); - this->AddTechNode(ALIEN_EVOLUTION_TWELVE, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); - } - } -} - -const AvHTechTree& AvHTeam::GetTechNodes() const -{ - return this->mResearchManager.GetTechNodes(); -} - -AvHTechTree& AvHTeam::GetTechNodes() -{ - return this->mResearchManager.GetTechNodes(); -} - -const AvHTechSlotManager& AvHTeam::GetTechSlotManager() const -{ - return this->mTechSlotManager; -} - -AvHTechSlotManager& AvHTeam::GetTechSlotManager() -{ - return this->mTechSlotManager; -} - -// For marine teams, this means a player is trying to vote down the commander -bool AvHTeam::PlayerVote(int inPlayerIndex, string& outPlayerMessage) -{ - bool theSuccess = false; - - // If we are a marine team and we have a commander - if((this->GetTeamType() == AVH_CLASS_TYPE_MARINE) && (this->GetCommander() != -1) && GetGameRules()->GetGameStarted()) - { - // If player is allowed to vote (can only vote every x minutes) - EntityListType::iterator theIterator = std::find(this->mVotingPlayers.begin(), this->mVotingPlayers.end(), inPlayerIndex); - if(theIterator == this->mVotingPlayers.end()) - { - AvHPlayer* theVotingPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex))); - if(theVotingPlayer) - { - theVotingPlayer->SendMessage(kVoteCast); - - // Increment vote - this->mVotes++; - this->mVotingPlayers.push_back(inPlayerIndex); - theSuccess = true; - - AvHPlayer* theCommander = GetCommanderPlayer(); - if(theCommander) - { - theCommander->LogPlayerActionPlayer(theVotingPlayer, "votedown"); - } - } - } - else - { - // Display message that player has already voted - outPlayerMessage = kAlreadyVoted; - } - } - else - { - outPlayerMessage = kVoteIllegal; - } - - return theSuccess; -} - -void AvHTeam::PlayFunHUDSoundOnce(AvHHUDSound inHUDSound) -{ - if(std::find(this->mFunSoundsPlayed.begin(), this->mFunSoundsPlayed.end(), inHUDSound) == this->mFunSoundsPlayed.end()) - { - this->PlayHUDSoundForAlivePlayers(inHUDSound); - this->mFunSoundsPlayed.push_back(inHUDSound); - } -} - -void AvHTeam::PlayHUDSoundForAlivePlayers(AvHHUDSound inHUDSound) const -{ - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetIsRelevant() && (theEntity->pev->team == this->mTeamNumber)) - { - theEntity->PlayHUDSound(inHUDSound); - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); -} - -// joev: Bug 0000767 -void AvHTeam::PlayHUDSoundForAllPlayers(AvHHUDSound inHUDSound) const -{ - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - theEntity->PlayHUDSound(inHUDSound); - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); -} -// :joev - - -// Assumes rank change has been approved from on high. It just does the best job it can, -// it doesn't change any other ranks than the one indicated -bool AvHTeam::ProcessRankChange(AvHPlayer* inPlayer, AvHUser3 inOldUser3, AvHUser3 inNewUser3) -{ - bool theSuccess = false; - - // Someone just stepped up or down, change the player's squad - int thePlayerIndex = inPlayer->entindex(); - - // if player is now a soldier - if(inNewUser3 == AVH_USER3_MARINE_PLAYER) - { - if(this->GetCommander() == thePlayerIndex) - { - this->SetCommander(-1); - } - } - // else if player is now commander - else if(inNewUser3 == AVH_USER3_COMMANDER_PLAYER) - { - // Set him as commander - this->SetCommander(thePlayerIndex); - - theSuccess = true; - } - - return theSuccess; -} - -void AvHTeam::ResetGame() -{ - AvHTeamNumber theTeamNumber = this->mTeamNumber; - AvHClassType theTeamType = this->mTeamType; - - this->Init(); - - this->mTeamNumber = theTeamNumber; - this->mTeamType = theTeamType; - - this->mLastInjectionTime = gpGlobals->time; - this->mLastResourceUpdateTime = -1; - this->mLastCommandScoreUpdateTime = -1; - this->mStartingLocation.x = this->mStartingLocation.y = this->mStartingLocation.z = 0; - this->mNumBuiltCommandStations = 0; - this->mNumActiveHives = 0; - - if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - // Spawn the initial hives - int theNumHives = GetGameRules()->GetGameplay().GetInitialHives(); - for(int i = 0; i < theNumHives; i++) - { - this->SpawnHive(&this->mStartingLocation, true); - } - - if(GetGameRules()->GetIsCombatMode()) - { - for(int i= 0; i < 3; i++) - { - this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_DEFENSE); - this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_MOVEMENT); - this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_SENSORY); - } - } - } - - if(!GetGameRules()->GetIsCombatMode()) - { - // Put down command station - FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) - if(theEntity->pev->team == (int)(this->mTeamNumber)) - { - theEntity->SetConstructionComplete(true); - DROP_TO_FLOOR(ENT(theEntity->pev)); - //PLAYBACK_EVENT_FULL(0, theEntity->edict(), gPhaseInEventID, 0, theEntity->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - VectorCopy(theEntity->pev->origin, this->mStartingLocation); - } - END_FOR_ALL_ENTITIES(kwsTeamCommand) - - if(this->mTeamType == AVH_CLASS_TYPE_MARINE) - { - this->SetTeamResources(GetGameRules()->GetGameplay().GetInitialMarinePoints()); - } - // else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - // { - // this->SetTeamResources(GetGameRules()->GetGameplay().GetInitialAlienPoints()); - // } - - this->SpawnResourceTower(); - } - - this->mOrderList.clear(); - - this->ResetServerPlayerData(); - //this->InitializeRandomResourceShares(); - - this->mHiveInfoList.clear(); - this->mTimeOfLastStructureUpdate = -1; - - this->mVoteStarted = false; - this->mPreviousVotes = this->mVotes = 0; - this->mVotingPlayers.clear(); - this->mTimeVoteStarted = -1; - this->mVotesNeeded = 0; - - - this->mLowResourceAlerts.clear(); - this->mCommandersToldAboutJumpKey.clear(); - this->mFunSoundsPlayed.clear(); - this->mPlayedSeeDeadPeople = false; - - int theEasterEggChance = max(0.0f, avh_eastereggchance.value); - this->mPlayFunSoundsThisGame = (RANDOM_LONG(0, theEasterEggChance) == 0); - - // Don't give hints right away - this->mTimeOfLastTeamHint = gpGlobals->time; - this->mTimeLastHintUpdate = -1; - - // Reset group info - for(int i = 0; i < kNumHotkeyGroups; i++) - { - this->mGroups[i].clear(); - this->mGroupTypes[i] = AVH_USER3_NONE; - } - this->mSelectAllGroup.clear(); - - this->mObjectiveManager.Clear(); -} - -void AvHTeam::SpawnResourceTower() -{ - string theResourceEntity; - AvHMessageID theResourceEntityMessageID = MESSAGE_NULL; - - if(this->mTeamType == AVH_CLASS_TYPE_MARINE) - { - theResourceEntity = kwsResourceTower; - theResourceEntityMessageID = BUILD_RESOURCES; - } - else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - theResourceEntity = kwsAlienResourceTower; - theResourceEntityMessageID = ALIEN_BUILD_RESOURCES; - } - - // Create a resource tower for the team, nearest their spawn - Vector theLocationToBuild; - - // Look for func resource nearest to team start - CBaseEntity* theNearestFuncResource = NULL; - float theNearestDistance = -1; - - FOR_ALL_ENTITIES(kesFuncResource, AvHFuncResource*) - if(!theEntity->GetIsOccupied()) - { - float theDistance = VectorDistance(this->mStartingLocation, theEntity->pev->origin); - if((theNearestDistance == -1) || (theDistance < theNearestDistance)) - { - theNearestFuncResource = theEntity; - theNearestDistance = theDistance; - } - } - END_FOR_ALL_ENTITIES(kesFuncResource); - - if(theNearestFuncResource) - { - Vector theLocation = theNearestFuncResource->pev->origin; - theLocation.z += 35; - - CBaseEntity* theBaseEntity = CBaseEntity::Create(theResourceEntity.c_str(), theLocation, AvHSUGetRandomBuildingAngles()); - - theBaseEntity->pev->team = this->mTeamNumber; - - AvHSUBuildingJustCreated(theResourceEntityMessageID, theBaseEntity, NULL); - - // Set it complete immediately. If not, aliens are slowed down noticeably because they need builders to tower building - AvHResourceTower* theResourceTower = dynamic_cast(theBaseEntity); - ASSERT(theResourceTower); - // The first resource tower is active immediately - theResourceTower->SetActivateTime(0); - - //DROP_TO_FLOOR(ENT(theBaseBuildable->pev)); - theResourceTower->SetConstructionComplete(true); - } -} - -// Delete any server player data that has expired -void AvHTeam::ResetServerPlayerData() -{ - // Run through list - for(AvHServerPlayerDataListType::iterator theIter = this->mServerPlayerData.begin(); theIter != this->mServerPlayerData.end(); /* no inc */) - { - // Delete it unless the player has been voted down - if(theIter->second.GetTimeVotedDown() == -1) - { - AvHServerPlayerDataListType::iterator theTempIter = theIter; - theTempIter++; - this->mServerPlayerData.erase(theIter); - theIter = theTempIter; - } - else - { - theIter++; - } - } -} - -int AvHTeam::GetDesiredSpawnCount() const -{ - int theDesiredSpawns = 0; - - if(this->mTeamType == AVH_CLASS_TYPE_MARINE) - { - // 16 for the marine start - theDesiredSpawns = 16; - } - else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - // 16 for each hive - theDesiredSpawns = 16*3; - } - - return theDesiredSpawns; -} - -float AvHTeam::GetInitialPlayerPoints(edict_t* inEdict) -{ - float theInitialPoints = 0; - - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(inEdict)); - ASSERT(thePlayer); - - // Marines don't get any individual points, aliens get random amount - // Use WONID to make sure player doesn't try to get more points by quitting and rejoining - if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - if(GetGameRules()->GetArePlayersAllowedToJoinImmediately() /*&& !thePlayer->GetHasLeftReadyRoom()*/) - { - theInitialPoints = GetGameRules()->GetGameplay().GetInitialAlienPoints(); - } - - // If WON id already exists (because we already gave points out to that player) - AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(inEdict); - if(theServerPlayerData && (theServerPlayerData->GetResources() != -1)) - { - // Give them the number of points they last had - theInitialPoints = theServerPlayerData->GetResources(); - } - } - - return theInitialPoints; -} - -float AvHTeam::GetInitialExperience(edict_t* inEdict) -{ - float theInitialExperience = 0.0f; - - //AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(inEdict); - //if(theServerPlayerData && (theServerPlayerData->GetExperience() > 0.0f)) - //{ - // // Give them experience they last had - // theInitialExperience = theServerPlayerData->GetExperience(); - //} - - return theInitialExperience; -} - -void AvHTeam::SetGameStarted(bool inGameStarted) -{ - if(inGameStarted) - { - if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - this->mLastHiveSpawnTime = gpGlobals->time; - } - this->mLastInjectionTime = gpGlobals->time; - - //#ifndef DEBUG - // If team is alien, give them all game started hud sound - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->pev->team == this->mTeamNumber) - { - theEntity->PlayHUDSound(HUD_SOUND_GAMESTART); - - // Send Combat objective - if(GetGameRules()->GetIsCombatMode()) - { - string theMessage; - if(GetGameRules()->GetCombatAttackingTeamNumber() == this->mTeamNumber) - { - theMessage = kYouAreAttacking; - - } - else - { - theMessage = kYouAreDefending; - } - - theEntity->SendMessage(theMessage.c_str(), NORMAL); - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - //#endif - } -} - -void AvHTeam::KillCS() -{ - AvHCommandStation* theCS = NULL; - - FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) - if(theEntity->pev->health > 0) - { - theEntity->TakeDamage(theEntity->pev, theEntity->pev, 20000, DMG_GENERIC); - } - END_FOR_ALL_ENTITIES(kwsTeamCommand) -} - -void AvHTeam::KillHive() -{ - if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - AvHHive* theHive = AvHSUGetRandomActiveHive(this->mTeamNumber); - if(theHive) - { - theHive->Killed(NULL, 0); - } - } -} - -void AvHTeam::SpawnHive(Vector* outLocation, bool inForce) -{ - if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - AvHHive* theHive = AvHSUGetRandomActivateableHive(this->mTeamNumber); - if(theHive) - { - if(theHive->StartSpawningForTeam(this->mTeamNumber, inForce)) - { - theHive->SetConstructionComplete(true); - this->mNumActiveHives++; - - if(outLocation) - { - VectorCopy(theHive->pev->origin, *outLocation); - } - } - else - { - ALERT(at_console, "Error spawning hive. Is it blocked by something?\n"); - } - } - } -} - -int AvHTeam::GetCommander() const -{ - return this->mCommander; -} - -AvHPlayer* AvHTeam::GetCommanderPlayer() -{ - AvHPlayer* theCommanderPlayer = NULL; - - if(this->mCommander > 0) - { - theCommanderPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCommander))); - } - - return theCommanderPlayer; -} - - -AvHTeamNumber AvHTeam::GetTeamNumber(void) const -{ - return this->mTeamNumber; -} - -void AvHTeam::SetTeamNumber(AvHTeamNumber inTeamNumber) -{ - this->mTeamNumber = inTeamNumber; -} - -AvHClassType AvHTeam::GetTeamType(void) const -{ - return this->mTeamType; -} - -const char* AvHTeam::GetTeamTypeString(void) const -{ - const char* theTeamString = "Unknown"; - - switch(this->mTeamType) - { - case AVH_CLASS_TYPE_MARINE: - theTeamString = "marine"; - break; - case AVH_CLASS_TYPE_ALIEN: - theTeamString = "alien"; - break; - } - - return theTeamString; -} - -void AvHTeam::AddTeamUpgrade(AvHAlienUpgradeCategory inCategory) -{ - int theNumUpgradeCategories = AvHGetNumUpgradesInCategoryInList(this->mAlienUpgrades, inCategory); - - // If we don't have an upgrade of this type, trigger an alert - if(theNumUpgradeCategories == 0) - { - GetGameRules()->TriggerAlert(this->mTeamNumber, ALERT_NEW_TRAIT, -1); - } - - this->mAlienUpgrades.push_back(inCategory); -} - -AvHAlienUpgradeListType AvHTeam::GetAlienUpgrades() const -{ - return this->mAlienUpgrades; -} - -bool AvHTeam::RemoveAlienUpgradeCategory(AvHAlienUpgradeCategory inCategory) -{ - bool theRemoved = AvHRemoveUpgradeCategory(inCategory, this->mAlienUpgrades); - return theRemoved; -} - -int AvHTeam::GetTeamWideUpgrades() const -{ - return this->mTeamWideUpgrades; -} - -void AvHTeam::ProcessTeamUpgrade(AvHMessageID inMessageID, bool inGive) -{ - ProcessGenericUpgrade(this->mTeamWideUpgrades, inMessageID, inGive); -} - -void AvHTeam::InitializeRandomResourceShares() -{ - this->mRandomResourceShares.clear(); - this->mTotalShareWeight = 0; - - // Populate random resource shares with random value (like cards in Bridge) - for(int i = 0; i < kTotalShares; i++) - { - const int kSlope = 2; - int theBoundaryOne = kTotalShares/3; - int theBoundaryTwo = (2*kTotalShares)/3; - int theCardValue = 1; - - if((i > theBoundaryOne) && (i < theBoundaryTwo)) - { - theCardValue = 2*kSlope; - } - else if(i > theBoundaryTwo) - { - theCardValue = 3*kSlope; - } - - this->mRandomResourceShares.push_back(theCardValue); - this->mTotalShareWeight += theCardValue; - } -} - -float AvHTeam::WithdrawPointsViaRandomShares(int inPlayerIndex) -{ - int theNumSharesLeft = this->mRandomResourceShares.size(); - ASSERT(theNumSharesLeft >= kNumSharesPerPlayer); - - // Pick some random shares and add them up - int theRandomShares = 0; - - for(int i = 0; i < kNumSharesPerPlayer; i++) - { - // Pick a random "card", add it's shares, remove card from pool - int theRandomNumber = g_engfuncs.pfnRandomLong(0, this->mRandomResourceShares.size() - 1); - RandomResourceShareListType::iterator theIter = this->mRandomResourceShares.begin() + theRandomNumber; - - theRandomShares = theRandomShares + *theIter; - - // Remember which "cards" we've given this player so we can add them back in later - this->mPlayerShares[inPlayerIndex].push_back(*theIter); - - this->mRandomResourceShares.erase(theIter); - } - - // Return this percentage of the team resources for this player - float thePercentage = theRandomShares/(float)this->mTotalShareWeight; - - float theInitialPoints = thePercentage*this->GetTeamResources(); - this->SetTeamResources(this->GetTeamResources() - theInitialPoints); - - return theInitialPoints; -} - -void AvHTeam::SetTeamType(AvHClassType inType) -{ - this->mTeamType = inType; - - switch(inType) - { - case AVH_CLASS_TYPE_MARINE: - if( this->GetTeamNumber() == TEAM_ONE ) - { this->mTeamName = kMarine1Team; } - else - { this->mTeamName = kMarine2Team; } - this->mTeamPrettyName = kMarinePrettyName; - break; - case AVH_CLASS_TYPE_ALIEN: - if( this->GetTeamNumber() == TEAM_TWO ) - { this->mTeamName = kAlien1Team; } - else - { this->mTeamName = kAlien2Team; } - this->mTeamPrettyName = kAlienPrettyName; - break; - default: - this->mTeamName = kUndefinedTeam; - this->mTeamPrettyName = kUndefPrettyName; - break; - } - - // Make sure team names aren't longer than MAX_TEAM_NAME - ASSERT(this->mTeamName.length() < MAX_TEAM_NAME); -} - -int AvHTeam::GetNumWebStrands() const -{ - return this->mNumWebStrands; -} - -void AvHTeam::SetNumWebStrands(int inNumWebStrands) -{ - this->mNumWebStrands = inNumWebStrands; -} - -float AvHTeam::GetTeamResources() const -{ - return this->mTeamResources; -} - -void AvHTeam::SetTeamResources(float inTeamResources) -{ - this->mTeamResources = inTeamResources; -} - -float AvHTeam::GetHandicap() const -{ - return this->mHandicap; -} - -void AvHTeam::SetHandicap(float inHandicap) -{ - this->mHandicap = inHandicap; -} - -const char* AvHTeam::GetTeamName(void) const -{ - return this->mTeamName.c_str(); -} - -const char* AvHTeam::GetTeamPrettyName(void) const -{ - return this->mTeamPrettyName.c_str(); -} - -void AvHTeam::SetCommander(int inPlayerNumber) -{ - if(inPlayerNumber != this->mCommander) - { - // Reset commander hints - this->mTimeOfLastTeamHint = -1; - - this->mCommander = inPlayerNumber; - } -} - -void AvHTeam::SetTeamName(const char* inTeamName) -{ - this->mTeamName = inTeamName; -} - -void AvHTeam::SetTeamPrettyName(const char* inTeamName) -{ - this->mTeamPrettyName = inTeamName; -} - -int AvHTeam::GetPlayerCount(bool inCountOnlyDead) const -{ - int theNumPlayers = this->mPlayerList.size(); - - if(inCountOnlyDead) - { - theNumPlayers = 0; - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if((AvHTeamNumber)theEntity->pev->team == this->mTeamNumber) - { - AvHPlayer* thePlayer = dynamic_cast(theEntity); - ASSERT(thePlayer); - - if(!thePlayer->IsAlive()) - { - theNumPlayers++; - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - } - - return theNumPlayers; -} - -bool AvHTeam::RemovePlayer(int inPlayerIndex) -{ - bool theSuccess = false; - - if(this->GetIsPlayerOnTeam(inPlayerIndex)) - { - // Remove player - EntityListType::iterator thePlayerIter = std::find(this->mPlayerList.begin(), this->mPlayerList .end(), inPlayerIndex); - if(thePlayerIter != this->mPlayerList.end()) - { - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex))); - ASSERT(thePlayer); - - // Add cards back into pool -// RandomResourceShareListType& theVector = this->mPlayerShares[inPlayerIndex]; -// for(RandomResourceShareListType::iterator theCardIter = theVector.begin(); theCardIter != theVector.end(); theCardIter++) -// { -// this->mRandomResourceShares.push_back(*theCardIter); -// } - - // Make sure no entities have this player as an owner - edict_t* thePlayerEdict = thePlayer->edict(); - FOR_ALL_BASEENTITIES(); - if(theBaseEntity->pev->owner == thePlayerEdict) - { - theBaseEntity->pev->owner = NULL; - } - - AvHBuildable* theBuildable = dynamic_cast(theBaseEntity); - if(theBuildable && (theBuildable->GetBuilder() == thePlayer->entindex())) - { - theBuildable->SetBuilder(-1); - } - END_FOR_ALL_BASEENTITIES(); - - AvHSURemoveEntityFromHotgroupsAndSelection(inPlayerIndex); - -// theVector.clear(); - - this->mPlayerList.erase(thePlayerIter); - - theSuccess = true; - } - } - - if(this->mCommander == inPlayerIndex) - { - this->SetCommander(-1); - } - - return theSuccess; -} - -void AvHTeam::TriggerAddTech(AvHTechID inTechID) -{ - if(!GetGameRules()->GetIsCombatMode()) - { - this->mResearchManager.TriggerAddTech(inTechID); - - // Get list of completed research that depended on this tech - TechNodeMap nodes = this->mResearchManager.GetResearchNodesDependentOn(inTechID); - - // For all researched nodes - TechNodeMap::const_iterator current, end = nodes.end(); - for( current = nodes.begin(); current != end; ++current ) - { - if( current->second->getIsResearched() ) - { - GetGameRules()->ProcessTeamUpgrade( current->first, this->mTeamNumber, 0, true ); - } - } - } -} - -void AvHTeam::TriggerRemoveTech(AvHTechID inTechID) -{ - if(!GetGameRules()->GetIsCombatMode()) - { - // Run through world, looking for anything alive that could still be giving us this tech - bool theFoundActiveTech = false; - - FOR_ALL_BASEENTITIES(); - AvHBuildable* theBuildable = dynamic_cast(theBaseEntity); - if(theBuildable) - { - if((theBuildable->GetSupportsTechID(inTechID)) && (theBuildable->GetTeamNumber() == this->mTeamNumber)) - { - if(theBuildable->GetIsTechActive()) - { - theFoundActiveTech = true; - break; - } - } - } - END_FOR_ALL_BASEENTITIES(); - - if(!theFoundActiveTech) - { - // If we can't find anything, remove it from the tech nodes - this->mResearchManager.TriggerRemoveTech(inTechID); - - // Get list of completed research that depended on this tech - TechNodeMap nodes = this->mResearchManager.GetResearchNodesDependentOn(inTechID); - - // For all researched nodes - TechNodeMap::iterator current, end = nodes.end(); - for( current = nodes.begin(); current != end; ++current ) - { - if( current->second->getIsResearched() ) - { - // Disable because of loss of this tech and remove them - GetGameRules()->ProcessTeamUpgrade( current->first, this->mTeamNumber, 0, false ); - } - } - } - } -} - -void AvHTeam::Update() -{ - PROFILE_START() - this->UpdateHints(); - PROFILE_END(kAvHTeamUpdateHints) - - PROFILE_START() - this->UpdateInventoryEnabledState(); - PROFILE_END(kAvHTeamUpdateInventoryEnabledState) - - PROFILE_START() - this->UpdateReinforcementWave(); - PROFILE_END(kAvHTeamUpdateReinforcementWave) - - PROFILE_START() - this->UpdateOrders(); - PROFILE_END(kAvHTeamUpdateOrders) - - PROFILE_START() - this->UpdateResearch(); - PROFILE_END(kAvHTeamUpdateResearch) - - PROFILE_START() - this->UpdateResources(); - PROFILE_END(kAvHTeamUpdateResources) - - PROFILE_START() - this->UpdateAlerts(); - PROFILE_END(kAvHTeamUpdateAlerts) - - PROFILE_START() - this->UpdateServerPlayerData(); - PROFILE_END(kAvHTeamUpdateServerPlayerData) - - PROFILE_START() - this->UpdateTeamStructures(); - PROFILE_END(kAvHTeamUpdateTeamStructures) - - PROFILE_START() - this->UpdateVoting(); - PROFILE_END(kAvHTeamUpdateVoting) - - PROFILE_START() - this->UpdatePlayers(); - PROFILE_END(kAvHTeamUpdatePlayers) -} - -void AvHTeam::UpdateTeamStructures() -{ - const float kStructureUpdateRate = 1.0f; - if((this->mTimeOfLastStructureUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastStructureUpdate + kStructureUpdateRate))) - { - this->mTimeOfLastStructureUpdate = gpGlobals->time; - - if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - this->mHiveInfoList.clear(); - - // Search for free hives, or hives on our team - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - if((theEntity->pev->team == 0) || ((AvHTeamNumber)theEntity->pev->team == this->mTeamNumber)) - { - AvHHiveInfo theHiveInfo; - - theHiveInfo.mPosX = theEntity->pev->origin.x; - theHiveInfo.mPosY = theEntity->pev->origin.y; - theHiveInfo.mPosZ = theEntity->pev->origin.z; - - if (theEntity->pev->max_health > 0) { - theHiveInfo.mHealthPercentage = (int)(100 * theEntity->pev->health / theEntity->pev->max_health); - } else { - theHiveInfo.mHealthPercentage = 0; - } - - int theEntityIndex = theEntity->entindex(); - - // Fill in hive status - int theStatus = kHiveInfoStatusBuilt; - - // Unbuilt hives - if(theEntity->pev->team == 0) - { - theStatus = kHiveInfoStatusUnbuilt; - } - // building hives - else if(!theEntity->GetIsBuilt()) - { - float theNormalizedBuildPercentage = theEntity->GetNormalizedBuildPercentage(); - if(theNormalizedBuildPercentage > 0.0f) - { - theStatus = kHiveInfoStatusBuildingStage1; - int theSteps = (int)(theNormalizedBuildPercentage*5.0f); - theStatus += theSteps; - } - else - { - theStatus = kHiveInfoStatusUnbuilt; - } - } - - theHiveInfo.mStatus = theStatus; - theHiveInfo.mTechnology = (int)(theEntity->GetTechnology()); - - // Under attack? - theHiveInfo.mUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntityIndex); - - // Add onto the end, so we don't resend unnecessarily - this->mHiveInfoList.insert(this->mHiveInfoList.end(), theHiveInfo); - } - END_FOR_ALL_ENTITIES(kesTeamHive); - - // Count number of active hives - int theNumHives = 0; - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - if(theEntity->GetIsActive()) - { - AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); - if(theTeamNumber == this->mTeamNumber) - { - theNumHives++; - } - } - END_FOR_ALL_ENTITIES(kesTeamHive); - this->mNumActiveHives = theNumHives; - } - else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) - { - // Count # of command stations - int theNumActiveCommandStations = 0; - FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) - if(theEntity->GetIsBuilt()) - { - AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); - if(theTeamNumber == this->mTeamNumber) - { - theNumActiveCommandStations++; - } - } - END_FOR_ALL_ENTITIES(kwsTeamCommand); - this->mNumBuiltCommandStations = theNumActiveCommandStations; - } - } -} - -void AvHTeam::UpdatePlayers() -{ - //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - // GetGameRules()->ClientCommand(theEntity, ); - //END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - - // Update every once in awhile - const float kPlayerUpdateRate = 2.0f; - if((this->mLastPlayerUpdateTime == -1) || (gpGlobals->time > (this->mLastPlayerUpdateTime + kPlayerUpdateRate))) - { - if(!GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsTournamentMode() && !GetGameRules()->GetIsCombatMode()) - { - // If the team is alien and has no hives left - if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - // Do we have any active hives? - int theNumGrowingOrActiveHives = 0; - FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) - AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); - if(theTeamNumber == this->mTeamNumber) - { - if(theEntity->GetIsActive() || theEntity->GetIsSpawning()) - { - theNumGrowingOrActiveHives++; - } - } - END_FOR_ALL_ENTITIES(kesTeamHive); - - if(theNumGrowingOrActiveHives == 0) - { - CBaseEntity* theWorld = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(0)); - if(theWorld) - { - entvars_t* theInflictor = theWorld->pev; - - // Do some damage to remaining players - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if((theEntity->pev->team == this->mTeamNumber) && theEntity->IsAlive()) - { - AvHPlayer* thePlayer = dynamic_cast(theEntity); - ASSERT(thePlayer); - float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theEntity->pev->iuser4, theEntity->GetUser3(), thePlayer->GetExperienceLevel()); - float theDamage = theMaxHealth/8; - - theEntity->TakeDamage(theInflictor, theInflictor, theDamage, DMG_DROWN | DMG_IGNOREARMOR); - } - theEntity->PlayHUDSound(HUD_SOUND_COUNTDOWN); - - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - } - } - } - } - - this->mLastPlayerUpdateTime = gpGlobals->time; - } - - // Update team score if we're in tournament mode - this->SendResourcesGatheredScore(true); -} - -bool AvHTeam::SendResourcesGatheredScore(bool inThisTeamOnly) -{ - bool theSentMessage = false; - - if(GetGameRules()->GetIsTournamentMode()) - { - // Only send message to players on our team, or spectating players - int theTeamScore = (int)this->GetTotalResourcesGathered(); - if(theTeamScore != this->mClientTotalResourcesGathered) - { - if(inThisTeamOnly) - { - for(EntityListType::iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) - { - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); - if(thePlayer) - { - NetMsg_TeamScore( thePlayer->pev, this->GetTeamName(), theTeamScore, 0 ); - } - } - } - else - { - NetMsg_TeamScore( this->GetTeamName(), theTeamScore, 0 ); - } - - this->mClientTotalResourcesGathered = theTeamScore; - - theSentMessage = true; - } - } - - return theSentMessage; -} - - -void AvHTeam::UpdateVoting() -{ - string theMessageForPlayers; - AvHHUDSound theSoundForPlayers = HUD_SOUND_INVALID; - bool theSendOnlyToNonvotingPlayers = false; - - // If we have more then 1 vote against commander - if(this->mVotes > 0) - { - // If a vote hasn't started - if(!this->mVoteStarted) - { - // Tell all players on team that a vote has started - theMessageForPlayers = kVoteStarted; - theSendOnlyToNonvotingPlayers = true; - - // Start vote - this->mVoteStarted = true; - this->mCommanderWhenVoteStarted = this->mCommander; - this->mTimeVoteStarted = gpGlobals->time; - } - else - { - bool theResetVote = false; - - // If commander logs out after vote started, cancel vote - if(this->mCommander != this->mCommanderWhenVoteStarted) - { - theMessageForPlayers = kVoteCancelled; - theResetVote = true; - } - else - { - // If we have enough votes to kick commander - float theVotePercent = avh_votepercentneeded.value; - int theMinVotesRequired = (int)avh_minvotesneeded.value; - int theVotesNeeded = max((float)theMinVotesRequired, this->mPlayerList.size()*theVotePercent); - - // #545: If the voting has changed, indicate so in the HUD - if ((this->mVotesNeeded != theVotesNeeded) || (this->mVotes != this->mPreviousVotes)) { - - // tankefugl: 0000545 - char theVoteMessage[512]; - sprintf(theVoteMessage, "Eject commander: %d of %d votes needed.", this->mVotes, theVotesNeeded); - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->pev->team == this->mTeamNumber) - { - UTIL_SayText(theVoteMessage, theEntity); // , theEntity->entindex()); - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - - - // UTIL_ClientPrintAll(HUD_PRINTTALK, theVoteMessage); - UTIL_LogPrintf(theVoteMessage); - - this->mVotesNeeded = theVotesNeeded; - this->mPreviousVotes = this->mVotes; - // :tankefugl - } - - if(this->mVotes >= theVotesNeeded) - { - // Kick commander - AvHPlayer* theCommander = this->GetCommanderPlayer(); - if(theCommander) - { - theCommander->StopTopDownMode(); - theCommander->SetUser3(AVH_USER3_MARINE_PLAYER); - - // Remember when we were voted down, so they can't command again - AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(theCommander->edict()); - if(theServerPlayerData) - { - theServerPlayerData->SetTimeVotedDown(gpGlobals->time); - } - - // Tell all players on team that commander has been ejected - theMessageForPlayers = kVoteSucceeded; - theSoundForPlayers = HUD_SOUND_MARINE_COMMANDER_EJECTED; - theResetVote = true; - } - } - else - { - // else if enough time has elapsed without a vote going through - int theVoteCastTimeInSeconds = avh_votecasttime.value*60; - if(gpGlobals->time > (this->mTimeVoteStarted + theVoteCastTimeInSeconds)) - { - // Tell all players on team that the vote has failed - theMessageForPlayers = kVoteFailed; - theResetVote = true; - } - } - } - - if(theResetVote) - { - // Reset vote (don't reset players that voted, they can only vote once per game) - this->mVotes = 0; - this->mVoteStarted = false; - this->mTimeVoteStarted = -1; - // clear the list of voting players to allow more than one vote each game (#545) - this->mVotingPlayers.clear(); - this->mVotesNeeded = 0; - this->mPreviousVotes = 0; - } - } - } - - // Send message to players - if(theMessageForPlayers != "") - { - // Send message to players on team - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetTeam() == this->GetTeamNumber()) - { - int thePlayerIndex = theEntity->entindex(); - EntityListType::iterator theIterator = std::find(this->mVotingPlayers.begin(), this->mVotingPlayers.end(), thePlayerIndex); - bool thePlayerHasVoted = (theIterator != this->mVotingPlayers.end()); - - if(!theSendOnlyToNonvotingPlayers || !thePlayerHasVoted) - { - theEntity->SendMessage(theMessageForPlayers.c_str()); - - // Play "commander has been ejected" sound for all players - if(theSoundForPlayers != HUD_SOUND_INVALID) - { - theEntity->PlayHUDSound(theSoundForPlayers); - } - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - } -} - -void AvHTeam::UpdateHints() -{ - if(GetGameRules()->GetGameStarted() && (this->GetPlayerCount() > 1) && !GetGameRules()->GetIsCombatMode()) - { - const float kHintUpdateInterval = 2.0f; - if((this->mTimeLastHintUpdate == -1) || (gpGlobals->time > (this->mTimeLastHintUpdate + kHintUpdateInterval))) - { - this->mTimeLastHintUpdate = gpGlobals->time; - - float kHintInterval = 20.0f; - if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) - { - kHintInterval = 30.0f; - } - - if((this->mTimeOfLastTeamHint == -1) || (gpGlobals->time > (this->mTimeOfLastTeamHint + kHintInterval))) - { - bool thePlayedHint = false; - - // Update commander hints - AvHPlayer* theCommanderPlayer = this->GetCommanderPlayer(); - if(theCommanderPlayer) - { - const float kGiveOrdersHintDelay = 20.0f; - float theTimeAsCommander = gpGlobals->time - theCommanderPlayer->GetTimeStartedTopDown(); - - if(theTimeAsCommander > kGiveOrdersHintDelay) - { - // Remove warning because tech tree doesn't currently require it - //// Do we have zero infantry portals, and we haven't played "need infantry portal" sound for awhile - //bool theTeamHasLiveInfantryPortal = false; - // - //FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) - // if(theEntity->pev->team == this->mTeamNumber) - // { - // if(theEntity->GetIsBuilt()) - // { - // theTeamHasLiveInfantryPortal = true; - // break; - // } - // } - //END_FOR_ALL_ENTITIES(kwsInfantryPortal); - // - //if(!theTeamHasLiveInfantryPortal) - //{ - // // Play "need infantry portal" sound - // theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_NEEDPORTAL); - // thePlayedHint = true; - //} - - if(!thePlayedHint) - { - // Did commander just join and hasn't given any orders? Play "select your troops and give them orders". - if(!theCommanderPlayer->GetHasGivenOrder()) - { - theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_GIVEORDERS); - thePlayedHint = true; - } - } - - if(!thePlayedHint) - { - // If we just played an alert, tell them how to go to it (but only tell every commander once) - const float kMinAlertNotifyInterval = 5.0f; - float theTimeOfLastAudioAlert = -1; - const AvHAlert* theLastAlert = this->GetLastAudioAlert(); - if(theLastAlert) - { - theTimeOfLastAudioAlert = theLastAlert->GetTime(); - } - - if((theTimeOfLastAudioAlert == -1) || ((gpGlobals->time - theTimeOfLastAudioAlert) < kMinAlertNotifyInterval)) - { - int theCurrentCommander = theCommanderPlayer->entindex(); - if(std::find(this->mCommandersToldAboutJumpKey.begin(), this->mCommandersToldAboutJumpKey.end(), theCurrentCommander) == this->mCommandersToldAboutJumpKey.end()) - { - theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_GOTOALERT); - this->mCommandersToldAboutJumpKey.push_back(theCurrentCommander); - } - } - } - - // Detect shell-shocked commander. Play "do something" sound. - if(!thePlayedHint) - { - const float kShellShockedInterval = 130.0f; - - float theTimeOfLastSignificantCommanderAction = theCommanderPlayer->GetTimeOfLastSignificantCommanderAction(); - float theTimeSinceLastSignificantAction = gpGlobals->time - theTimeOfLastSignificantCommanderAction; - - if((theTimeOfLastSignificantCommanderAction == -1) || (theTimeSinceLastSignificantAction > kShellShockedInterval)) - { - theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_COMMANDERIDLE); - thePlayedHint = true; - } - } - } - } - - // Update alien team hints - if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - int theNumBuilders = 0; - int theNumAliveTeamMembers = 0; - - // If we have no builders, tell the team - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - if(theEntity->GetIsRelevant() && (theEntity->pev->team == this->mTeamNumber)) - { - // If we have builders, or players that are evolving to builders... - if(theEntity->GetUser3() == AVH_USER3_ALIEN_PLAYER2) - { - theNumBuilders++; - } - else if(theEntity->GetEvolution() == ALIEN_LIFEFORM_TWO) - { - theNumBuilders++; - } - else if((theEntity->GetUser3() == AVH_USER3_ALIEN_EMBRYO) && (theEntity->GetPreviousUser3() == AVH_USER3_ALIEN_PLAYER2)) - { - AvHMessageID theEvolution = theEntity->GetEvolution(); - switch(theEvolution) - { - case ALIEN_EVOLUTION_ONE: - case ALIEN_EVOLUTION_TWO: - case ALIEN_EVOLUTION_THREE: - case ALIEN_EVOLUTION_SEVEN: - case ALIEN_EVOLUTION_EIGHT: - case ALIEN_EVOLUTION_NINE: - case ALIEN_EVOLUTION_TEN: - case ALIEN_EVOLUTION_ELEVEN: - case ALIEN_EVOLUTION_TWELVE: - theNumBuilders++; - break; - } - } - - if(theEntity->IsAlive()) - { - theNumAliveTeamMembers++; - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - - // Only play if there is more then one person alive on team (player could be last one left or could be saving up) - if((theNumBuilders == 0) && (theNumAliveTeamMembers > 1)) - { - this->PlayHUDSoundForAlivePlayers(HUD_SOUND_ALIEN_NEED_BUILDERS); - thePlayedHint = true; - } - - // Now, fun easter eggs, but not in tourny mode - if(!thePlayedHint && !GetGameRules()->GetIsTournamentMode() && this->mPlayFunSoundsThisGame) - { - // If a hive is under attack and the marines are way ahead, play "game over man" - - AvHHive* theRandomHive = AvHSUGetRandomActiveHive(this->mTeamNumber); - if(theRandomHive) - { - const float kRadius = 600; - - int theClutterEntities = 0; - int theDeadTeammates = 0; - int theDeadPlayers = 0; - - CBaseEntity* theEntity = NULL; - while((theEntity = UTIL_FindEntityInSphere(theEntity, theRandomHive->pev->origin, kRadius)) != NULL) - { - // If there are a ton of dead alien players, sometimes play "we need better players" - if(theEntity->IsPlayer() && !theEntity->IsAlive()) - { - if(theEntity->pev->team == this->mTeamNumber) - { - theDeadTeammates++; - } - - theDeadPlayers++; - } - else - { - // If there are tons of aliens buildings nearby, play "place is a mess" sound for all aliens nearby - AvHBaseBuildable* theBuildable = dynamic_cast(theEntity); - CBasePlayerItem* theItem = dynamic_cast(theEntity); - if(theBuildable || theItem) - { - theClutterEntities++; - } - } - } - - if((theDeadPlayers >= 14) && !this->mPlayedSeeDeadPeople) - { - EMIT_SOUND(theRandomHive->edict(), CHAN_AUTO, kAlienSeeDead, 1.0f, ATTN_NORM); - this->mPlayedSeeDeadPeople; - thePlayedHint = true; - } - else if(theDeadTeammates >= 8) - { - this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_NEED_BETTER); - thePlayedHint = true; - } - else if(theClutterEntities >= 25) - { - this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_MESS); - thePlayedHint = true; - } - } - - // If aliens have all three hives and all upgrades, play "donce" - if(this->GetNumActiveHives() == kMaxHives) - { - if(this->GetAlienUpgrades().size() >= kMaxUpgradeCategories*kMaxUpgradeLevel) - { - this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_NOW_DONCE); - thePlayedHint = true; - } - } - - // If there are tons of bodies near a hive, play "I see dead people" - - } - } - - if(thePlayedHint) - { - this->mTimeOfLastTeamHint = gpGlobals->time; - } - } - } - } -} - -void AvHTeam::UpdateCommanderScore() -{ - // The commander score is the average of his team score - const float kUpdateScoreTime = 2.0f; - float theCurrentTime = gpGlobals->time; - - int theCommander = this->GetCommander(); - if(theCommander > 0) - { - if((this->mLastCommandScoreUpdateTime == -1) || (theCurrentTime > this->mLastCommandScoreUpdateTime + kUpdateScoreTime)) - { - int theScore = 0; - int theNumPlayers = 0; - - // Run through players on team and average score - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - // For each player, update alien inventory with this # of hives - if(theEntity->GetTeam() == this->GetTeamNumber()) - { - if(theEntity->entindex() != theCommander) - { - theScore += theEntity->GetScore(); - theNumPlayers++; - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName); - - int theCommanderScore = (int)((theScore/(float)theNumPlayers) + .5f); - - AvHPlayer* theCommanderPlayer = this->GetCommanderPlayer(); - if(theCommanderPlayer) - { - theCommanderPlayer->SetScore(theCommanderScore); - } - - this->mLastCommandScoreUpdateTime = theCurrentTime; - } - } -} - -void AvHTeam::UpdateInventoryEnabledState() -{ - // Count # of hives - int theNumHives = this->GetNumActiveHives(); - - // Loop through players on team - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - // For each player, update alien inventory with this # of hives - if(theEntity->GetTeam() == this->GetTeamNumber()) - { - if(GetGameRules()->GetIsCombatMode()) - { - theNumHives = 1; - - MessageIDListType& thePurchasedCombatUpgrades = theEntity->GetPurchasedCombatUpgrades(); - MessageIDListType::iterator theIter = std::find(thePurchasedCombatUpgrades.begin(), thePurchasedCombatUpgrades.end(), ALIEN_HIVE_TWO_UNLOCK); - - //AvHTechNode theTechNode; - //if(theEntity->GetCombatNodes().GetTechNode(ALIEN_HIVE_TWO_UNLOCK, theTechNode) && theTechNode.GetIsResearched()) - if(theIter != thePurchasedCombatUpgrades.end()) - { - theNumHives++; - - //if(theEntity->GetCombatNodes().GetTechNode(ALIEN_HIVE_THREE_UNLOCK, theTechNode) && theTechNode.GetIsResearched()) - theIter = std::find(thePurchasedCombatUpgrades.begin(), thePurchasedCombatUpgrades.end(), ALIEN_HIVE_THREE_UNLOCK); - if(theIter != thePurchasedCombatUpgrades.end()) - { - theNumHives++; - } - } - } - - theEntity->UpdateInventoryEnabledState(theNumHives); - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) -} - -void AvHTeam::UpdateReinforcementWave() -{ - int theNumPlayersOnTeam = this->GetPlayerCount(); - int theNumDeadPlayers = this->GetPlayerCount(true); - int theWaveSize = AvHSUCalcCombatSpawnWaveSize(theNumPlayersOnTeam, theNumDeadPlayers); - - // If we're in combat mode - if(GetGameRules()->GetIsCombatMode() && (GetGameRules()->GetVictoryTeam() == TEAM_IND) && !GetGameRules()->GetIsIronMan()) - { - // If reinforcement wave hasn't started - if(this->mTimeReinforcementWaveComplete == -1) - { - // If team has at least one dead player, start wave - for(EntityListType::const_iterator theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) - { - const AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); - ASSERT(thePlayer); - float theWaveTime = AvHSUCalcCombatSpawnTime(this->mTeamType, theNumPlayersOnTeam, theNumDeadPlayers, 0); - - switch(thePlayer->GetPlayMode()) - { - case PLAYMODE_AWAITINGREINFORCEMENT: - case PLAYMODE_REINFORCING: - // Set time reinforcement is complete - this->mTimeReinforcementWaveComplete = gpGlobals->time + theWaveTime; - break; - } - } - } - else - { - // Spawn back in a max of X players per wave (1/4 rounded up) - int theNumRespawning = 0; - - EntityListType::const_iterator theIter; - - // Count number of people currently respawning - for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) - { - AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); - ASSERT(thePlayer); - - if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING) - { - theNumRespawning++; - } - } - - // Find the player that's been waiting the longest - if(theNumRespawning < theWaveSize) - { - EntityListType::const_iterator theLongestWaitingPlayer = this->mPlayerList.end(); - float theLongestWaitingTime = -1; - - // Loop through players - for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) - { - // While we don't allow any more to repsawn or we hit end of list - AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); - ASSERT(thePlayer); - - if(thePlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) - { - float thePlayerWaitTime = (gpGlobals->time - thePlayer->GetTimeLastPlaying()); - if((theLongestWaitingTime == -1) || (thePlayerWaitTime > theLongestWaitingTime)) - { - theLongestWaitingPlayer = theIter; - theLongestWaitingTime = thePlayerWaitTime; - } - } - } - - if(theLongestWaitingPlayer != this->mPlayerList.end()) - { - AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theLongestWaitingPlayer); - ASSERT(thePlayer); - thePlayer->SetPlayMode(PLAYMODE_REINFORCING); - } - } - - // Respawn players when wave is complete - if(gpGlobals->time > this->mTimeReinforcementWaveComplete) - { - for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) - { - // While we don't allow any more to repsawn or we hit end of list - AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); - ASSERT(thePlayer); - - if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING) - { - thePlayer->SetPlayMode(PLAYMODE_PLAYING); - } - } - - // Reset wave - this->mTimeReinforcementWaveComplete = -1; - } - } - } -} - -void AvHTeam::UpdateOrders() -{ - // For each order in our list - for(OrderListType::iterator theOrderIter = this->mOrderList.begin(); theOrderIter != this->mOrderList.end(); /* no increment*/) - { - ASSERT(theOrderIter->GetReceiver() != -1 ); - if(theOrderIter->Update()) - { - bool theOrderCancelled = theOrderIter->GetOrderCancelled(); - if(!theOrderCancelled) - { - EntityInfo theReceiver = theOrderIter->GetReceiver(); - if(theReceiver != -1 ) - { - GetGameRules()->TriggerAlert(this->mTeamNumber, ALERT_ORDER_COMPLETE, theReceiver); - } - } - } - - // Have any orders been completed for a bit? - const float kExpireTime = 1.0f; - if(!theOrderIter->GetOrderActive() && (theOrderIter->GetTimeOrderCompleted() != -1) && (gpGlobals->time > (theOrderIter->GetTimeOrderCompleted() + kExpireTime))) - { - this->mOrderList.erase(theOrderIter); - } - else - { - theOrderIter++; - } - - // Is the order complete? -// for(OrderListType::iterator theOrderIter = this->mOrderList.begin(); theOrderIter != this->mOrderList.end(); /* no increment */) -// { -// // Is the order complete? -// bool theOrderCancelled = false; -// if(theOrderIter->GetIsComplete(&theOrderCancelled)) -// { -// if(!theOrderCancelled) -// { -// // For each receiver -// EntityListType theReceivers = theOrderIter->GetReceivers(); -// for(EntityListType::iterator theReceiverIter = theReceivers.begin(); theReceiverIter != theReceivers.end(); theReceiverIter++) -// { -// // Get player. This fails occasionally, due to selection issues (ie, a building or other non-player entity will be in here) -// AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theReceiverIter))); -// //ASSERT(thePlayer); -// if(thePlayer) -// { -// // Set the order complete -// thePlayer->PlayOrderComplete(); -// } -// } -// -// // Set order complete for commander -// int theCommanderIndex = this->GetCommander(); -// if(theCommanderIndex >= 0) -// { -// AvHPlayer* theCommander = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theCommanderIndex))); -// if(theCommander) -// { -// theCommander->PlayOrderComplete(); -// } -// } -// } -// -// // Set the receivers to none because it's no longer relevant -// AvHOrder theCompletedOrder(*theOrderIter); -// theCompletedOrder.SetOrderCompleted(); -// -// this->SetOrder(theCompletedOrder); -// } -// else -// { -// theOrderIter++; -// } - } -} - -//void AvHTeam::UpdateReinforcements() -//{ -// // If this team is alien -// if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) -// { -// // For each hive on this team -// FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) -// if(AvHTeamNumber(theEntity->pev->team) == this->mTeamNumber) -// { -// // Update reinforcements -// theEntity->UpdateReinforcements(); -// } -// END_FOR_ALL_ENTITIES(kesTeamHive); -// } -// // else if it's marine -// else if(this->mTeamType == AVH_CLASS_TYPE_MARINE) -// { -// // For each infantry portal on this team -// FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) -// if(AvHTeamNumber(theEntity->pev->team) == this->mTeamNumber) -// { -// // Update reinforcements -// theEntity->UpdateReinforcements(); -// } -// END_FOR_ALL_ENTITIES(kwsInfantryPortal); -// } -// else -// { -// ASSERT(false); -// } -//} - -void AvHTeam::UpdateResearch() -{ - this->mResearchManager.UpdateResearch(); - this->UpdateMenuTechSlots(); -} - -void AvHTeam::UpdateResources() -{ - const AvHGameplay& theGameplay = GetGameRules()->GetGameplay(); - float kResourceUpdateInterval = theGameplay.GetTowerInjectionTime(); - - if(GetGameRules()->GetIsHamboneMode()) - { - kResourceUpdateInterval /= 2.0f; - } - - // Change here if teams should always get at least one resource - float theResourcesGathered = 0.0f; - - float theCurrentTime = gpGlobals->time; - if((this->mLastResourceUpdateTime == -1) || (theCurrentTime > (this->mLastResourceUpdateTime + kResourceUpdateInterval))) - { - // Add resources for each built-on resource on the team - for(EntityListType::iterator theResourceTowerIter = this->mResourceTowerList.begin(); theResourceTowerIter != this->mResourceTowerList.end(); /* nothing */) - { - CBaseEntity* theResourceTowerEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theResourceTowerIter)); - AvHResourceTower* theResourceTower = dynamic_cast(theResourceTowerEntity); - if(theResourceTower && (theResourceTower->pev->team == this->mTeamNumber)) - { - if(theResourceTower->GetIsActive()) - { - // Has enough time elapsed? - float theLastTimeResourcesContributed = theResourceTower->GetTimeLastContributed(); - float theCurrentTime = gpGlobals->time; - - if(theCurrentTime > (theLastTimeResourcesContributed + kResourceUpdateInterval)) - { - // Take into account upgrade level stored in tower - float theTowerContribution = theGameplay.GetTowerInjectionAmount(); - theResourcesGathered += theTowerContribution; - - // Decrement the amount of points the resources has - theResourceTower->SetTimeLastContributed(theCurrentTime); - - AvHSUPlayNumericEventAboveStructure(theTowerContribution, theResourceTower); - } - } - - theResourceTowerIter++; - } - else - { - // Remove it from team also - theResourceTowerIter = this->mResourceTowerList.erase(theResourceTowerIter); - } - } - - // If we're marines, add it to the team total - if(this->mTeamType == AVH_CLASS_TYPE_MARINE) - { - this->SetTeamResources(this->GetTeamResources() + theResourcesGathered); - } - else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) - { - // Else dump it into the pool, and split the whole pool up among all players on our team - float theTeamResources = this->GetTeamResources() + theResourcesGathered; - - int theNumPlayers = this->GetPlayerCount(); - float theAmountForPlayer = theTeamResources/(float)theNumPlayers; - - // Clear resources, assuming all res will be given out. Anything over will gob ack into team pool - this->SetTeamResources(0.0f); - - for(EntityListType::iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) - { - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); - ASSERT(thePlayer); - - // If the amount goes over the player's max, it goes back into team resources - float theResourcesBefore = thePlayer->GetResources(); - thePlayer->SetResources(thePlayer->GetResources() + theAmountForPlayer); - } - } - - // Increment total resources gathered - this->AddResourcesGathered(theResourcesGathered); - - this->mLastResourceUpdateTime = theCurrentTime; - } -} -// puzl: 1041 -// o Added back in steamid based authids -#ifndef USE_OLDAUTH -#ifdef AVH_PLAYTEST_BUILD -// Function that is backwards-compatible with WON ids -string AvHSUGetPlayerAuthIDString(edict_t* inPlayer) -{ - const char* kSteamIDInvalidID = "-1"; - string thePlayerAuthID; - - // Try to get SteamID - const char* theSteamID = g_engfuncs.pfnGetPlayerAuthId(inPlayer); - if(strcmp(theSteamID, kSteamIDInvalidID)) - { - thePlayerAuthID = theSteamID; - } - // If that fails, get WonID and put it into a string - else - { - int theWonID = g_engfuncs.pfnGetPlayerWONId(inPlayer); - thePlayerAuthID = MakeStringFromInt(theWonID); - } - - return thePlayerAuthID; -} -#endif -#endif -AvHServerPlayerData* AvHTeam::GetServerPlayerData(edict_t* inEdict) -{ -#ifdef AVH_PLAYTEST_BUILD - string theNetworkID = AvHSUGetPlayerAuthIDString(inEdict); -#else - string theNetworkID = AvHNexus::getNetworkID(inEdict); -#endif - return &this->mServerPlayerData[theNetworkID]; -} -// :puzl -void AvHTeam::UpdateServerPlayerData() -{ - const float kServerPlayerDataUpdateInterval = 1.0f; - float theCurrentTime = gpGlobals->time; - if((this->mLastServerPlayerDataUpdateTime == -1) || (theCurrentTime > (this->mLastServerPlayerDataUpdateTime + kServerPlayerDataUpdateInterval))) - { - // Update all settings to remember keyed to their WONid - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(theEntity->edict()); - if(theServerPlayerData) - { - // Expire any commander bans - float theCommanderVoteDownTime = avh_votedowntime.value; - float theTimeVotedDown = theServerPlayerData->GetTimeVotedDown()*60; - if(theTimeVotedDown > 0) - { - if(gpGlobals->time > (theTimeVotedDown + theCommanderVoteDownTime)) - { - theServerPlayerData->SetTimeVotedDown(-1); - } - } - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - } -} - -void AvHTeam::UpdateMenuTechSlots() -{ - if(this->mTeamType == AVH_CLASS_TYPE_MARINE) - { - // Update commander menus. Get tech slots for each menu and add them all in one 32 long list - this->mMenuTechSlots = 0; - - AvHGamerules* theGameRules = GetGameRules(); - const AvHTeam* theTeam = theGameRules->GetTeam(this->mTeamNumber); - if(theTeam) - { - for(int j = 0; j < 4; j++) - { - AvHUser3 theUser3 = AVH_USER3_NONE; - int theBaseIndex = 0; - - switch(j) - { - case 0: - theUser3 = AVH_USER3_MENU_BUILD; - theBaseIndex = 0; - break; - case 1: - theUser3 = AVH_USER3_MENU_BUILD_ADVANCED; - theBaseIndex = 8; - break; - case 2: - theUser3 = AVH_USER3_MENU_ASSIST; - theBaseIndex = 16; - break; - case 3: - theUser3 = AVH_USER3_MENU_EQUIP; - theBaseIndex = 24; - break; - } - - // For each slot, see if tech is available - AvHTechSlots theTechSlots; - if(theTeam->GetTechSlotManager().GetTechSlotList(theUser3, theTechSlots)) - { - // Store results in full 32-bits of iuser1 - for(int i = 0; i < kNumTechSlots; i++) - { - int theCurrentMask = 1 << (theBaseIndex + i); - - AvHMessageID theMessage = theTechSlots.mTechSlots[i]; - if(theMessage != MESSAGE_NULL) - { - if(this->GetIsTechnologyAvailable(theMessage)) - { - this->mMenuTechSlots |= theCurrentMask; - } - } - } - } - } - } - } -} - -bool AvHTeam::GetIsTechnologyAvailable(AvHMessageID inMessageID) const -{ - bool theTechnologyAvailable = this->GetResearchManager().GetIsMessageAvailable(inMessageID); - return theTechnologyAvailable; -} - - - - -bool AvHTeam::GetLastAlert(AvHAlert& outAlert, bool inClearAlert, bool inAnyMessage, AvHMessageID* ioMessageID) -{ - float theLastAlertTime = -1; - bool theSuccess = false; - - // Look for last alert - AlertListType::iterator theLastAlertIter; - AvHMessageID theOutputMessageID = MESSAGE_NULL; - - for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) - { - if(inAnyMessage || (ioMessageID && (theIter->first == *ioMessageID))) - { - for(AlertListType::iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) - { - float theCurrentTime = theAlertIter->GetTime(); - if(theCurrentTime > theLastAlertTime) - { - theLastAlertIter = theAlertIter; - theLastAlertTime = theCurrentTime; - theOutputMessageID = theIter->first; - theSuccess = true; - } - } - } - } - - // If we found it, set the alert - if(theSuccess) - { - outAlert = *theLastAlertIter; - if(ioMessageID) - { - *ioMessageID = theOutputMessageID; - } - } - - // Clear the alert if specified - if(inClearAlert) - { - for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) - { - for(AlertListType::iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) - { - if(theAlertIter == theLastAlertIter) - { - theIter->second.erase(theLastAlertIter); - break; - } - } - } - } - - return theSuccess; -} - -float AvHTeam::GetAudioAlertInterval() const -{ - float theAudioAlertInterval = BALANCE_VAR(kAudioAlertInterval); - return theAudioAlertInterval; -} - -void AvHTeam::AddAlert(const AvHAlert& inAlert, AvHMessageID inMessageID) -{ - this->mAlertList[inMessageID].push_back(inAlert); -} - -const AvHAlert* AvHTeam::GetLastAudioAlert() const -{ - // Look for last audio alert - const AvHAlert* theLastAudioAlert = NULL; - float theLastTime = -1; - - for(MessageAlertListType::const_iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) - { - for(AlertListType::const_iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) - { - if(theAlertIter->GetPlayedAudio()) - { - float theCurrentTime = theAlertIter->GetTime(); - if(theCurrentTime > theLastTime) - { - theLastAudioAlert = &(*theAlertIter); - theLastTime = theCurrentTime; - } - } - } - } - - return theLastAudioAlert; -} - -AlertListType AvHTeam::GetAlerts(AvHMessageID inMessageID) -{ - return this->mAlertList[inMessageID]; -} - -void AvHTeam::UpdateAlerts() -{ - const float theAlertDuration = BALANCE_VAR(kAlertExpireTime); - - #ifdef AVH_PLAYTEST_BUILD - // Run through our team, and alert everyone if there is a player that's no longer playing or on the server - for(EntityListType::const_iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) - { - int thePlayerIndex = *thePlayerIter; - bool theSendAlert = false; - char theErrorMessage[512]; - - AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); - if(!thePlayer) - { - theSendAlert = true; - strcpy(theErrorMessage, "NULL player"); - } - else - { - AvHPlayMode thePlayMode = thePlayer->GetPlayMode(); - if((thePlayMode != PLAYMODE_PLAYING) && (thePlayMode != PLAYMODE_AWAITINGREINFORCEMENT) && (thePlayMode != PLAYMODE_REINFORCING)) - { - theSendAlert = true; - sprintf(theErrorMessage, "Invalid play mode: %d\n", thePlayMode); - } - else if(AvHTeamNumber(thePlayer->pev->team) != this->mTeamNumber) - { - theSendAlert = true; - sprintf(theErrorMessage, "on wrong team (%d instead of %d)", thePlayer->pev->team, this->mTeamNumber); - } - } - - if(theSendAlert) - { - const char* thePlayerName = ""; - thePlayerName = STRING(thePlayer->pev->netname); - - char theFullErrorMesssage[512]; - sprintf(theFullErrorMesssage, "Invalid player bug detected <\"%s\">: %d", thePlayerName, theErrorMessage); - UTIL_SayTextAll(theFullErrorMesssage, thePlayer); - } - } - #endif - - // Run through alerts, and expire any past a certain age - for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); ++theIter) - { - AlertListType& theAlertList = theIter->second; - for(AlertListType::iterator theAlertIter = theAlertList.begin(); theAlertIter != theAlertList.end(); /* no inc */) - { - float theAlertTime = theAlertIter->GetTime(); - if(gpGlobals->time > (theAlertTime + theAlertDuration)) - { - theAlertIter = theAlertList.erase(theAlertIter); - } - else - { - theAlertIter++; - } - } - } -} - -EntityListType AvHTeam::GetGroup(int inGroupNumber) -{ - ASSERT(inGroupNumber >= 0); - ASSERT(inGroupNumber < kNumHotkeyGroups); - - return this->mGroups[inGroupNumber]; -} - -void AvHTeam::SetGroup(int inGroupNumber, EntityListType& inEntityListType) -{ - // Set group - ASSERT(inGroupNumber >= 0); - ASSERT(inGroupNumber < kNumHotkeyGroups); - - this->mGroups[inGroupNumber] = inEntityListType; - - // Update group type - AvHUser3 theUser3 = AvHSUGetGroupTypeFromSelection(this->mGroups[inGroupNumber]); - this->mGroupTypes[inGroupNumber] = theUser3; -} - -AvHUser3 AvHTeam::GetGroupType(int inGroupNumber) -{ - ASSERT(inGroupNumber >= 0); - ASSERT(inGroupNumber < kNumHotkeyGroups); - - return this->mGroupTypes[inGroupNumber]; -} - -EntityListType AvHTeam::GetSelectAllGroup() -{ - return this->mSelectAllGroup; -} - -void AvHTeam::SetSelectAllGroup(EntityListType& inGroup) -{ - this->mSelectAllGroup = inGroup; -} - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -AvHObjectiveManager *AvHTeam::GetObjectiveManager() -{ - return &this->mObjectiveManager; -} +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHTeam.cpp $ +// $Date: 2002/11/26 20:35:00 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHTeam.cpp,v $ +// Revision 1.55 2002/11/26 20:35:00 Flayra +// - Hurt fix +// +// Revision 1.54 2002/11/26 15:58:31 Flayra +// - When the alien team no longer has any growing or active hives, all aliens slowly take damage. This is done to speed up the end game and prevent hiding llamas. +// +// Revision 1.53 2002/11/22 23:26:59 Flayra +// - Don't play kill lingering aliens with cheats on (when testing) +// +// Revision 1.52 2002/11/22 21:15:37 Flayra +// - Remove owner of entities when player leaves the team +// - Do damage to aliens without hives +// - Fixed bug where a vote could affect a commander that logged in after a vote had been started. +// +// Revision 1.51 2002/11/15 23:31:26 Flayra +// - Added "ready" verification for tourny mode +// +// Revision 1.50 2002/11/12 02:29:33 Flayra +// - Added better standardized logging +// +// Revision 1.49 2002/11/06 03:08:56 Flayra +// - Undid +1 for resources, it's too drastic +// +// Revision 1.48 2002/11/06 02:23:49 Flayra +// - Removed faulty minimum trickle that was giving 1 extra resource point to both teams +// +// Revision 1.47 2002/11/06 01:39:04 Flayra +// - Show true resources going to team +// +// Revision 1.46 2002/11/03 04:52:55 Flayra +// - Hard-coded resources +// +// Revision 1.45 2002/10/24 21:43:29 Flayra +// - Alien easter eggs +// - Voting fix +// +// Revision 1.44 2002/10/19 21:28:38 Flayra +// - Debugging info for linux +// +// Revision 1.43 2002/10/19 21:09:56 Flayra +// - Debugging info for linux +// +// Revision 1.42 2002/10/19 20:56:06 Flayra +// - Debugging info for linux +// +// Revision 1.41 2002/10/19 20:19:26 Flayra +// - Debugging info for linux +// +// Revision 1.40 2002/10/19 20:04:14 Flayra +// - Debugging info for linux +// +// Revision 1.39 2002/10/18 22:23:04 Flayra +// - Added beginnings of alien easter eggs +// - Added "we need builders" alert +// +// Revision 1.38 2002/10/04 18:03:23 Flayra +// - Fixed bug where extra resources were sometimes being wasted on the alien team +// +// Revision 1.37 2002/10/03 19:09:55 Flayra +// - New resource model +// - Orders refactoring +// - Tech tree changes +// - Aliens always get initial points when joining +// - Play gamestart sound +// - New trait available trigger +// - Moved voting stuff to server variables +// - Slowed down hints +// +// Revision 1.36 2002/09/25 20:51:31 Flayra +// - Play commander-idle sounds less often +// +// Revision 1.35 2002/09/23 22:35:43 Flayra +// - Removed hive donation and put in new system for builders +// - Updated tech tree (jetpacks, heavy armor, moved phase) +// - Resource towers set as built on game start +// - Added tons of commander hints (low resource alerts, tell commander about pressing jump key, commander ejected, select troops and give orders, don't just sit there, etc.) +// +// Revision 1.34 2002/09/09 20:08:26 Flayra +// - Added commander voting +// - Added hive info +// - Changed how commander scoring works +// +// Revision 1.33 2002/08/31 18:01:03 Flayra +// - Work at VALVe +// +// Revision 1.32 2002/08/02 21:45:20 Flayra +// - Reworked alerts. +// +// Revision 1.31 2002/07/28 19:21:27 Flayra +// - Balance changes after/during RC4a +// +// Revision 1.30 2002/07/26 23:08:25 Flayra +// - Added numerical feedback +// +// Revision 1.29 2002/07/25 16:58:00 flayra +// - Linux changes +// +// Revision 1.28 2002/07/24 18:55:53 Flayra +// - Linux case sensitivity stuff +// +// Revision 1.27 2002/07/23 17:32:23 Flayra +// - Resource model overhaul (closed system, random initial points), tech tree changes (new marine upgrades), free resource tower at nearest func_resource +// +// Revision 1.26 2002/07/10 14:45:26 Flayra +// - Fixed victory condition bug (bug #171) +// +// Revision 1.25 2002/07/08 17:19:42 Flayra +// - Added handicapping, map validity checking, reinforcements happen independently of teams now +// +// Revision 1.24 2002/07/01 21:24:07 Flayra +// - Brought siege back, removed alpha male resource model +// +// Revision 1.23 2002/06/25 18:21:33 Flayra +// - New enabled/disabled system for alien weapons, better victory detection, updated tech tree (removed old junk, added armory upgrade), fixed bug where a commander that leaves the game hogged the station, update resources less often (optimization) +// +// Revision 1.22 2002/06/03 17:00:33 Flayra +// - Removed mines and siege temporarily while being worked on, removed old code, removed redundant hive class name +// +// Revision 1.21 2002/05/28 18:09:51 Flayra +// - Track number of webs for team, fix for premature victory condition (again), fixed bug where alien upgrades weren't being cleared between levels, causing eventual overflows for alien players after many games, recycling support, spawn in command center when game starts +// +// Revision 1.20 2002/05/23 02:32:57 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "dlls/cbase.h" +#include "mod/AvHTeam.h" +#include "mod/AvHPlayer.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHServerVariables.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "game_shared/teamconst.h" +#include "mod/AvHBuildable.h" +#include "mod/AvHServerUtil.h" +#include "mod/AvHSharedUtil.h" +#include "mod/AvHTitles.h" +#include "mod/AvHPlayerUpgrade.h" +#include "util/MathUtil.h" +#include "mod/AvHNetworkMessages.h" +#include "mod/AvHNexusServer.h" + +extern int gPhaseInEventID; +extern cvar_t avh_votecasttime; +extern cvar_t avh_votedowntime; +extern cvar_t avh_votepercentneeded; +extern cvar_t avh_minvotesneeded; +extern cvar_t avh_initialalienpoints; +extern cvar_t avh_eastereggchance; + +AvHTeam::AvHTeam(AvHTeamNumber inTeamNumber) +{ + this->Init(); + this->mTeamNumber = inTeamNumber; + this->mResearchManager.SetTeamNumber(inTeamNumber); +} + +void AvHTeam::Init() +{ + this->mTeamType = AVH_CLASS_TYPE_UNDEFINED; + this->mTeamName = kInvalidTeamName; + this->mTeamNumber = TEAM_IND; + this->mCommander = -1; + this->mCommanderWhenVoteStarted = -1; + + this->mPlayerList.clear(); + this->mResourceTowerList.clear(); + + this->mLastResourceUpdateTime = -1; + this->mLastCommandScoreUpdateTime = -1; + this->mLastServerPlayerDataUpdateTime = -1; + this->mLastPlayerUpdateTime = -1; + this->mLastInjectionTime = 0; + this->mLastHiveSpawnTime = 0; + + // Alerts + this->mAlertList.clear(); + this->mTimeOfLastTeamHint = -1; + this->mTimeLastHintUpdate = -1; + + this->mTeamResources = 0; + this->mHandicap = 1.0f; + + this->mResearchManager.Reset(); + + this->mAlienUpgrades.clear(); + this->mTeamWideUpgrades = 0; + + this->mNumWebStrands = 0; + this->mIsReady = false; + + this->mTotalResourcesGathered = 0; + this->mClientTotalResourcesGathered = 0; + + this->mMenuTechSlots = 0; + + this->mTimeReinforcementWaveComplete = -1; +} + +bool AvHTeam::AddPlayer(int inPlayerIndex) +{ + bool theSuccess = false; + + if(!this->GetIsPlayerOnTeam(inPlayerIndex)) + { + // Add player + this->mPlayerList.push_back(inPlayerIndex); + + theSuccess = true; + } + + return theSuccess; +} + +bool AvHTeam::AddResourceTower(int inResourceTowerIndex) +{ + bool theSuccess = false; + + EntityListType::const_iterator theIter = std::find(this->mResourceTowerList.begin(), this->mResourceTowerList .end(), inResourceTowerIndex); + if(theIter == this->mResourceTowerList.end()) + { + this->mResourceTowerList.push_back(inResourceTowerIndex); + theSuccess = true; + } + + return theSuccess; +} + +bool AvHTeam::RemoveResourceTower(int inResourceTowerIndex) +{ + bool theSuccess = false; + + EntityListType::iterator theIter = std::find(this->mResourceTowerList.begin(), this->mResourceTowerList .end(), inResourceTowerIndex); + if(theIter != this->mResourceTowerList.end()) + { + this->mResourceTowerList.erase(theIter); + theSuccess = true; + } + + return theSuccess; +} + +bool AvHTeam::GetIsReady() const +{ + return this->mIsReady; +} + +int AvHTeam::GetMenuTechSlots() const +{ + return this->mMenuTechSlots; +} + +void AvHTeam::SetIsReady(bool bIsReady) +{ + this->mIsReady = bIsReady; +} + +int AvHTeam::GetNumBuiltCommandStations() const +{ + return this->mNumBuiltCommandStations; +} + +int AvHTeam::GetNumActiveHives() const +{ + return this->mNumActiveHives; +} + +float AvHTeam::GetMaxResources(AvHUser3 inUser3) const +{ + float theMaxResources = -1; + + if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + //if(inUser3 == AVH_USER3_ALIEN_PLAYER2) + //{ + // Needed so builders can create hives (also means that having more builders diverts resources from combat/offense) + theMaxResources = kMaxAlienResources; + //} + //else + //{ + // int theNumActiveHives = max(this->GetNumActiveHives(), 1); + // theMaxResources = (theNumActiveHives/(float)kMaxAlienHives)*kMaxAlienResources; + //} + } + + return theMaxResources; +} + + +bool AvHTeam::GetTeamHasAbilityToRespawn() const +{ + bool theAbilityToRespawn = false; + + // If game hasn't started + if(!GetGameRules()->GetGameStarted()) + { + // return true + theAbilityToRespawn = true; + } + // else if we're a marine team + else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) + { + // Do we have any built infantry portals? + FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) + if(theEntity->GetIsBuilt()) + { + AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); + if(theTeamNumber == this->mTeamNumber) + { + theAbilityToRespawn = true; + break; + } + } + END_FOR_ALL_ENTITIES(kwsInfantryPortal); + } + // else if we're an alien team + else if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + // Do we have any active hives? + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if(theEntity->GetIsActive()) + { + AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); + if(theTeamNumber == this->mTeamNumber) + { + theAbilityToRespawn = true; + break; + } + } + END_FOR_ALL_ENTITIES(kesTeamHive); + } + + return theAbilityToRespawn; +} + +bool AvHTeam::GetHasAtLeastOneActivePlayer(bool* outHasAtLeastOnePlayerOnTeam) const +{ + bool theHasAtLeastOneActivePlayer = false; + + // Loop through all players on team + for(EntityListType::const_iterator theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) + { + const AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); + ASSERT(thePlayer); + + if(outHasAtLeastOnePlayerOnTeam) + { + *outHasAtLeastOnePlayerOnTeam = true; + } + + if((thePlayer->IsAlive() && !thePlayer->GetIsBeingDigested()) || (thePlayer->GetUser3() == AVH_USER3_COMMANDER_PLAYER)) + { + theHasAtLeastOneActivePlayer = true; + break; + } + } + + return theHasAtLeastOneActivePlayer; +} + +bool AvHTeam::GetHasTeamLost() const +{ + bool theTeamHasLost = false; + + bool theHasAtLeastOnePlayerOnTeam = false; + bool theHasAtLeastOneActivePlayer = this->GetHasAtLeastOneActivePlayer(&theHasAtLeastOnePlayerOnTeam); + + if(GetGameRules()->GetIsCombatMode()) + { + if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + if((this->GetNumActiveHives() == 0) || !theHasAtLeastOnePlayerOnTeam) + { + theTeamHasLost = true; + } + } + else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) + { + if((this->GetNumBuiltCommandStations() == 0) || !theHasAtLeastOnePlayerOnTeam) + { + theTeamHasLost = true; + } + } + } + else + { + // We need at least one alive player, OR, at the ability to respawn + bool theHasAbilityToRespawn = this->GetTeamHasAbilityToRespawn(); + if((!theHasAtLeastOneActivePlayer && !theHasAbilityToRespawn) || !theHasAtLeastOnePlayerOnTeam) + { + theTeamHasLost = true; + } + } + + if(theTeamHasLost) + { + //UTIL_LogPrintf("Team %d has lost. theAtLeastOneAlivePlayer: %d, theAbilityToRespawn: %d, theHasAtLeastOnePlayerOnTeam: %d\n", this->mTeamNumber, theHasAtLeastOneAlivePlayer, theHasAbilityToRespawn, theHasAtLeastOnePlayerOnTeam); + UTIL_LogPrintf("Team %d has lost.\n", this->mTeamNumber); + + //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + // UTIL_LogPrintf(" Victory player info: ent: %d, team: %d, role: %d, playmode: %d\n", theEntity->entindex(), theEntity->pev->team, (int)theEntity->GetRole(), (int)theEntity->GetPlayMode()); + //END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + } + + return theTeamHasLost; +} + +HiveInfoListType AvHTeam::GetHiveInfoList() const +{ + return this->mHiveInfoList; +} + + +const AvHResearchManager& AvHTeam::GetResearchManager() const +{ + return this->mResearchManager; +} + +AvHResearchManager& AvHTeam::GetResearchManager() +{ + return this->mResearchManager; +} + +float AvHTeam::GetTotalResourcesGathered() const +{ + return this->mTotalResourcesGathered; +} + +void AvHTeam::AddResourcesGathered(float inResources) +{ + this->mTotalResourcesGathered += inResources; +} + +bool AvHTeam::GetIsPlayerOnTeam(int inPlayerIndex) const +{ + bool theSuccess = false; + + EntityListType::const_iterator theIter = std::find(this->mPlayerList.begin(), this->mPlayerList .end(), inPlayerIndex); + if(theIter != this->mPlayerList.end()) + { + theSuccess = true; + } + + return theSuccess; +} + +AvHPlayer* AvHTeam::GetPlayerFromIndex(int inPlayerIndex) +{ + AvHPlayer* thePlayer = NULL; + + CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex)); + thePlayer = dynamic_cast(theBaseEntity); + + return thePlayer; +} + +const AvHPlayer* AvHTeam::GetPlayerFromIndex(int inPlayerIndex) const +{ + const AvHPlayer* thePlayer = NULL; + const CBaseEntity* theBaseEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex)); + thePlayer = dynamic_cast(theBaseEntity); + + return thePlayer; +} + +void AvHTeam::GetOrders(OrderListType& outOrderList) const +{ + outOrderList = this->mOrderList; +} + +bool AvHTeam::GetDoesPlayerHaveOrder(int inPlayerIndex) +{ + bool thePlayerHasOrders = false; + + for(OrderListType::iterator theIter = this->mOrderList.begin(); theIter != this->mOrderList.end(); theIter++) + { + EntityInfo theReceiver = theIter->GetReceiver(); + if(theReceiver == inPlayerIndex) + { + thePlayerHasOrders = true; + } + } + + return thePlayerHasOrders; +} + +//void AvHTeam::GetPlayersCompletingOrders(const EntityListType& outPlayerList) const +//{ +// outPlayerList = this->mPlayersCompletingOrder; +//} + +void AvHTeam::SetOrder(const AvHOrder& inOrder) +{ + AvHChangeOrder(this->mOrderList, inOrder); +} + +void AvHTeam::AddTechNode(AvHMessageID inMessageID, AvHTechID inTechID, AvHTechID inPrereq1, AvHTechID inPrereq2, bool inAllowMultiples, bool inResearched) +{ + int theCost = GetGameRules()->GetCostForMessageID(inMessageID); + int theBuildTime = GetGameRules()->GetBuildTimeForMessageID(inMessageID); + + this->mResearchManager.AddTechNode(inMessageID, inTechID, inPrereq1, inPrereq2, theCost, theBuildTime, inResearched, inAllowMultiples); +} + +void AvHTeam::InitializeTechNodes() +{ + // Clear them first + this->mResearchManager.Reset(); + //this->mUpgradeCosts.clear(); + this->mTechSlotManager.Clear(); + + // Depending on game mode, set tech tree + + if(GetGameRules()->GetIsCombatMode()) + { + this->InitializeCombatTechNodes(); + } + else + { + // else regular NS + if(this->mTeamType == AVH_CLASS_TYPE_MARINE) + { + this->AddTechNode(MESSAGE_NULL, TECH_COMMAND_CENTER, TECH_NULL, TECH_NULL, true, true); + this->AddTechNode(BUILD_RECYCLE, TECH_NULL, TECH_NULL); + this->AddTechNode(BUILD_COMMANDSTATION, TECH_NULL, TECH_NULL); + + this->AddTechNode(BUILD_INFANTRYPORTAL, TECH_INFANTRYPORTAL, TECH_COMMAND_CENTER); + //this->AddTechNode(RESEARCH_FASTER_REINFORCEMENTS, TECH_RESEARCH_FASTER_REINFORCEMENTS, TECH_INFANTRYPORTAL, TECH_OBSERVATORY, false, false); + + this->AddTechNode(MESSAGE_CANCEL, TECH_NULL, TECH_NULL, TECH_NULL, true); + + this->AddTechNode(BUILD_AMMO, TECH_NULL, TECH_NULL); + this->AddTechNode(BUILD_HEALTH, TECH_NULL, TECH_NULL); + this->AddTechNode(BUILD_CAT, TECH_NULL, TECH_RESEARCH_CATALYSTS, TECH_NULL, true, false); + this->AddTechNode(BUILD_RESOURCES, TECH_COMMAND_CENTER, TECH_NULL, TECH_NULL, true, false); + this->AddTechNode(BUILD_HEALTH, TECH_COMMAND_CENTER, TECH_NULL); + + // CC + //this->AddTechNode(RESEARCH_HEALTH, TECH_RESEARCH_HEALTH, TECH_COMMAND_CENTER, TECH_NULL, false); + + // Turret factory route + this->AddTechNode(BUILD_TURRET_FACTORY, TECH_TURRET_FACTORY); + this->AddTechNode(BUILD_TURRET, TECH_NULL, TECH_TURRET_FACTORY); + this->AddTechNode(RESEARCH_ELECTRICAL, TECH_RESEARCH_ELECTRICAL, TECH_TURRET_FACTORY, TECH_NULL, true); + + this->AddTechNode(TURRET_FACTORY_UPGRADE, TECH_ADVANCED_TURRET_FACTORY, TECH_TURRET_FACTORY, TECH_NULL, true); + this->AddTechNode(BUILD_SIEGE, TECH_NULL, TECH_ADVANCED_TURRET_FACTORY); + + // Arms lab + this->AddTechNode(BUILD_ARMSLAB, TECH_ARMSLAB, TECH_ARMORY); + + this->AddTechNode(RESEARCH_ARMOR_ONE, TECH_RESEARCH_ARMOR_ONE, TECH_ARMSLAB, TECH_NULL, false); + this->AddTechNode(RESEARCH_ARMOR_TWO, TECH_RESEARCH_ARMOR_TWO, TECH_RESEARCH_ARMOR_ONE, TECH_NULL, false); + this->AddTechNode(RESEARCH_ARMOR_THREE, TECH_RESEARCH_ARMOR_THREE, TECH_RESEARCH_ARMOR_TWO, TECH_NULL, false); + + this->AddTechNode(RESEARCH_WEAPONS_ONE, TECH_RESEARCH_WEAPONS_ONE, TECH_ARMSLAB, TECH_NULL, false); + this->AddTechNode(RESEARCH_WEAPONS_TWO, TECH_RESEARCH_WEAPONS_TWO, TECH_RESEARCH_WEAPONS_ONE, TECH_NULL, false); + this->AddTechNode(RESEARCH_WEAPONS_THREE, TECH_RESEARCH_WEAPONS_THREE, TECH_RESEARCH_WEAPONS_TWO, TECH_NULL, false); + this->AddTechNode(RESEARCH_CATALYSTS, TECH_RESEARCH_CATALYSTS, TECH_ARMSLAB, TECH_NULL, false, false); + + this->AddTechNode(RESEARCH_HEAVYARMOR, TECH_RESEARCH_HEAVYARMOR, TECH_PROTOTYPE_LAB, TECH_NULL, false); + + // Prototype lab + this->AddTechNode(BUILD_PROTOTYPE_LAB, TECH_PROTOTYPE_LAB, TECH_ARMSLAB, TECH_ADVANCED_ARMORY); + this->AddTechNode(RESEARCH_JETPACKS, TECH_RESEARCH_JETPACKS, TECH_PROTOTYPE_LAB, TECH_NULL, false, false); + + // Armor add-ons + this->AddTechNode(BUILD_JETPACK, TECH_NULL, TECH_RESEARCH_JETPACKS, TECH_PROTOTYPE_LAB); + this->AddTechNode(BUILD_HEAVY, TECH_NULL, TECH_RESEARCH_HEAVYARMOR, TECH_PROTOTYPE_LAB); + + // Weapon factory route + this->AddTechNode(BUILD_ARMORY, TECH_ARMORY); + this->AddTechNode(ARMORY_UPGRADE, TECH_ADVANCED_ARMORY, TECH_ARMORY); + this->AddTechNode(RESEARCH_GRENADES, TECH_RESEARCH_GRENADES, TECH_ARMORY, TECH_NULL, false, false); + //this->AddTechNode(BUILD_NUKE_PLANT, TECH_NUKE_PLANT, TECH_ADVANCED_ARMORY); + + // Observatory/knowledge/phase gate route + this->AddTechNode(BUILD_OBSERVATORY, TECH_OBSERVATORY, TECH_ARMORY); + this->AddTechNode(BUILD_SCAN, TECH_NULL, TECH_OBSERVATORY); + this->AddTechNode(RESEARCH_MOTIONTRACK, TECH_RESEARCH_MOTIONTRACK, TECH_OBSERVATORY, TECH_NULL, false); + this->AddTechNode(RESEARCH_DISTRESSBEACON, TECH_RESEARCH_DISTRESSBEACON, TECH_OBSERVATORY, TECH_NULL); + this->AddTechNode(RESEARCH_PHASETECH, TECH_RESEARCH_PHASETECH, TECH_INFANTRYPORTAL, TECH_OBSERVATORY, false); + this->AddTechNode(BUILD_PHASEGATE, TECH_NULL, TECH_OBSERVATORY, TECH_RESEARCH_PHASETECH); + + // Weapons + this->AddTechNode(BUILD_SHOTGUN, TECH_NULL, TECH_ARMORY); + this->AddTechNode(BUILD_WELDER, TECH_NULL, TECH_ARMORY); + this->AddTechNode(BUILD_MINES, TECH_NULL, TECH_ARMORY); + + this->AddTechNode(BUILD_HMG, TECH_NULL, TECH_ADVANCED_ARMORY); + this->AddTechNode(BUILD_GRENADE_GUN, TECH_NULL, TECH_ADVANCED_ARMORY, TECH_NULL); + //this->AddTechNode(BUILD_NUKE, TECH_NULL, TECH_NUKE_PLANT, TECH_ADVANCED_ARMORY); + + // Specials + //this->AddTechNode(BUILD_MEDKIT, TECH_NULL, TECH_MEDLAB); + + // Add tech to allow resource adjustment (allow it to be researched multiple times) + //this->AddTechNode(RESOURCE_UPGRADE, TECH_NULL, TECH_NULL, TECH_NULL); + + // Initialize tech slots for marines + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_COMMANDER_STATION, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_TURRET_FACTORY, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, TURRET_FACTORY_UPGRADE, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ADVANCED_TURRET_FACTORY, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ARMORY, ARMORY_UPGRADE, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_GRENADES, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ADVANCED_ARMORY, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_GRENADES, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_ARMSLAB, RESEARCH_WEAPONS_ONE, RESEARCH_WEAPONS_TWO, RESEARCH_WEAPONS_THREE, RESEARCH_CATALYSTS, RESEARCH_ARMOR_ONE, RESEARCH_ARMOR_TWO, RESEARCH_ARMOR_THREE, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_PROTOTYPE_LAB, RESEARCH_JETPACKS, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_HEAVYARMOR, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_OBSERVATORY, BUILD_SCAN, RESEARCH_PHASETECH, MESSAGE_NULL, MESSAGE_NULL, RESEARCH_DISTRESSBEACON, RESEARCH_MOTIONTRACK, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_TURRET, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_SIEGETURRET, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_RESTOWER, RESEARCH_ELECTRICAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_INFANTRYPORTAL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_PHASEGATE, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, MESSAGE_NULL, BUILD_RECYCLE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MARINE_PLAYER)); + + // Initialize tech slots for top-level menus. Note that the data for these four menus is stored in the command station iuser1 + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_BUILD, BUILD_RESOURCES, BUILD_INFANTRYPORTAL, BUILD_ARMORY, BUILD_COMMANDSTATION, BUILD_TURRET_FACTORY, BUILD_TURRET, BUILD_SIEGE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_BUILD_ADVANCED, BUILD_OBSERVATORY, BUILD_ARMSLAB, BUILD_PROTOTYPE_LAB, BUILD_PHASEGATE)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_ASSIST, BUILD_AMMO, BUILD_HEALTH, BUILD_CAT)); + this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_EQUIP, BUILD_MINES, BUILD_SHOTGUN, BUILD_HMG, BUILD_GRENADE_GUN, BUILD_WELDER, BUILD_JETPACK, BUILD_HEAVY)); + //this->mTechSlotManager.AddTechSlots(AvHTechSlots(AVH_USER3_MENU_ORDERS, BUILD_HEALTH)); + } + else + { + // These upgrades are per alien + this->AddTechNode(ALIEN_LIFEFORM_ONE, TECH_SKULK, TECH_NULL, TECH_NULL, true, true); + this->AddTechNode(ALIEN_LIFEFORM_TWO, TECH_GORGE, TECH_NULL, TECH_NULL, true, true); + this->AddTechNode(ALIEN_LIFEFORM_THREE, TECH_LERK, TECH_SKULK, TECH_NULL, true, true); + this->AddTechNode(ALIEN_LIFEFORM_FOUR, TECH_FADE, TECH_LERK, TECH_NULL, true, true); + this->AddTechNode(ALIEN_LIFEFORM_FIVE, TECH_ONOS, TECH_FADE, TECH_NULL, true, true); + + // Only gorge can build + this->AddTechNode(ALIEN_BUILD_RESOURCES, TECH_ALIEN_RESOURCE_NODE, TECH_GORGE, TECH_NULL, true, false); + this->AddTechNode(ALIEN_BUILD_HIVE, TECH_HIVE, TECH_GORGE, TECH_NULL, true, false); + this->AddTechNode(ALIEN_BUILD_MOVEMENT_CHAMBER, TECH_MOVEMENT_CHAMBER, TECH_GORGE, TECH_NULL, true, false); + this->AddTechNode(ALIEN_BUILD_DEFENSE_CHAMBER, TECH_DEFENSE_CHAMBER, TECH_GORGE, TECH_NULL, true, false); + this->AddTechNode(ALIEN_BUILD_SENSORY_CHAMBER, TECH_SENSORY_CHAMBER, TECH_GORGE, TECH_NULL, true, false); + this->AddTechNode(ALIEN_BUILD_OFFENSE_CHAMBER, TECH_OFFENSE_CHAMBER, TECH_GORGE, TECH_NULL, true, false); + + // Upgrades + this->AddTechNode(ALIEN_EVOLUTION_ONE, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); + this->AddTechNode(ALIEN_EVOLUTION_TWO, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); + this->AddTechNode(ALIEN_EVOLUTION_THREE, TECH_DEFENSE_CHAMBER, TECH_NULL, TECH_NULL, true, false); + + this->AddTechNode(ALIEN_EVOLUTION_SEVEN, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); + this->AddTechNode(ALIEN_EVOLUTION_EIGHT, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); + this->AddTechNode(ALIEN_EVOLUTION_NINE, TECH_MOVEMENT_CHAMBER, TECH_NULL, TECH_NULL, true, false); + + this->AddTechNode(ALIEN_EVOLUTION_TEN, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); + this->AddTechNode(ALIEN_EVOLUTION_ELEVEN, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); + this->AddTechNode(ALIEN_EVOLUTION_TWELVE, TECH_SENSORY_CHAMBER, TECH_NULL, TECH_NULL, true, false); + } + } +} + +const AvHTechTree& AvHTeam::GetTechNodes() const +{ + return this->mResearchManager.GetTechNodes(); +} + +AvHTechTree& AvHTeam::GetTechNodes() +{ + return this->mResearchManager.GetTechNodes(); +} + +const AvHTechSlotManager& AvHTeam::GetTechSlotManager() const +{ + return this->mTechSlotManager; +} + +AvHTechSlotManager& AvHTeam::GetTechSlotManager() +{ + return this->mTechSlotManager; +} + +// For marine teams, this means a player is trying to vote down the commander +bool AvHTeam::PlayerVote(int inPlayerIndex, string& outPlayerMessage) +{ + bool theSuccess = false; + + // If we are a marine team and we have a commander + if((this->GetTeamType() == AVH_CLASS_TYPE_MARINE) && (this->GetCommander() != -1) && GetGameRules()->GetGameStarted()) + { + // If player is allowed to vote (can only vote every x minutes) + EntityListType::iterator theIterator = std::find(this->mVotingPlayers.begin(), this->mVotingPlayers.end(), inPlayerIndex); + if(theIterator == this->mVotingPlayers.end()) + { + AvHPlayer* theVotingPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex))); + if(theVotingPlayer) + { + theVotingPlayer->SendMessage(kVoteCast); + + // Increment vote + this->mVotes++; + this->mVotingPlayers.push_back(inPlayerIndex); + theSuccess = true; + + AvHPlayer* theCommander = GetCommanderPlayer(); + if(theCommander) + { + theCommander->LogPlayerActionPlayer(theVotingPlayer, "votedown"); + } + } + } + else + { + // Display message that player has already voted + outPlayerMessage = kAlreadyVoted; + } + } + else + { + outPlayerMessage = kVoteIllegal; + } + + return theSuccess; +} + +void AvHTeam::PlayFunHUDSoundOnce(AvHHUDSound inHUDSound) +{ + if(std::find(this->mFunSoundsPlayed.begin(), this->mFunSoundsPlayed.end(), inHUDSound) == this->mFunSoundsPlayed.end()) + { + this->PlayHUDSoundForAlivePlayers(inHUDSound); + this->mFunSoundsPlayed.push_back(inHUDSound); + } +} + +void AvHTeam::PlayHUDSoundForAlivePlayers(AvHHUDSound inHUDSound) const +{ + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetIsRelevant() && (theEntity->pev->team == this->mTeamNumber)) + { + theEntity->PlayHUDSound(inHUDSound); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); +} + +// : Bug 0000767 +void AvHTeam::PlayHUDSoundForAllPlayers(AvHHUDSound inHUDSound) const +{ + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + theEntity->PlayHUDSound(inHUDSound); + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); +} +// : + + +// Assumes rank change has been approved from on high. It just does the best job it can, +// it doesn't change any other ranks than the one indicated +bool AvHTeam::ProcessRankChange(AvHPlayer* inPlayer, AvHUser3 inOldUser3, AvHUser3 inNewUser3) +{ + bool theSuccess = false; + + // Someone just stepped up or down, change the player's squad + int thePlayerIndex = inPlayer->entindex(); + + // if player is now a soldier + if(inNewUser3 == AVH_USER3_MARINE_PLAYER) + { + if(this->GetCommander() == thePlayerIndex) + { + this->SetCommander(-1); + } + } + // else if player is now commander + else if(inNewUser3 == AVH_USER3_COMMANDER_PLAYER) + { + // Set him as commander + this->SetCommander(thePlayerIndex); + + theSuccess = true; + } + + return theSuccess; +} + +void AvHTeam::ResetGame() +{ + AvHTeamNumber theTeamNumber = this->mTeamNumber; + AvHClassType theTeamType = this->mTeamType; + + this->Init(); + + this->mTeamNumber = theTeamNumber; + this->mTeamType = theTeamType; + + this->mLastInjectionTime = gpGlobals->time; + this->mLastResourceUpdateTime = -1; + this->mLastCommandScoreUpdateTime = -1; + this->mStartingLocation.x = this->mStartingLocation.y = this->mStartingLocation.z = 0; + this->mNumBuiltCommandStations = 0; + this->mNumActiveHives = 0; + + if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + // Spawn the initial hives + int theNumHives = GetGameRules()->GetGameplay().GetInitialHives(); + for(int i = 0; i < theNumHives; i++) + { + this->SpawnHive(&this->mStartingLocation, true); + } + + if(GetGameRules()->GetIsCombatMode()) + { + for(int i= 0; i < 3; i++) + { + this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_DEFENSE); + this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_MOVEMENT); + this->AddTeamUpgrade(ALIEN_UPGRADE_CATEGORY_SENSORY); + } + } + } + + if(!GetGameRules()->GetIsCombatMode()) + { + // Put down command station + FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) + if(theEntity->pev->team == (int)(this->mTeamNumber)) + { + theEntity->SetConstructionComplete(true); + DROP_TO_FLOOR(ENT(theEntity->pev)); + //PLAYBACK_EVENT_FULL(0, theEntity->edict(), gPhaseInEventID, 0, theEntity->pev->origin, (float *)&g_vecZero, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + VectorCopy(theEntity->pev->origin, this->mStartingLocation); + } + END_FOR_ALL_ENTITIES(kwsTeamCommand) + + if(this->mTeamType == AVH_CLASS_TYPE_MARINE) + { + this->SetTeamResources(GetGameRules()->GetGameplay().GetInitialMarinePoints()); + } + // else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + // { + // this->SetTeamResources(GetGameRules()->GetGameplay().GetInitialAlienPoints()); + // } + + this->SpawnResourceTower(); + } + + this->mOrderList.clear(); + + this->ResetServerPlayerData(); + //this->InitializeRandomResourceShares(); + + this->mHiveInfoList.clear(); + this->mTimeOfLastStructureUpdate = -1; + + this->mVoteStarted = false; + this->mPreviousVotes = this->mVotes = 0; + this->mVotingPlayers.clear(); + this->mTimeVoteStarted = -1; + this->mVotesNeeded = 0; + + + this->mLowResourceAlerts.clear(); + this->mCommandersToldAboutJumpKey.clear(); + this->mFunSoundsPlayed.clear(); + this->mPlayedSeeDeadPeople = false; + + int theEasterEggChance = max(0.0f, avh_eastereggchance.value); + this->mPlayFunSoundsThisGame = (RANDOM_LONG(0, theEasterEggChance) == 0); + + // Don't give hints right away + this->mTimeOfLastTeamHint = gpGlobals->time; + this->mTimeLastHintUpdate = -1; + + // Reset group info + for(int i = 0; i < kNumHotkeyGroups; i++) + { + this->mGroups[i].clear(); + this->mGroupTypes[i] = AVH_USER3_NONE; + } + this->mSelectAllGroup.clear(); +} + +void AvHTeam::SpawnResourceTower() +{ + string theResourceEntity; + AvHMessageID theResourceEntityMessageID = MESSAGE_NULL; + + if(this->mTeamType == AVH_CLASS_TYPE_MARINE) + { + theResourceEntity = kwsResourceTower; + theResourceEntityMessageID = BUILD_RESOURCES; + } + else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + theResourceEntity = kwsAlienResourceTower; + theResourceEntityMessageID = ALIEN_BUILD_RESOURCES; + } + + // Create a resource tower for the team, nearest their spawn + Vector theLocationToBuild; + + // Look for func resource nearest to team start + CBaseEntity* theNearestFuncResource = NULL; + float theNearestDistance = -1; + + FOR_ALL_ENTITIES(kesFuncResource, AvHFuncResource*) + if(!theEntity->GetIsOccupied()) + { + float theDistance = VectorDistance(this->mStartingLocation, theEntity->pev->origin); + if((theNearestDistance == -1) || (theDistance < theNearestDistance)) + { + theNearestFuncResource = theEntity; + theNearestDistance = theDistance; + } + } + END_FOR_ALL_ENTITIES(kesFuncResource); + + if(theNearestFuncResource) + { + Vector theLocation = theNearestFuncResource->pev->origin; + theLocation.z += 35; + + CBaseEntity* theBaseEntity = CBaseEntity::Create(theResourceEntity.c_str(), theLocation, AvHSUGetRandomBuildingAngles()); + + theBaseEntity->pev->team = this->mTeamNumber; + + AvHSUBuildingJustCreated(theResourceEntityMessageID, theBaseEntity, NULL); + + // Set it complete immediately. If not, aliens are slowed down noticeably because they need builders to tower building + AvHResourceTower* theResourceTower = dynamic_cast(theBaseEntity); + ASSERT(theResourceTower); + // The first resource tower is active immediately + theResourceTower->SetActivateTime(0); + + //DROP_TO_FLOOR(ENT(theBaseBuildable->pev)); + theResourceTower->SetConstructionComplete(true); + } +} + +// Delete any server player data that has expired +void AvHTeam::ResetServerPlayerData() +{ + // Run through list + for(AvHServerPlayerDataListType::iterator theIter = this->mServerPlayerData.begin(); theIter != this->mServerPlayerData.end(); /* no inc */) + { + // Delete it unless the player has been voted down + if(theIter->second.GetTimeVotedDown() == -1) + { + AvHServerPlayerDataListType::iterator theTempIter = theIter; + theTempIter++; + this->mServerPlayerData.erase(theIter); + theIter = theTempIter; + } + else + { + theIter++; + } + } +} + +int AvHTeam::GetDesiredSpawnCount() const +{ + int theDesiredSpawns = 0; + + if(this->mTeamType == AVH_CLASS_TYPE_MARINE) + { + // 16 for the marine start + theDesiredSpawns = 16; + } + else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + // 16 for each hive + theDesiredSpawns = 16*3; + } + + return theDesiredSpawns; +} + +float AvHTeam::GetInitialPlayerPoints(edict_t* inEdict) +{ + float theInitialPoints = 0; + + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(inEdict)); + ASSERT(thePlayer); + + // Marines don't get any individual points, aliens get random amount + // Use WONID to make sure player doesn't try to get more points by quitting and rejoining + if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + if(GetGameRules()->GetArePlayersAllowedToJoinImmediately() /*&& !thePlayer->GetHasLeftReadyRoom()*/) + { + theInitialPoints = GetGameRules()->GetGameplay().GetInitialAlienPoints(); + } + + // If WON id already exists (because we already gave points out to that player) + AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(inEdict); + if(theServerPlayerData && (theServerPlayerData->GetResources() != -1)) + { + // Give them the number of points they last had + theInitialPoints = theServerPlayerData->GetResources(); + } + } + + return theInitialPoints; +} + +float AvHTeam::GetInitialExperience(edict_t* inEdict) +{ + float theInitialExperience = 0.0f; + + //AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(inEdict); + //if(theServerPlayerData && (theServerPlayerData->GetExperience() > 0.0f)) + //{ + // // Give them experience they last had + // theInitialExperience = theServerPlayerData->GetExperience(); + //} + + return theInitialExperience; +} + +void AvHTeam::SetGameStarted(bool inGameStarted) +{ + if(inGameStarted) + { + if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + this->mLastHiveSpawnTime = gpGlobals->time; + } + this->mLastInjectionTime = gpGlobals->time; + + //#ifndef DEBUG + // If team is alien, give them all game started hud sound + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->pev->team == this->mTeamNumber) + { + theEntity->PlayHUDSound(HUD_SOUND_GAMESTART); + + // Send Combat objective + if(GetGameRules()->GetIsCombatMode()) + { + string theMessage; + if(GetGameRules()->GetCombatAttackingTeamNumber() == this->mTeamNumber) + { + theMessage = kYouAreAttacking; + + } + else + { + theMessage = kYouAreDefending; + } + + theEntity->SendMessage(theMessage.c_str(), NORMAL); + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + //#endif + } +} + +void AvHTeam::KillCS() +{ + AvHCommandStation* theCS = NULL; + + FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) + if(theEntity->pev->health > 0) + { + theEntity->TakeDamage(theEntity->pev, theEntity->pev, 20000, DMG_GENERIC); + } + END_FOR_ALL_ENTITIES(kwsTeamCommand) +} + +void AvHTeam::KillHive() +{ + if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + AvHHive* theHive = AvHSUGetRandomActiveHive(this->mTeamNumber); + if(theHive) + { + theHive->Killed(NULL, 0); + } + } +} + +void AvHTeam::SpawnHive(Vector* outLocation, bool inForce) +{ + if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + AvHHive* theHive = AvHSUGetRandomActivateableHive(this->mTeamNumber); + if(theHive) + { + if(theHive->StartSpawningForTeam(this->mTeamNumber, inForce)) + { + theHive->SetConstructionComplete(true); + this->mNumActiveHives++; + + if(outLocation) + { + VectorCopy(theHive->pev->origin, *outLocation); + } + } + else + { + ALERT(at_console, "Error spawning hive. Is it blocked by something?\n"); + } + } + } +} + +int AvHTeam::GetCommander() const +{ + return this->mCommander; +} + +AvHPlayer* AvHTeam::GetCommanderPlayer() +{ + AvHPlayer* theCommanderPlayer = NULL; + + if(this->mCommander > 0) + { + theCommanderPlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCommander))); + } + + return theCommanderPlayer; +} + + +AvHTeamNumber AvHTeam::GetTeamNumber(void) const +{ + return this->mTeamNumber; +} + +void AvHTeam::SetTeamNumber(AvHTeamNumber inTeamNumber) +{ + this->mTeamNumber = inTeamNumber; +} + +AvHClassType AvHTeam::GetTeamType(void) const +{ + return this->mTeamType; +} + +const char* AvHTeam::GetTeamTypeString(void) const +{ + const char* theTeamString = "Unknown"; + + switch(this->mTeamType) + { + case AVH_CLASS_TYPE_MARINE: + theTeamString = "marine"; + break; + case AVH_CLASS_TYPE_ALIEN: + theTeamString = "alien"; + break; + } + + return theTeamString; +} + +void AvHTeam::AddTeamUpgrade(AvHAlienUpgradeCategory inCategory) +{ + int theNumUpgradeCategories = AvHGetNumUpgradesInCategoryInList(this->mAlienUpgrades, inCategory); + + // If we don't have an upgrade of this type, trigger an alert + if(theNumUpgradeCategories == 0) + { + GetGameRules()->TriggerAlert(this->mTeamNumber, ALERT_NEW_TRAIT, -1); + } + + this->mAlienUpgrades.push_back(inCategory); +} + +AvHAlienUpgradeListType AvHTeam::GetAlienUpgrades() const +{ + return this->mAlienUpgrades; +} + +bool AvHTeam::RemoveAlienUpgradeCategory(AvHAlienUpgradeCategory inCategory) +{ + bool theRemoved = AvHRemoveUpgradeCategory(inCategory, this->mAlienUpgrades); + return theRemoved; +} + +int AvHTeam::GetTeamWideUpgrades() const +{ + return this->mTeamWideUpgrades; +} + +void AvHTeam::ProcessTeamUpgrade(AvHMessageID inMessageID, bool inGive) +{ + ProcessGenericUpgrade(this->mTeamWideUpgrades, inMessageID, inGive); +} + +void AvHTeam::InitializeRandomResourceShares() +{ + this->mRandomResourceShares.clear(); + this->mTotalShareWeight = 0; + + // Populate random resource shares with random value (like cards in Bridge) + for(int i = 0; i < kTotalShares; i++) + { + const int kSlope = 2; + int theBoundaryOne = kTotalShares/3; + int theBoundaryTwo = (2*kTotalShares)/3; + int theCardValue = 1; + + if((i > theBoundaryOne) && (i < theBoundaryTwo)) + { + theCardValue = 2*kSlope; + } + else if(i > theBoundaryTwo) + { + theCardValue = 3*kSlope; + } + + this->mRandomResourceShares.push_back(theCardValue); + this->mTotalShareWeight += theCardValue; + } +} + +float AvHTeam::WithdrawPointsViaRandomShares(int inPlayerIndex) +{ + int theNumSharesLeft = this->mRandomResourceShares.size(); + ASSERT(theNumSharesLeft >= kNumSharesPerPlayer); + + // Pick some random shares and add them up + int theRandomShares = 0; + + for(int i = 0; i < kNumSharesPerPlayer; i++) + { + // Pick a random "card", add it's shares, remove card from pool + int theRandomNumber = g_engfuncs.pfnRandomLong(0, this->mRandomResourceShares.size() - 1); + RandomResourceShareListType::iterator theIter = this->mRandomResourceShares.begin() + theRandomNumber; + + theRandomShares = theRandomShares + *theIter; + + // Remember which "cards" we've given this player so we can add them back in later + this->mPlayerShares[inPlayerIndex].push_back(*theIter); + + this->mRandomResourceShares.erase(theIter); + } + + // Return this percentage of the team resources for this player + float thePercentage = theRandomShares/(float)this->mTotalShareWeight; + + float theInitialPoints = thePercentage*this->GetTeamResources(); + this->SetTeamResources(this->GetTeamResources() - theInitialPoints); + + return theInitialPoints; +} + +void AvHTeam::SetTeamType(AvHClassType inType) +{ + this->mTeamType = inType; + + switch(inType) + { + case AVH_CLASS_TYPE_MARINE: + if( this->GetTeamNumber() == TEAM_ONE ) + { this->mTeamName = kMarine1Team; } + else + { this->mTeamName = kMarine2Team; } + this->mTeamPrettyName = kMarinePrettyName; + break; + case AVH_CLASS_TYPE_ALIEN: + if( this->GetTeamNumber() == TEAM_TWO ) + { this->mTeamName = kAlien1Team; } + else + { this->mTeamName = kAlien2Team; } + this->mTeamPrettyName = kAlienPrettyName; + break; + default: + this->mTeamName = kUndefinedTeam; + this->mTeamPrettyName = kUndefPrettyName; + break; + } + + // Make sure team names aren't longer than MAX_TEAM_NAME + ASSERT(this->mTeamName.length() < MAX_TEAM_NAME); +} + +int AvHTeam::GetNumWebStrands() const +{ + return this->mNumWebStrands; +} + +void AvHTeam::SetNumWebStrands(int inNumWebStrands) +{ + this->mNumWebStrands = inNumWebStrands; +} + +float AvHTeam::GetTeamResources() const +{ + return this->mTeamResources; +} + +void AvHTeam::SetTeamResources(float inTeamResources) +{ + this->mTeamResources = inTeamResources; +} + +float AvHTeam::GetHandicap() const +{ + return this->mHandicap; +} + +void AvHTeam::SetHandicap(float inHandicap) +{ + this->mHandicap = inHandicap; +} + +const char* AvHTeam::GetTeamName(void) const +{ + return this->mTeamName.c_str(); +} + +const char* AvHTeam::GetTeamPrettyName(void) const +{ + return this->mTeamPrettyName.c_str(); +} + +void AvHTeam::SetCommander(int inPlayerNumber) +{ + if(inPlayerNumber != this->mCommander) + { + // Reset commander hints + this->mTimeOfLastTeamHint = -1; + + this->mCommander = inPlayerNumber; + } +} + +void AvHTeam::SetTeamName(const char* inTeamName) +{ + this->mTeamName = inTeamName; +} + +void AvHTeam::SetTeamPrettyName(const char* inTeamName) +{ + this->mTeamPrettyName = inTeamName; +} + +int AvHTeam::GetPlayerCount(bool inCountOnlyDead) const +{ + int theNumPlayers = this->mPlayerList.size(); + + if(inCountOnlyDead) + { + theNumPlayers = 0; + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if((AvHTeamNumber)theEntity->pev->team == this->mTeamNumber) + { + AvHPlayer* thePlayer = dynamic_cast(theEntity); + ASSERT(thePlayer); + + if(!thePlayer->IsAlive()) + { + theNumPlayers++; + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + } + + return theNumPlayers; +} + +bool AvHTeam::RemovePlayer(int inPlayerIndex) +{ + bool theSuccess = false; + + if(this->GetIsPlayerOnTeam(inPlayerIndex)) + { + // Remove player + EntityListType::iterator thePlayerIter = std::find(this->mPlayerList.begin(), this->mPlayerList .end(), inPlayerIndex); + if(thePlayerIter != this->mPlayerList.end()) + { + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(inPlayerIndex))); + ASSERT(thePlayer); + + // Add cards back into pool +// RandomResourceShareListType& theVector = this->mPlayerShares[inPlayerIndex]; +// for(RandomResourceShareListType::iterator theCardIter = theVector.begin(); theCardIter != theVector.end(); theCardIter++) +// { +// this->mRandomResourceShares.push_back(*theCardIter); +// } + + // Make sure no entities have this player as an owner + edict_t* thePlayerEdict = thePlayer->edict(); + FOR_ALL_BASEENTITIES(); + if(theBaseEntity->pev->owner == thePlayerEdict) + { + theBaseEntity->pev->owner = NULL; + } + + AvHBuildable* theBuildable = dynamic_cast(theBaseEntity); + if(theBuildable && (theBuildable->GetBuilder() == thePlayer->entindex())) + { + theBuildable->SetBuilder(-1); + } + END_FOR_ALL_BASEENTITIES(); + + AvHSURemoveEntityFromHotgroupsAndSelection(inPlayerIndex); + +// theVector.clear(); + + this->mPlayerList.erase(thePlayerIter); + + theSuccess = true; + } + } + + if(this->mCommander == inPlayerIndex) + { + this->SetCommander(-1); + } + + return theSuccess; +} + +void AvHTeam::TriggerAddTech(AvHTechID inTechID) +{ + if(!GetGameRules()->GetIsCombatMode()) + { + this->mResearchManager.TriggerAddTech(inTechID); + + // Get list of completed research that depended on this tech + TechNodeMap nodes = this->mResearchManager.GetResearchNodesDependentOn(inTechID); + + // For all researched nodes + TechNodeMap::const_iterator current, end = nodes.end(); + for( current = nodes.begin(); current != end; ++current ) + { + if( current->second->getIsResearched() ) + { + GetGameRules()->ProcessTeamUpgrade( current->first, this->mTeamNumber, 0, true ); + } + } + } +} + +void AvHTeam::TriggerRemoveTech(AvHTechID inTechID) +{ + if(!GetGameRules()->GetIsCombatMode()) + { + // Run through world, looking for anything alive that could still be giving us this tech + bool theFoundActiveTech = false; + + FOR_ALL_BASEENTITIES(); + AvHBuildable* theBuildable = dynamic_cast(theBaseEntity); + if(theBuildable) + { + if((theBuildable->GetSupportsTechID(inTechID)) && (theBuildable->GetTeamNumber() == this->mTeamNumber)) + { + if(theBuildable->GetIsTechActive()) + { + theFoundActiveTech = true; + break; + } + } + } + END_FOR_ALL_BASEENTITIES(); + + if(!theFoundActiveTech) + { + // If we can't find anything, remove it from the tech nodes + this->mResearchManager.TriggerRemoveTech(inTechID); + + // Get list of completed research that depended on this tech + TechNodeMap nodes = this->mResearchManager.GetResearchNodesDependentOn(inTechID); + + // For all researched nodes + TechNodeMap::iterator current, end = nodes.end(); + for( current = nodes.begin(); current != end; ++current ) + { + if( current->second->getIsResearched() ) + { + // Disable because of loss of this tech and remove them + GetGameRules()->ProcessTeamUpgrade( current->first, this->mTeamNumber, 0, false ); + } + } + } + } +} + +void AvHTeam::Update() +{ + PROFILE_START() + this->UpdateHints(); + PROFILE_END(kAvHTeamUpdateHints) + + PROFILE_START() + this->UpdateInventoryEnabledState(); + PROFILE_END(kAvHTeamUpdateInventoryEnabledState) + + PROFILE_START() + this->UpdateReinforcementWave(); + PROFILE_END(kAvHTeamUpdateReinforcementWave) + + PROFILE_START() + this->UpdateOrders(); + PROFILE_END(kAvHTeamUpdateOrders) + + PROFILE_START() + this->UpdateResearch(); + PROFILE_END(kAvHTeamUpdateResearch) + + PROFILE_START() + this->UpdateResources(); + PROFILE_END(kAvHTeamUpdateResources) + + PROFILE_START() + this->UpdateAlerts(); + PROFILE_END(kAvHTeamUpdateAlerts) + + PROFILE_START() + this->UpdateServerPlayerData(); + PROFILE_END(kAvHTeamUpdateServerPlayerData) + + PROFILE_START() + this->UpdateTeamStructures(); + PROFILE_END(kAvHTeamUpdateTeamStructures) + + PROFILE_START() + this->UpdateVoting(); + PROFILE_END(kAvHTeamUpdateVoting) + + PROFILE_START() + this->UpdatePlayers(); + PROFILE_END(kAvHTeamUpdatePlayers) +} + +void AvHTeam::UpdateTeamStructures() +{ + const float kStructureUpdateRate = 1.0f; + if((this->mTimeOfLastStructureUpdate == -1) || (gpGlobals->time > (this->mTimeOfLastStructureUpdate + kStructureUpdateRate))) + { + this->mTimeOfLastStructureUpdate = gpGlobals->time; + + if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + this->mHiveInfoList.clear(); + + // Search for free hives, or hives on our team + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if((theEntity->pev->team == 0) || ((AvHTeamNumber)theEntity->pev->team == this->mTeamNumber)) + { + AvHHiveInfo theHiveInfo; + + theHiveInfo.mPosX = theEntity->pev->origin.x; + theHiveInfo.mPosY = theEntity->pev->origin.y; + theHiveInfo.mPosZ = theEntity->pev->origin.z; + + if (theEntity->pev->max_health > 0) { + theHiveInfo.mHealthPercentage = (int)(100 * theEntity->pev->health / theEntity->pev->max_health); + } else { + theHiveInfo.mHealthPercentage = 0; + } + + int theEntityIndex = theEntity->entindex(); + + // Fill in hive status + int theStatus = kHiveInfoStatusBuilt; + + // Unbuilt hives + if(theEntity->pev->team == 0) + { + theStatus = kHiveInfoStatusUnbuilt; + } + // building hives + else if(!theEntity->GetIsBuilt()) + { + float theNormalizedBuildPercentage = theEntity->GetNormalizedBuildPercentage(); + if(theNormalizedBuildPercentage > 0.0f) + { + theStatus = kHiveInfoStatusBuildingStage1; + int theSteps = (int)(theNormalizedBuildPercentage*5.0f); + theStatus += theSteps; + } + else + { + theStatus = kHiveInfoStatusUnbuilt; + } + } + + theHiveInfo.mStatus = theStatus; + theHiveInfo.mTechnology = (int)(theEntity->GetTechnology()); + + // Under attack? + theHiveInfo.mUnderAttack = GetGameRules()->GetIsEntityUnderAttack(theEntityIndex); + + // Add onto the end, so we don't resend unnecessarily + this->mHiveInfoList.insert(this->mHiveInfoList.end(), theHiveInfo); + } + END_FOR_ALL_ENTITIES(kesTeamHive); + + // Count number of active hives + int theNumHives = 0; + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + if(theEntity->GetIsActive()) + { + AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); + if(theTeamNumber == this->mTeamNumber) + { + theNumHives++; + } + } + END_FOR_ALL_ENTITIES(kesTeamHive); + this->mNumActiveHives = theNumHives; + } + else if(this->GetTeamType() == AVH_CLASS_TYPE_MARINE) + { + // Count # of command stations + int theNumActiveCommandStations = 0; + FOR_ALL_ENTITIES(kwsTeamCommand, AvHCommandStation*) + if(theEntity->GetIsBuilt()) + { + AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); + if(theTeamNumber == this->mTeamNumber) + { + theNumActiveCommandStations++; + } + } + END_FOR_ALL_ENTITIES(kwsTeamCommand); + this->mNumBuiltCommandStations = theNumActiveCommandStations; + } + } +} + +void AvHTeam::UpdatePlayers() +{ + //FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + // GetGameRules()->ClientCommand(theEntity, ); + //END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + + // Update every once in awhile + const float kPlayerUpdateRate = 2.0f; + if((this->mLastPlayerUpdateTime == -1) || (gpGlobals->time > (this->mLastPlayerUpdateTime + kPlayerUpdateRate))) + { + if(!GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsTournamentMode() && !GetGameRules()->GetIsCombatMode()) + { + // If the team is alien and has no hives left + if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + // Do we have any active hives? + int theNumGrowingOrActiveHives = 0; + FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) + AvHTeamNumber theTeamNumber = (AvHTeamNumber)(theEntity->pev->team); + if(theTeamNumber == this->mTeamNumber) + { + if(theEntity->GetIsActive() || theEntity->GetIsSpawning()) + { + theNumGrowingOrActiveHives++; + } + } + END_FOR_ALL_ENTITIES(kesTeamHive); + + if(theNumGrowingOrActiveHives == 0) + { + CBaseEntity* theWorld = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(0)); + if(theWorld) + { + entvars_t* theInflictor = theWorld->pev; + + // Do some damage to remaining players + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if((theEntity->pev->team == this->mTeamNumber) && theEntity->IsAlive()) + { + AvHPlayer* thePlayer = dynamic_cast(theEntity); + ASSERT(thePlayer); + float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(theEntity->pev->iuser4, theEntity->GetUser3(), thePlayer->GetExperienceLevel()); + float theDamage = theMaxHealth/8; + + theEntity->TakeDamage(theInflictor, theInflictor, theDamage, DMG_DROWN | DMG_IGNOREARMOR); + } + theEntity->PlayHUDSound(HUD_SOUND_COUNTDOWN); + + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + } + } + } + } + + this->mLastPlayerUpdateTime = gpGlobals->time; + } + + // Update team score if we're in tournament mode + this->SendResourcesGatheredScore(true); +} + +bool AvHTeam::SendResourcesGatheredScore(bool inThisTeamOnly) +{ + bool theSentMessage = false; + + if(GetGameRules()->GetIsTournamentMode()) + { + // Only send message to players on our team, or spectating players + int theTeamScore = (int)this->GetTotalResourcesGathered(); + if(theTeamScore != this->mClientTotalResourcesGathered) + { + if(inThisTeamOnly) + { + for(EntityListType::iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) + { + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); + if(thePlayer) + { + NetMsg_TeamScore( thePlayer->pev, this->GetTeamName(), theTeamScore, 0 ); + } + } + } + else + { + NetMsg_TeamScore( this->GetTeamName(), theTeamScore, 0 ); + } + + this->mClientTotalResourcesGathered = theTeamScore; + + theSentMessage = true; + } + } + + return theSentMessage; +} + + +void AvHTeam::UpdateVoting() +{ + string theMessageForPlayers; + AvHHUDSound theSoundForPlayers = HUD_SOUND_INVALID; + bool theSendOnlyToNonvotingPlayers = false; + + // If we have more then 1 vote against commander + if(this->mVotes > 0) + { + // If a vote hasn't started + if(!this->mVoteStarted) + { + // Tell all players on team that a vote has started + theMessageForPlayers = kVoteStarted; + theSendOnlyToNonvotingPlayers = true; + + // Start vote + this->mVoteStarted = true; + this->mCommanderWhenVoteStarted = this->mCommander; + this->mTimeVoteStarted = gpGlobals->time; + } + else + { + bool theResetVote = false; + + // If a new comm logs in after vote started, cancel vote + if((this->mCommander > 0) && (this->mCommander != this->mCommanderWhenVoteStarted)) + { + theMessageForPlayers = kVoteCancelled; + theResetVote = true; + } + else + { + // If we have enough votes to kick commander + float theVotePercent = avh_votepercentneeded.value; + int theMinVotesRequired = (int)avh_minvotesneeded.value; + int theVotesNeeded = max((float)theMinVotesRequired, this->mPlayerList.size()*theVotePercent); + + // #545: If the voting has changed, indicate so in the HUD + if ((this->mVotesNeeded != theVotesNeeded) || (this->mVotes != this->mPreviousVotes)) { + + // : 0000545 + char theVoteMessage[512]; + sprintf(theVoteMessage, "Eject commander: %d of %d votes needed.", this->mVotes, theVotesNeeded); + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->pev->team == this->mTeamNumber) + { + UTIL_SayText(theVoteMessage, theEntity); // , theEntity->entindex()); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + + + // UTIL_ClientPrintAll(HUD_PRINTTALK, theVoteMessage); + UTIL_LogPrintf(theVoteMessage); + + this->mVotesNeeded = theVotesNeeded; + this->mPreviousVotes = this->mVotes; + // : + } + + if(this->mVotes >= theVotesNeeded) + { + // Kick commander + AvHPlayer* theCommander = this->GetCommanderPlayer(); + if(theCommander) + { + theCommander->StopTopDownMode(); + theCommander->SetUser3(AVH_USER3_MARINE_PLAYER); + + // Remember when we were voted down, so they can't command again + AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(theCommander->edict()); + if(theServerPlayerData) + { + theServerPlayerData->SetTimeVotedDown(gpGlobals->time); + } + + // Tell all players on team that commander has been ejected + theMessageForPlayers = kVoteSucceeded; + theSoundForPlayers = HUD_SOUND_MARINE_COMMANDER_EJECTED; + theResetVote = true; + } + } + else + { + // else if enough time has elapsed without a vote going through + int theVoteCastTimeInSeconds = avh_votecasttime.value*60; + if(gpGlobals->time > (this->mTimeVoteStarted + theVoteCastTimeInSeconds)) + { + // Tell all players on team that the vote has failed + theMessageForPlayers = kVoteFailed; + theResetVote = true; + } + } + } + + if(theResetVote) + { + // Reset vote (don't reset players that voted, they can only vote once per game) + this->mVotes = 0; + this->mVoteStarted = false; + this->mTimeVoteStarted = -1; + // clear the list of voting players to allow more than one vote each game (#545) + this->mVotingPlayers.clear(); + this->mVotesNeeded = 0; + this->mPreviousVotes = 0; + } + } + } + + // Send message to players + if(theMessageForPlayers != "") + { + // Send message to players on team + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetTeam() == this->GetTeamNumber()) + { + int thePlayerIndex = theEntity->entindex(); + EntityListType::iterator theIterator = std::find(this->mVotingPlayers.begin(), this->mVotingPlayers.end(), thePlayerIndex); + bool thePlayerHasVoted = (theIterator != this->mVotingPlayers.end()); + + if(!theSendOnlyToNonvotingPlayers || !thePlayerHasVoted) + { + theEntity->SendMessage(theMessageForPlayers.c_str()); + + // Play "commander has been ejected" sound for all players + if(theSoundForPlayers != HUD_SOUND_INVALID) + { + theEntity->PlayHUDSound(theSoundForPlayers); + } + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + } +} + +void AvHTeam::UpdateHints() +{ + if(GetGameRules()->GetGameStarted() && (this->GetPlayerCount() > 1) && !GetGameRules()->GetIsCombatMode()) + { + const float kHintUpdateInterval = 2.0f; + if((this->mTimeLastHintUpdate == -1) || (gpGlobals->time > (this->mTimeLastHintUpdate + kHintUpdateInterval))) + { + this->mTimeLastHintUpdate = gpGlobals->time; + + float kHintInterval = 20.0f; + if(this->GetTeamType() == AVH_CLASS_TYPE_ALIEN) + { + kHintInterval = 30.0f; + } + + if((this->mTimeOfLastTeamHint == -1) || (gpGlobals->time > (this->mTimeOfLastTeamHint + kHintInterval))) + { + bool thePlayedHint = false; + + // Update commander hints + AvHPlayer* theCommanderPlayer = this->GetCommanderPlayer(); + if(theCommanderPlayer) + { + const float kGiveOrdersHintDelay = 20.0f; + float theTimeAsCommander = gpGlobals->time - theCommanderPlayer->GetTimeStartedTopDown(); + + if(theTimeAsCommander > kGiveOrdersHintDelay) + { + // Remove warning because tech tree doesn't currently require it + //// Do we have zero infantry portals, and we haven't played "need infantry portal" sound for awhile + //bool theTeamHasLiveInfantryPortal = false; + // + //FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) + // if(theEntity->pev->team == this->mTeamNumber) + // { + // if(theEntity->GetIsBuilt()) + // { + // theTeamHasLiveInfantryPortal = true; + // break; + // } + // } + //END_FOR_ALL_ENTITIES(kwsInfantryPortal); + // + //if(!theTeamHasLiveInfantryPortal) + //{ + // // Play "need infantry portal" sound + // theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_NEEDPORTAL); + // thePlayedHint = true; + //} + + if(!thePlayedHint) + { + // Did commander just join and hasn't given any orders? Play "select your troops and give them orders". + if(!theCommanderPlayer->GetHasGivenOrder()) + { + theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_GIVEORDERS); + thePlayedHint = true; + } + } + + if(!thePlayedHint) + { + // If we just played an alert, tell them how to go to it (but only tell every commander once) + const float kMinAlertNotifyInterval = 5.0f; + float theTimeOfLastAudioAlert = -1; + const AvHAlert* theLastAlert = this->GetLastAudioAlert(); + if(theLastAlert) + { + theTimeOfLastAudioAlert = theLastAlert->GetTime(); + } + + if((theTimeOfLastAudioAlert == -1) || ((gpGlobals->time - theTimeOfLastAudioAlert) < kMinAlertNotifyInterval)) + { + int theCurrentCommander = theCommanderPlayer->entindex(); + if(std::find(this->mCommandersToldAboutJumpKey.begin(), this->mCommandersToldAboutJumpKey.end(), theCurrentCommander) == this->mCommandersToldAboutJumpKey.end()) + { + theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_GOTOALERT); + this->mCommandersToldAboutJumpKey.push_back(theCurrentCommander); + } + } + } + + // Detect shell-shocked commander. Play "do something" sound. + if(!thePlayedHint) + { + const float kShellShockedInterval = 130.0f; + + float theTimeOfLastSignificantCommanderAction = theCommanderPlayer->GetTimeOfLastSignificantCommanderAction(); + float theTimeSinceLastSignificantAction = gpGlobals->time - theTimeOfLastSignificantCommanderAction; + + if((theTimeOfLastSignificantCommanderAction == -1) || (theTimeSinceLastSignificantAction > kShellShockedInterval)) + { + theCommanderPlayer->PlayHUDSound(HUD_SOUND_MARINE_COMMANDERIDLE); + thePlayedHint = true; + } + } + } + } + + // Update alien team hints + if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + int theNumBuilders = 0; + int theNumAliveTeamMembers = 0; + + // If we have no builders, tell the team + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + if(theEntity->GetIsRelevant() && (theEntity->pev->team == this->mTeamNumber)) + { + // If we have builders, or players that are evolving to builders... + if(theEntity->GetUser3() == AVH_USER3_ALIEN_PLAYER2) + { + theNumBuilders++; + } + else if(theEntity->GetEvolution() == ALIEN_LIFEFORM_TWO) + { + theNumBuilders++; + } + else if((theEntity->GetUser3() == AVH_USER3_ALIEN_EMBRYO) && (theEntity->GetPreviousUser3() == AVH_USER3_ALIEN_PLAYER2)) + { + AvHMessageID theEvolution = theEntity->GetEvolution(); + switch(theEvolution) + { + case ALIEN_EVOLUTION_ONE: + case ALIEN_EVOLUTION_TWO: + case ALIEN_EVOLUTION_THREE: + case ALIEN_EVOLUTION_SEVEN: + case ALIEN_EVOLUTION_EIGHT: + case ALIEN_EVOLUTION_NINE: + case ALIEN_EVOLUTION_TEN: + case ALIEN_EVOLUTION_ELEVEN: + case ALIEN_EVOLUTION_TWELVE: + theNumBuilders++; + break; + } + } + + if(theEntity->IsAlive()) + { + theNumAliveTeamMembers++; + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + + // Only play if there is more then one person alive on team (player could be last one left or could be saving up) + if((theNumBuilders == 0) && (theNumAliveTeamMembers > 1)) + { + this->PlayHUDSoundForAlivePlayers(HUD_SOUND_ALIEN_NEED_BUILDERS); + thePlayedHint = true; + } + + // Now, fun easter eggs, but not in tourny mode + if(!thePlayedHint && !GetGameRules()->GetIsTournamentMode() && this->mPlayFunSoundsThisGame) + { + // If a hive is under attack and the marines are way ahead, play "game over man" + + AvHHive* theRandomHive = AvHSUGetRandomActiveHive(this->mTeamNumber); + if(theRandomHive) + { + const float kRadius = 600; + + int theClutterEntities = 0; + int theDeadTeammates = 0; + int theDeadPlayers = 0; + + CBaseEntity* theEntity = NULL; + while((theEntity = UTIL_FindEntityInSphere(theEntity, theRandomHive->pev->origin, kRadius)) != NULL) + { + // If there are a ton of dead alien players, sometimes play "we need better players" + if(theEntity->IsPlayer() && !theEntity->IsAlive()) + { + if(theEntity->pev->team == this->mTeamNumber) + { + theDeadTeammates++; + } + + theDeadPlayers++; + } + else + { + // If there are tons of aliens buildings nearby, play "place is a mess" sound for all aliens nearby + AvHBaseBuildable* theBuildable = dynamic_cast(theEntity); + CBasePlayerItem* theItem = dynamic_cast(theEntity); + if(theBuildable || theItem) + { + theClutterEntities++; + } + } + } + + if((theDeadPlayers >= 14) && !this->mPlayedSeeDeadPeople) + { + EMIT_SOUND(theRandomHive->edict(), CHAN_AUTO, kAlienSeeDead, 1.0f, ATTN_NORM); + this->mPlayedSeeDeadPeople; + thePlayedHint = true; + } + else if(theDeadTeammates >= 8) + { + this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_NEED_BETTER); + thePlayedHint = true; + } + else if(theClutterEntities >= 25) + { + this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_MESS); + thePlayedHint = true; + } + } + + // If aliens have all three hives and all upgrades, play "donce" + if(this->GetNumActiveHives() == kMaxHives) + { + if(this->GetAlienUpgrades().size() >= kMaxUpgradeCategories*kMaxUpgradeLevel) + { + this->PlayFunHUDSoundOnce(HUD_SOUND_ALIEN_NOW_DONCE); + thePlayedHint = true; + } + } + + // If there are tons of bodies near a hive, play "I see dead people" + + } + } + + if(thePlayedHint) + { + this->mTimeOfLastTeamHint = gpGlobals->time; + } + } + } + } +} + +void AvHTeam::UpdateCommanderScore() +{ + // The commander score is the average of his team score + const float kUpdateScoreTime = 2.0f; + float theCurrentTime = gpGlobals->time; + + int theCommander = this->GetCommander(); + if(theCommander > 0) + { + if((this->mLastCommandScoreUpdateTime == -1) || (theCurrentTime > this->mLastCommandScoreUpdateTime + kUpdateScoreTime)) + { + int theScore = 0; + int theNumPlayers = 0; + + // Run through players on team and average score + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + // For each player, update alien inventory with this # of hives + if(theEntity->GetTeam() == this->GetTeamNumber()) + { + if(theEntity->entindex() != theCommander) + { + theScore += theEntity->GetScore(); + theNumPlayers++; + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName); + + int theCommanderScore = (int)((theScore/(float)theNumPlayers) + .5f); + + AvHPlayer* theCommanderPlayer = this->GetCommanderPlayer(); + if(theCommanderPlayer) + { + theCommanderPlayer->SetScore(theCommanderScore); + } + + this->mLastCommandScoreUpdateTime = theCurrentTime; + } + } +} + +void AvHTeam::UpdateInventoryEnabledState() +{ + // Count # of hives + int theNumHives = this->GetNumActiveHives(); + + // Loop through players on team + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + // For each player, update alien inventory with this # of hives + if(theEntity->GetTeam() == this->GetTeamNumber()) + { + if(GetGameRules()->GetIsCombatMode()) + { + theNumHives = 1; + + MessageIDListType& thePurchasedCombatUpgrades = theEntity->GetPurchasedCombatUpgrades(); + MessageIDListType::iterator theIter = std::find(thePurchasedCombatUpgrades.begin(), thePurchasedCombatUpgrades.end(), ALIEN_HIVE_TWO_UNLOCK); + + //AvHTechNode theTechNode; + //if(theEntity->GetCombatNodes().GetTechNode(ALIEN_HIVE_TWO_UNLOCK, theTechNode) && theTechNode.GetIsResearched()) + if(theIter != thePurchasedCombatUpgrades.end()) + { + theNumHives++; + + //if(theEntity->GetCombatNodes().GetTechNode(ALIEN_HIVE_THREE_UNLOCK, theTechNode) && theTechNode.GetIsResearched()) + theIter = std::find(thePurchasedCombatUpgrades.begin(), thePurchasedCombatUpgrades.end(), ALIEN_HIVE_THREE_UNLOCK); + if(theIter != thePurchasedCombatUpgrades.end()) + { + theNumHives++; + } + } + } + + theEntity->UpdateInventoryEnabledState(theNumHives); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) +} + +void AvHTeam::UpdateReinforcementWave() +{ + int theNumPlayersOnTeam = this->GetPlayerCount(); + int theNumDeadPlayers = this->GetPlayerCount(true); + int theWaveSize = AvHSUCalcCombatSpawnWaveSize(theNumPlayersOnTeam, theNumDeadPlayers); + + // If we're in combat mode + if(GetGameRules()->GetIsCombatMode() && (GetGameRules()->GetVictoryTeam() == TEAM_IND) && !GetGameRules()->GetIsIronMan()) + { + // If reinforcement wave hasn't started + if(this->mTimeReinforcementWaveComplete == -1) + { + // If team has at least one dead player, start wave + for(EntityListType::const_iterator theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) + { + const AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); + ASSERT(thePlayer); + float theWaveTime = AvHSUCalcCombatSpawnTime(this->mTeamType, theNumPlayersOnTeam, theNumDeadPlayers, 0); + + switch(thePlayer->GetPlayMode()) + { + case PLAYMODE_AWAITINGREINFORCEMENT: + case PLAYMODE_REINFORCING: + // Set time reinforcement is complete + this->mTimeReinforcementWaveComplete = gpGlobals->time + theWaveTime; + break; + } + } + } + else + { + // Spawn back in a max of X players per wave (1/4 rounded up) + int theNumRespawning = 0; + + EntityListType::const_iterator theIter; + + // Count number of people currently respawning + for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) + { + AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); + ASSERT(thePlayer); + + if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING) + { + theNumRespawning++; + } + } + + // Find the player that's been waiting the longest + if(theNumRespawning < theWaveSize) + { + EntityListType::const_iterator theLongestWaitingPlayer = this->mPlayerList.end(); + float theLongestWaitingTime = -1; + + // Loop through players + for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) + { + // While we don't allow any more to repsawn or we hit end of list + AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); + ASSERT(thePlayer); + + if(thePlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) + { + float thePlayerWaitTime = (gpGlobals->time - thePlayer->GetTimeLastPlaying()); + if((theLongestWaitingTime == -1) || (thePlayerWaitTime > theLongestWaitingTime)) + { + theLongestWaitingPlayer = theIter; + theLongestWaitingTime = thePlayerWaitTime; + } + } + } + + if(theLongestWaitingPlayer != this->mPlayerList.end()) + { + AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theLongestWaitingPlayer); + ASSERT(thePlayer); + thePlayer->SetPlayMode(PLAYMODE_REINFORCING); + } + } + + // Respawn players when wave is complete + if(gpGlobals->time > this->mTimeReinforcementWaveComplete) + { + for(theIter = this->mPlayerList.begin(); theIter != this->mPlayerList.end(); theIter++) + { + // While we don't allow any more to repsawn or we hit end of list + AvHPlayer* thePlayer = this->GetPlayerFromIndex(*theIter); + ASSERT(thePlayer); + + if(thePlayer->GetPlayMode() == PLAYMODE_REINFORCING) + { + thePlayer->SetPlayMode(PLAYMODE_PLAYING); + } + } + + // Reset wave + this->mTimeReinforcementWaveComplete = -1; + } + } + } +} + +void AvHTeam::UpdateOrders() +{ + // For each order in our list + for(OrderListType::iterator theOrderIter = this->mOrderList.begin(); theOrderIter != this->mOrderList.end(); /* no increment*/) + { + ASSERT(theOrderIter->GetReceiver() != -1 ); + if(theOrderIter->Update()) + { + bool theOrderCancelled = theOrderIter->GetOrderCancelled(); + if(!theOrderCancelled) + { + EntityInfo theReceiver = theOrderIter->GetReceiver(); + if(theReceiver != -1 ) + { + GetGameRules()->TriggerAlert(this->mTeamNumber, ALERT_ORDER_COMPLETE, theReceiver); + } + } + } + + // Have any orders been completed for a bit? + const float kExpireTime = 1.0f; + if(!theOrderIter->GetOrderActive() && (theOrderIter->GetTimeOrderCompleted() != -1) && (gpGlobals->time > (theOrderIter->GetTimeOrderCompleted() + kExpireTime))) + { + this->mOrderList.erase(theOrderIter); + } + else + { + theOrderIter++; + } + + // Is the order complete? +// for(OrderListType::iterator theOrderIter = this->mOrderList.begin(); theOrderIter != this->mOrderList.end(); /* no increment */) +// { +// // Is the order complete? +// bool theOrderCancelled = false; +// if(theOrderIter->GetIsComplete(&theOrderCancelled)) +// { +// if(!theOrderCancelled) +// { +// // For each receiver +// EntityListType theReceivers = theOrderIter->GetReceivers(); +// for(EntityListType::iterator theReceiverIter = theReceivers.begin(); theReceiverIter != theReceivers.end(); theReceiverIter++) +// { +// // Get player. This fails occasionally, due to selection issues (ie, a building or other non-player entity will be in here) +// AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theReceiverIter))); +// //ASSERT(thePlayer); +// if(thePlayer) +// { +// // Set the order complete +// thePlayer->PlayOrderComplete(); +// } +// } +// +// // Set order complete for commander +// int theCommanderIndex = this->GetCommander(); +// if(theCommanderIndex >= 0) +// { +// AvHPlayer* theCommander = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(theCommanderIndex))); +// if(theCommander) +// { +// theCommander->PlayOrderComplete(); +// } +// } +// } +// +// // Set the receivers to none because it's no longer relevant +// AvHOrder theCompletedOrder(*theOrderIter); +// theCompletedOrder.SetOrderCompleted(); +// +// this->SetOrder(theCompletedOrder); +// } +// else +// { +// theOrderIter++; +// } + } +} + +//void AvHTeam::UpdateReinforcements() +//{ +// // If this team is alien +// if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) +// { +// // For each hive on this team +// FOR_ALL_ENTITIES(kesTeamHive, AvHHive*) +// if(AvHTeamNumber(theEntity->pev->team) == this->mTeamNumber) +// { +// // Update reinforcements +// theEntity->UpdateReinforcements(); +// } +// END_FOR_ALL_ENTITIES(kesTeamHive); +// } +// // else if it's marine +// else if(this->mTeamType == AVH_CLASS_TYPE_MARINE) +// { +// // For each infantry portal on this team +// FOR_ALL_ENTITIES(kwsInfantryPortal, AvHInfantryPortal*) +// if(AvHTeamNumber(theEntity->pev->team) == this->mTeamNumber) +// { +// // Update reinforcements +// theEntity->UpdateReinforcements(); +// } +// END_FOR_ALL_ENTITIES(kwsInfantryPortal); +// } +// else +// { +// ASSERT(false); +// } +//} + +void AvHTeam::UpdateResearch() +{ + this->mResearchManager.UpdateResearch(); + this->UpdateMenuTechSlots(); +} + +void AvHTeam::UpdateResources() +{ + const AvHGameplay& theGameplay = GetGameRules()->GetGameplay(); + float kResourceUpdateInterval = theGameplay.GetTowerInjectionTime(); + + if(GetGameRules()->GetIsHamboneMode()) + { + kResourceUpdateInterval /= 2.0f; + } + + // Change here if teams should always get at least one resource + float theResourcesGathered = 0.0f; + + float theCurrentTime = gpGlobals->time; + if((this->mLastResourceUpdateTime == -1) || (theCurrentTime > (this->mLastResourceUpdateTime + kResourceUpdateInterval))) + { + // Add resources for each built-on resource on the team + for(EntityListType::iterator theResourceTowerIter = this->mResourceTowerList.begin(); theResourceTowerIter != this->mResourceTowerList.end(); /* nothing */) + { + CBaseEntity* theResourceTowerEntity = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*theResourceTowerIter)); + AvHResourceTower* theResourceTower = dynamic_cast(theResourceTowerEntity); + if(theResourceTower && (theResourceTower->pev->team == this->mTeamNumber)) + { + if(theResourceTower->GetIsActive()) + { + // Has enough time elapsed? + float theLastTimeResourcesContributed = theResourceTower->GetTimeLastContributed(); + float theCurrentTime = gpGlobals->time; + + if(theCurrentTime > (theLastTimeResourcesContributed + kResourceUpdateInterval)) + { + // Take into account upgrade level stored in tower + float theTowerContribution = theGameplay.GetTowerInjectionAmount(); + theResourcesGathered += theTowerContribution; + + // Decrement the amount of points the resources has + theResourceTower->SetTimeLastContributed(theCurrentTime); + + AvHSUPlayNumericEventAboveStructure(theTowerContribution, theResourceTower); + } + } + + theResourceTowerIter++; + } + else + { + // Remove it from team also + theResourceTowerIter = this->mResourceTowerList.erase(theResourceTowerIter); + } + } + + // If we're marines, add it to the team total + if(this->mTeamType == AVH_CLASS_TYPE_MARINE) + { + this->SetTeamResources(this->GetTeamResources() + theResourcesGathered); + } + else if(this->mTeamType == AVH_CLASS_TYPE_ALIEN) + { + // Else dump it into the pool, and split the whole pool up among all players on our team + float theTeamResources = this->GetTeamResources() + theResourcesGathered; + + int theNumPlayers = this->GetPlayerCount(); + float theAmountForPlayer = theTeamResources/(float)theNumPlayers; + + // Clear resources, assuming all res will be given out. Anything over will gob ack into team pool + this->SetTeamResources(0.0f); + + for(EntityListType::iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) + { + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); + ASSERT(thePlayer); + + // If the amount goes over the player's max, it goes back into team resources + float theResourcesBefore = thePlayer->GetResources(); + thePlayer->SetResources(thePlayer->GetResources() + theAmountForPlayer); + } + } + + // Increment total resources gathered + this->AddResourcesGathered(theResourcesGathered); + + this->mLastResourceUpdateTime = theCurrentTime; + } +} + +AvHServerPlayerData* AvHTeam::GetServerPlayerData(edict_t* inEdict) +{ + string theNetworkID = AvHNexus::getNetworkID(inEdict); + return &this->mServerPlayerData[theNetworkID]; +} +// : +void AvHTeam::UpdateServerPlayerData() +{ + const float kServerPlayerDataUpdateInterval = 1.0f; + float theCurrentTime = gpGlobals->time; + if((this->mLastServerPlayerDataUpdateTime == -1) || (theCurrentTime > (this->mLastServerPlayerDataUpdateTime + kServerPlayerDataUpdateInterval))) + { + // Update all settings to remember keyed to their WONid + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + AvHServerPlayerData* theServerPlayerData = this->GetServerPlayerData(theEntity->edict()); + if(theServerPlayerData) + { + // Expire any commander bans + float theCommanderVoteDownTime = avh_votedowntime.value; + float theTimeVotedDown = theServerPlayerData->GetTimeVotedDown()*60; + if(theTimeVotedDown > 0) + { + if(gpGlobals->time > (theTimeVotedDown + theCommanderVoteDownTime)) + { + theServerPlayerData->SetTimeVotedDown(-1); + } + } + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + } +} + +void AvHTeam::UpdateMenuTechSlots() +{ + if(this->mTeamType == AVH_CLASS_TYPE_MARINE) + { + // Update commander menus. Get tech slots for each menu and add them all in one 32 long list + this->mMenuTechSlots = 0; + + AvHGamerules* theGameRules = GetGameRules(); + const AvHTeam* theTeam = theGameRules->GetTeam(this->mTeamNumber); + if(theTeam) + { + for(int j = 0; j < 4; j++) + { + AvHUser3 theUser3 = AVH_USER3_NONE; + int theBaseIndex = 0; + + switch(j) + { + case 0: + theUser3 = AVH_USER3_MENU_BUILD; + theBaseIndex = 0; + break; + case 1: + theUser3 = AVH_USER3_MENU_BUILD_ADVANCED; + theBaseIndex = 8; + break; + case 2: + theUser3 = AVH_USER3_MENU_ASSIST; + theBaseIndex = 16; + break; + case 3: + theUser3 = AVH_USER3_MENU_EQUIP; + theBaseIndex = 24; + break; + } + + // For each slot, see if tech is available + AvHTechSlots theTechSlots; + if(theTeam->GetTechSlotManager().GetTechSlotList(theUser3, theTechSlots)) + { + // Store results in full 32-bits of iuser1 + for(int i = 0; i < kNumTechSlots; i++) + { + int theCurrentMask = 1 << (theBaseIndex + i); + + AvHMessageID theMessage = theTechSlots.mTechSlots[i]; + if(theMessage != MESSAGE_NULL) + { + if(this->GetIsTechnologyAvailable(theMessage)) + { + this->mMenuTechSlots |= theCurrentMask; + } + } + } + } + } + } + } +} + +bool AvHTeam::GetIsTechnologyAvailable(AvHMessageID inMessageID) const +{ + bool theTechnologyAvailable = this->GetResearchManager().GetIsMessageAvailable(inMessageID); + return theTechnologyAvailable; +} + + + + +bool AvHTeam::GetLastAlert(AvHAlert& outAlert, bool inClearAlert, bool inAnyMessage, AvHMessageID* ioMessageID) +{ + float theLastAlertTime = -1; + bool theSuccess = false; + + // Look for last alert + AlertListType::iterator theLastAlertIter; + AvHMessageID theOutputMessageID = MESSAGE_NULL; + + for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) + { + if(inAnyMessage || (ioMessageID && (theIter->first == *ioMessageID))) + { + for(AlertListType::iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) + { + float theCurrentTime = theAlertIter->GetTime(); + if(theCurrentTime > theLastAlertTime) + { + theLastAlertIter = theAlertIter; + theLastAlertTime = theCurrentTime; + theOutputMessageID = theIter->first; + theSuccess = true; + } + } + } + } + + // If we found it, set the alert + if(theSuccess) + { + outAlert = *theLastAlertIter; + if(ioMessageID) + { + *ioMessageID = theOutputMessageID; + } + } + + // Clear the alert if specified + if(inClearAlert) + { + for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) + { + for(AlertListType::iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) + { + if(theAlertIter == theLastAlertIter) + { + theIter->second.erase(theLastAlertIter); + break; + } + } + } + } + + return theSuccess; +} + +float AvHTeam::GetAudioAlertInterval() const +{ + float theAudioAlertInterval = BALANCE_VAR(kAudioAlertInterval); + return theAudioAlertInterval; +} + +void AvHTeam::AddAlert(const AvHAlert& inAlert, AvHMessageID inMessageID) +{ + this->mAlertList[inMessageID].push_back(inAlert); +} + +const AvHAlert* AvHTeam::GetLastAudioAlert() const +{ + // Look for last audio alert + const AvHAlert* theLastAudioAlert = NULL; + float theLastTime = -1; + + for(MessageAlertListType::const_iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); theIter++) + { + for(AlertListType::const_iterator theAlertIter = theIter->second.begin(); theAlertIter != theIter->second.end(); theAlertIter++) + { + if(theAlertIter->GetPlayedAudio()) + { + float theCurrentTime = theAlertIter->GetTime(); + if(theCurrentTime > theLastTime) + { + theLastAudioAlert = &(*theAlertIter); + theLastTime = theCurrentTime; + } + } + } + } + + return theLastAudioAlert; +} + +AlertListType AvHTeam::GetAlerts(AvHMessageID inMessageID) +{ + return this->mAlertList[inMessageID]; +} + +void AvHTeam::UpdateAlerts() +{ + const float theAlertDuration = BALANCE_VAR(kAlertExpireTime); + + #ifdef AVH_PLAYTEST_BUILD + // Run through our team, and alert everyone if there is a player that's no longer playing or on the server + for(EntityListType::const_iterator thePlayerIter = this->mPlayerList.begin(); thePlayerIter != this->mPlayerList.end(); thePlayerIter++) + { + int thePlayerIndex = *thePlayerIter; + bool theSendAlert = false; + char theErrorMessage[512]; + + AvHPlayer* thePlayer = dynamic_cast(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(*thePlayerIter))); + if(!thePlayer) + { + theSendAlert = true; + strcpy(theErrorMessage, "NULL player"); + } + else + { + AvHPlayMode thePlayMode = thePlayer->GetPlayMode(); + if((thePlayMode != PLAYMODE_PLAYING) && (thePlayMode != PLAYMODE_AWAITINGREINFORCEMENT) && (thePlayMode != PLAYMODE_REINFORCING)) + { + theSendAlert = true; + sprintf(theErrorMessage, "Invalid play mode: %d\n", thePlayMode); + } + else if(AvHTeamNumber(thePlayer->pev->team) != this->mTeamNumber) + { + theSendAlert = true; + sprintf(theErrorMessage, "on wrong team (%d instead of %d)", thePlayer->pev->team, this->mTeamNumber); + } + } + + if(theSendAlert) + { + const char* thePlayerName = ""; + thePlayerName = STRING(thePlayer->pev->netname); + + char theFullErrorMesssage[512]; + sprintf(theFullErrorMesssage, "Invalid player bug detected <\"%s\">: %d", thePlayerName, theErrorMessage); + UTIL_SayTextAll(theFullErrorMesssage, thePlayer); + } + } + #endif + + // Run through alerts, and expire any past a certain age + for(MessageAlertListType::iterator theIter = this->mAlertList.begin(); theIter != this->mAlertList.end(); ++theIter) + { + AlertListType& theAlertList = theIter->second; + for(AlertListType::iterator theAlertIter = theAlertList.begin(); theAlertIter != theAlertList.end(); /* no inc */) + { + float theAlertTime = theAlertIter->GetTime(); + if(gpGlobals->time > (theAlertTime + theAlertDuration)) + { + theAlertIter = theAlertList.erase(theAlertIter); + } + else + { + theAlertIter++; + } + } + } +} + +EntityListType AvHTeam::GetGroup(int inGroupNumber) +{ + ASSERT(inGroupNumber >= 0); + ASSERT(inGroupNumber < kNumHotkeyGroups); + + return this->mGroups[inGroupNumber]; +} + +void AvHTeam::SetGroup(int inGroupNumber, EntityListType& inEntityListType) +{ + // Set group + ASSERT(inGroupNumber >= 0); + ASSERT(inGroupNumber < kNumHotkeyGroups); + + this->mGroups[inGroupNumber] = inEntityListType; + + // Update group type + AvHUser3 theUser3 = AvHSUGetGroupTypeFromSelection(this->mGroups[inGroupNumber]); + this->mGroupTypes[inGroupNumber] = theUser3; +} + +AvHUser3 AvHTeam::GetGroupType(int inGroupNumber) +{ + ASSERT(inGroupNumber >= 0); + ASSERT(inGroupNumber < kNumHotkeyGroups); + + return this->mGroupTypes[inGroupNumber]; +} + +EntityListType AvHTeam::GetSelectAllGroup() +{ + return this->mSelectAllGroup; +} + +void AvHTeam::SetSelectAllGroup(EntityListType& inGroup) +{ + this->mSelectAllGroup = inGroup; +} diff --git a/main/source/mod/AvHTeam.h b/main/source/mod/AvHTeam.h index 5cc1310..96dd56e 100644 --- a/main/source/mod/AvHTeam.h +++ b/main/source/mod/AvHTeam.h @@ -1,342 +1,337 @@ -//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHTeam.h $ -// $Date: 2002/11/22 21:15:37 $ -// -//------------------------------------------------------------------------------- -// $Log: AvHTeam.h,v $ -// Revision 1.32 2002/11/22 21:15:37 Flayra -// - Remove owner of entities when player leaves the team -// - Do damage to aliens without hives -// - Fixed bug where a vote could affect a commander that logged in after a vote had been started. -// -// Revision 1.31 2002/11/15 23:31:26 Flayra -// - Added "ready" verification for tourny mode -// -// Revision 1.30 2002/11/12 02:29:33 Flayra -// - Added better standardized logging -// -// Revision 1.29 2002/10/24 21:43:29 Flayra -// - Alien easter eggs -// - Voting fix -// -// Revision 1.28 2002/10/18 22:23:04 Flayra -// - Added beginnings of alien easter eggs -// - Added "we need builders" alert -// -// Revision 1.27 2002/10/03 19:09:55 Flayra -// - New resource model -// - Orders refactoring -// - Tech tree changes -// - Aliens always get initial points when joining -// - Play gamestart sound -// - New trait available trigger -// - Moved voting stuff to server variables -// - Slowed down hints -// -// Revision 1.26 2002/09/23 22:35:43 Flayra -// - Removed hive donation and put in new system for builders -// - Updated tech tree (jetpacks, heavy armor, moved phase) -// - Resource towers set as built on game start -// - Added tons of commander hints (low resource alerts, tell commander about pressing jump key, commander ejected, select troops and give orders, don't just sit there, etc.) -// -// Revision 1.25 2002/09/09 20:08:26 Flayra -// - Added commander voting -// - Added hive info -// - Changed how commander scoring works -// -// Revision 1.24 2002/08/31 18:01:03 Flayra -// - Work at VALVe -// -// Revision 1.23 2002/08/02 21:45:20 Flayra -// - Reworked alerts. -// -// Revision 1.22 2002/07/23 17:32:23 Flayra -// - Resource model overhaul (closed system, random initial points), tech tree changes (new marine upgrades), free resource tower at nearest func_resource -// -// Revision 1.21 2002/07/08 17:19:42 Flayra -// - Added handicapping, map validity checking, reinforcements happen independently of teams now -// -// Revision 1.20 2002/06/25 18:21:33 Flayra -// - New enabled/disabled system for alien weapons, better victory detection, updated tech tree (removed old junk, added armory upgrade), fixed bug where a commander that leaves the game hogged the station, update resources less often (optimization) -// -// Revision 1.19 2002/05/28 18:09:51 Flayra -// - Track number of webs for team, fix for premature victory condition (again), fixed bug where alien upgrades weren't being cleared between levels, causing eventual overflows for alien players after many games, recycling support, spawn in command center when game starts -// -// Revision 1.18 2002/05/23 02:32:57 Flayra -// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. -// -//=============================================================================== -#ifndef AVH_TEAM_H -#define AVH_TEAM_H - -#include "mod/AvHConstants.h" -#include "mod/AvHOrder.h" -#include "mod/AvHSharedTypes.h" -#include "mod/AvHResearchManager.h" -#include "mod/AvHSpecials.h" -#include "mod/AvHServerPlayerData.h" -#include "mod/AvHTechSlotManager.h" -#include "mod/AvHAlert.h" -#include "mod/AvHObjective.h" - -class AvHPlayer; - -class AvHTeam -{ -public: - AvHTeam(AvHTeamNumber inTeamNumber); - - bool AddPlayer(int inPlayerIndex); - bool RemovePlayer(int inPlayerIndex); - - bool AddResourceTower(int inResourceTowerIndex); - bool RemoveResourceTower(int inResourceTowerIndex); - - bool GetHasAtLeastOneActivePlayer(bool* outHasAtLeastOnePlayerOnTeam = NULL) const; - bool GetHasTeamLost() const; - int GetNumBuiltCommandStations() const; - int GetNumActiveHives() const; - - bool ProcessRankChange(AvHPlayer* inPlayer, AvHUser3 inOldUser3, AvHUser3 inNewUser3); - - bool GetIsReady() const; - void SetIsReady(bool bIsReady = true); - - AvHPlayer* GetPlayerFromIndex(int inPlayerIndex); - const AvHPlayer* GetPlayerFromIndex(int inPlayerIndex) const; - - AvHClassType GetTeamType(void) const; - void SetTeamType(AvHClassType inType); - const char* GetTeamTypeString(void) const; - - float GetTeamResources() const; - void SetTeamResources(float inTeamResources); - float GetTotalResourcesGathered() const; - void AddResourcesGathered(float inResources); - - float GetMaxResources(AvHUser3 inUser3) const; - - const char* GetTeamName(void) const; - void SetTeamName(const char* inTeamName); - - const char* GetTeamPrettyName(void) const; - void SetTeamPrettyName(const char* inTeamName); - - bool GetIsPlayerOnTeam(int inPlayerIndex) const; - int GetPlayerCount(bool inCountOnlyDead = false) const; - AvHTeamNumber GetTeamNumber(void) const; - void SetTeamNumber(AvHTeamNumber inTeamNumber); - - float GetHandicap() const; - void SetHandicap(float inHandicap); - bool GetIsTechnologyAvailable(AvHMessageID inMessageID) const; - - int GetDesiredSpawnCount() const; - float GetInitialPlayerPoints(edict_t* inEdict); - float GetInitialExperience(edict_t* inEdict); - - bool SendResourcesGatheredScore(bool inThisTeamOnly = true); - - void AddTeamUpgrade(AvHAlienUpgradeCategory inCategory); - AvHAlienUpgradeListType GetAlienUpgrades() const; - bool RemoveAlienUpgradeCategory(AvHAlienUpgradeCategory inCategory); - - int GetTeamWideUpgrades() const; - void ProcessTeamUpgrade(AvHMessageID inMessageID, bool inGive); - - int GetNumCommanders(void) const; - - // returns -1 if no commander assigned yet - int GetCommander() const; - void SetCommander(int inPlayerNumber); - AvHPlayer* GetCommanderPlayer(); - - void ResetGame(); - virtual void SetGameStarted(bool inGameStarted); - - HiveInfoListType GetHiveInfoList() const; - - // Tech nodes for team - void InitializeTechNodes(); - - const AvHTechTree& GetTechNodes() const; - AvHTechTree& GetTechNodes(); - - const AvHTechSlotManager& GetTechSlotManager() const; - AvHTechSlotManager& GetTechSlotManager(); - - void TriggerAddTech(AvHTechID inTechID); - void TriggerRemoveTech(AvHTechID inTechID); - - void AddAlert(const AvHAlert& inAlert, AvHMessageID inMessageID = MESSAGE_NULL); - bool GetLastAlert(AvHAlert& outAlert, bool inClearAlert = false, bool inAnyMessage = true, AvHMessageID* ioMessageID = NULL); - const AvHAlert* GetLastAudioAlert() const; - float GetAudioAlertInterval() const; - int GetMenuTechSlots() const; - - void GetOrders(OrderListType& outOrderList) const; - bool GetDoesPlayerHaveOrder(int inPlayerIndex); - //void GetPlayersCompletingOrders(const EntityListType& outPlayerList) const; - void SetOrder(const AvHOrder& inOrder); - - void KillCS(); - void KillHive(); - void SpawnHive(Vector* outLocation = NULL, bool inForce = false); - - int GetNumWebStrands() const; - void SetNumWebStrands(int inNumWebStrands); - - void PlayFunHUDSoundOnce(AvHHUDSound inHUDSound); - void PlayHUDSoundForAlivePlayers(AvHHUDSound inHUDSound) const; - - // joev: Bug 0000767 - void PlayHUDSoundForAllPlayers(AvHHUDSound inHUDSound) const; - // :joev - - bool PlayerVote(int inPlayerIndex, string& outPlayerMessage); - - const AvHResearchManager& GetResearchManager() const; - AvHResearchManager& GetResearchManager(); - - AvHServerPlayerData* GetServerPlayerData(edict_t* inEdict); - - void Update(); - void UpdateMenuTechSlots(); - - AlertListType GetAlerts(AvHMessageID inMessageID); - - EntityListType GetGroup(int inGroupNumber); - void SetGroup(int inGroupNumber, EntityListType& inEntityListType); - - AvHUser3 GetGroupType(int inGroupNumber); - - EntityListType GetSelectAllGroup(); - void SetSelectAllGroup(EntityListType& inGroup); - - AvHObjectiveManager *GetObjectiveManager(); - -private: - void AddTechNode(AvHMessageID inMessageID, AvHTechID inTechID, AvHTechID inPrereq1 = TECH_NULL, AvHTechID inPrereq2 = TECH_NULL, bool inAllowMultiples = true, bool inResearched = false); - void SpawnResourceTower(); - - void Init(); - void InitializeCombatTechNodes(); - void InitializeRandomResourceShares(); - bool GetTeamHasAbilityToRespawn() const; - void ResetServerPlayerData(); - void RespawnLongestWaitingPlayer(); - void UpdateAlerts(); - void UpdateHints(); - void UpdateCommanderScore(); - void UpdateInventoryEnabledState(); - void UpdateReinforcementWave(); - void UpdateOrders(); - void UpdateResearch(); - void UpdateResources(); - void UpdateServerPlayerData(); - void UpdateTeamStructures(); - void UpdatePlayers(); - void UpdateVoting(); - - float WithdrawPointsViaRandomShares(int inPlayerIndex); - - AvHClassType mTeamType; - string mTeamName; - string mTeamPrettyName; - - AvHTeamNumber mTeamNumber; - float mLastResourceUpdateTime; - float mLastCommandScoreUpdateTime; - float mLastInjectionTime; - float mLastHiveSpawnTime; - float mLastPlayerUpdateTime; - float mLastServerPlayerDataUpdateTime; - Vector mStartingLocation; - - // List of player entindices - EntityListType mPlayerList; - EntityListType mResourceTowerList; - - OrderListType mOrderList; - - //EntityListType mPlayersCompletingOrder; - - int mCommander; - int mCommanderWhenVoteStarted; - - // Voting variables - bool mVoteStarted; - int mVotes; - int mPreviousVotes; - EntityListType mVotingPlayers; - int mVotesNeeded; - float mTimeVoteStarted; - - float mTeamResources; - float mHandicap; - - //AvHMessageID mResearchingTech; - AvHResearchManager mResearchManager; - - AvHAlienUpgradeListType mAlienUpgrades; - int mTeamWideUpgrades; - - int mNumWebStrands; - - typedef vector RandomResourceShareListType; - RandomResourceShareListType mRandomResourceShares; - - typedef map PlayerSharesListType; - PlayerSharesListType mPlayerShares; - - int mTotalShareWeight; - - typedef map AvHServerPlayerDataListType; - AvHServerPlayerDataListType mServerPlayerData; - - HiveInfoListType mHiveInfoList; - float mTimeOfLastStructureUpdate; - - EntityListType mLowResourceAlerts; - float mTimeLastHintUpdate; - float mTimeOfLastTeamHint; - - EntityListType mCommandersToldAboutJumpKey; - - bool mPlayFunSoundsThisGame; - HUDSoundListType mFunSoundsPlayed; - bool mPlayedSeeDeadPeople; - - bool mIsReady; - - AvHTechSlotManager mTechSlotManager; - - float mTotalResourcesGathered; - int mClientTotalResourcesGathered; - - int mMenuTechSlots; - int mNumActiveHives; - int mNumBuiltCommandStations; - - // Store alerts by message ID. This message ID is used to fetch then when the commander presses the button on the UI. General alerts use MESSAGE_NULL. - typedef map MessageAlertListType; - MessageAlertListType mAlertList; - - EntityListType mGroups[kNumHotkeyGroups]; - AvHUser3 mGroupTypes[kNumHotkeyGroups]; - EntityListType mSelectAllGroup; - - float mTimeReinforcementWaveComplete; - - AvHObjectiveManager mObjectiveManager; -}; - +//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHTeam.h $ +// $Date: 2002/11/22 21:15:37 $ +// +//------------------------------------------------------------------------------- +// $Log: AvHTeam.h,v $ +// Revision 1.32 2002/11/22 21:15:37 Flayra +// - Remove owner of entities when player leaves the team +// - Do damage to aliens without hives +// - Fixed bug where a vote could affect a commander that logged in after a vote had been started. +// +// Revision 1.31 2002/11/15 23:31:26 Flayra +// - Added "ready" verification for tourny mode +// +// Revision 1.30 2002/11/12 02:29:33 Flayra +// - Added better standardized logging +// +// Revision 1.29 2002/10/24 21:43:29 Flayra +// - Alien easter eggs +// - Voting fix +// +// Revision 1.28 2002/10/18 22:23:04 Flayra +// - Added beginnings of alien easter eggs +// - Added "we need builders" alert +// +// Revision 1.27 2002/10/03 19:09:55 Flayra +// - New resource model +// - Orders refactoring +// - Tech tree changes +// - Aliens always get initial points when joining +// - Play gamestart sound +// - New trait available trigger +// - Moved voting stuff to server variables +// - Slowed down hints +// +// Revision 1.26 2002/09/23 22:35:43 Flayra +// - Removed hive donation and put in new system for builders +// - Updated tech tree (jetpacks, heavy armor, moved phase) +// - Resource towers set as built on game start +// - Added tons of commander hints (low resource alerts, tell commander about pressing jump key, commander ejected, select troops and give orders, don't just sit there, etc.) +// +// Revision 1.25 2002/09/09 20:08:26 Flayra +// - Added commander voting +// - Added hive info +// - Changed how commander scoring works +// +// Revision 1.24 2002/08/31 18:01:03 Flayra +// - Work at VALVe +// +// Revision 1.23 2002/08/02 21:45:20 Flayra +// - Reworked alerts. +// +// Revision 1.22 2002/07/23 17:32:23 Flayra +// - Resource model overhaul (closed system, random initial points), tech tree changes (new marine upgrades), free resource tower at nearest func_resource +// +// Revision 1.21 2002/07/08 17:19:42 Flayra +// - Added handicapping, map validity checking, reinforcements happen independently of teams now +// +// Revision 1.20 2002/06/25 18:21:33 Flayra +// - New enabled/disabled system for alien weapons, better victory detection, updated tech tree (removed old junk, added armory upgrade), fixed bug where a commander that leaves the game hogged the station, update resources less often (optimization) +// +// Revision 1.19 2002/05/28 18:09:51 Flayra +// - Track number of webs for team, fix for premature victory condition (again), fixed bug where alien upgrades weren't being cleared between levels, causing eventual overflows for alien players after many games, recycling support, spawn in command center when game starts +// +// Revision 1.18 2002/05/23 02:32:57 Flayra +// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. +// +//=============================================================================== +#ifndef AVH_TEAM_H +#define AVH_TEAM_H + +#include "mod/AvHConstants.h" +#include "mod/AvHOrder.h" +#include "mod/AvHSharedTypes.h" +#include "mod/AvHResearchManager.h" +#include "mod/AvHSpecials.h" +#include "mod/AvHServerPlayerData.h" +#include "mod/AvHTechSlotManager.h" +#include "mod/AvHAlert.h" + +class AvHPlayer; + +class AvHTeam +{ +public: + AvHTeam(AvHTeamNumber inTeamNumber); + + bool AddPlayer(int inPlayerIndex); + bool RemovePlayer(int inPlayerIndex); + + bool AddResourceTower(int inResourceTowerIndex); + bool RemoveResourceTower(int inResourceTowerIndex); + + bool GetHasAtLeastOneActivePlayer(bool* outHasAtLeastOnePlayerOnTeam = NULL) const; + bool GetHasTeamLost() const; + int GetNumBuiltCommandStations() const; + int GetNumActiveHives() const; + + bool ProcessRankChange(AvHPlayer* inPlayer, AvHUser3 inOldUser3, AvHUser3 inNewUser3); + + bool GetIsReady() const; + void SetIsReady(bool bIsReady = true); + + AvHPlayer* GetPlayerFromIndex(int inPlayerIndex); + const AvHPlayer* GetPlayerFromIndex(int inPlayerIndex) const; + + AvHClassType GetTeamType(void) const; + void SetTeamType(AvHClassType inType); + const char* GetTeamTypeString(void) const; + + float GetTeamResources() const; + void SetTeamResources(float inTeamResources); + float GetTotalResourcesGathered() const; + void AddResourcesGathered(float inResources); + + float GetMaxResources(AvHUser3 inUser3) const; + + const char* GetTeamName(void) const; + void SetTeamName(const char* inTeamName); + + const char* GetTeamPrettyName(void) const; + void SetTeamPrettyName(const char* inTeamName); + + bool GetIsPlayerOnTeam(int inPlayerIndex) const; + int GetPlayerCount(bool inCountOnlyDead = false) const; + AvHTeamNumber GetTeamNumber(void) const; + void SetTeamNumber(AvHTeamNumber inTeamNumber); + + float GetHandicap() const; + void SetHandicap(float inHandicap); + bool GetIsTechnologyAvailable(AvHMessageID inMessageID) const; + + int GetDesiredSpawnCount() const; + float GetInitialPlayerPoints(edict_t* inEdict); + float GetInitialExperience(edict_t* inEdict); + + bool SendResourcesGatheredScore(bool inThisTeamOnly = true); + + void AddTeamUpgrade(AvHAlienUpgradeCategory inCategory); + AvHAlienUpgradeListType GetAlienUpgrades() const; + bool RemoveAlienUpgradeCategory(AvHAlienUpgradeCategory inCategory); + + int GetTeamWideUpgrades() const; + void ProcessTeamUpgrade(AvHMessageID inMessageID, bool inGive); + + int GetNumCommanders(void) const; + + // returns -1 if no commander assigned yet + int GetCommander() const; + void SetCommander(int inPlayerNumber); + AvHPlayer* GetCommanderPlayer(); + + void ResetGame(); + virtual void SetGameStarted(bool inGameStarted); + + HiveInfoListType GetHiveInfoList() const; + + // Tech nodes for team + void InitializeTechNodes(); + + const AvHTechTree& GetTechNodes() const; + AvHTechTree& GetTechNodes(); + + const AvHTechSlotManager& GetTechSlotManager() const; + AvHTechSlotManager& GetTechSlotManager(); + + void TriggerAddTech(AvHTechID inTechID); + void TriggerRemoveTech(AvHTechID inTechID); + + void AddAlert(const AvHAlert& inAlert, AvHMessageID inMessageID = MESSAGE_NULL); + bool GetLastAlert(AvHAlert& outAlert, bool inClearAlert = false, bool inAnyMessage = true, AvHMessageID* ioMessageID = NULL); + const AvHAlert* GetLastAudioAlert() const; + float GetAudioAlertInterval() const; + int GetMenuTechSlots() const; + + void GetOrders(OrderListType& outOrderList) const; + bool GetDoesPlayerHaveOrder(int inPlayerIndex); + //void GetPlayersCompletingOrders(const EntityListType& outPlayerList) const; + void SetOrder(const AvHOrder& inOrder); + + void KillCS(); + void KillHive(); + void SpawnHive(Vector* outLocation = NULL, bool inForce = false); + + int GetNumWebStrands() const; + void SetNumWebStrands(int inNumWebStrands); + + void PlayFunHUDSoundOnce(AvHHUDSound inHUDSound); + void PlayHUDSoundForAlivePlayers(AvHHUDSound inHUDSound) const; + + // : Bug 0000767 + void PlayHUDSoundForAllPlayers(AvHHUDSound inHUDSound) const; + // : + + bool PlayerVote(int inPlayerIndex, string& outPlayerMessage); + + const AvHResearchManager& GetResearchManager() const; + AvHResearchManager& GetResearchManager(); + + AvHServerPlayerData* GetServerPlayerData(edict_t* inEdict); + + void Update(); + void UpdateMenuTechSlots(); + + AlertListType GetAlerts(AvHMessageID inMessageID); + + EntityListType GetGroup(int inGroupNumber); + void SetGroup(int inGroupNumber, EntityListType& inEntityListType); + + AvHUser3 GetGroupType(int inGroupNumber); + + EntityListType GetSelectAllGroup(); + void SetSelectAllGroup(EntityListType& inGroup); + +private: + void AddTechNode(AvHMessageID inMessageID, AvHTechID inTechID, AvHTechID inPrereq1 = TECH_NULL, AvHTechID inPrereq2 = TECH_NULL, bool inAllowMultiples = true, bool inResearched = false); + void SpawnResourceTower(); + + void Init(); + void InitializeCombatTechNodes(); + void InitializeRandomResourceShares(); + bool GetTeamHasAbilityToRespawn() const; + void ResetServerPlayerData(); + void RespawnLongestWaitingPlayer(); + void UpdateAlerts(); + void UpdateHints(); + void UpdateCommanderScore(); + void UpdateInventoryEnabledState(); + void UpdateReinforcementWave(); + void UpdateOrders(); + void UpdateResearch(); + void UpdateResources(); + void UpdateServerPlayerData(); + void UpdateTeamStructures(); + void UpdatePlayers(); + void UpdateVoting(); + + float WithdrawPointsViaRandomShares(int inPlayerIndex); + + AvHClassType mTeamType; + string mTeamName; + string mTeamPrettyName; + + AvHTeamNumber mTeamNumber; + float mLastResourceUpdateTime; + float mLastCommandScoreUpdateTime; + float mLastInjectionTime; + float mLastHiveSpawnTime; + float mLastPlayerUpdateTime; + float mLastServerPlayerDataUpdateTime; + Vector mStartingLocation; + + // List of player entindices + EntityListType mPlayerList; + EntityListType mResourceTowerList; + + OrderListType mOrderList; + + //EntityListType mPlayersCompletingOrder; + + int mCommander; + int mCommanderWhenVoteStarted; + + // Voting variables + bool mVoteStarted; + int mVotes; + int mPreviousVotes; + EntityListType mVotingPlayers; + int mVotesNeeded; + float mTimeVoteStarted; + + float mTeamResources; + float mHandicap; + + //AvHMessageID mResearchingTech; + AvHResearchManager mResearchManager; + + AvHAlienUpgradeListType mAlienUpgrades; + int mTeamWideUpgrades; + + int mNumWebStrands; + + typedef vector RandomResourceShareListType; + RandomResourceShareListType mRandomResourceShares; + + typedef map PlayerSharesListType; + PlayerSharesListType mPlayerShares; + + int mTotalShareWeight; + + typedef map AvHServerPlayerDataListType; + AvHServerPlayerDataListType mServerPlayerData; + + HiveInfoListType mHiveInfoList; + float mTimeOfLastStructureUpdate; + + EntityListType mLowResourceAlerts; + float mTimeLastHintUpdate; + float mTimeOfLastTeamHint; + + EntityListType mCommandersToldAboutJumpKey; + + bool mPlayFunSoundsThisGame; + HUDSoundListType mFunSoundsPlayed; + bool mPlayedSeeDeadPeople; + + bool mIsReady; + + AvHTechSlotManager mTechSlotManager; + + float mTotalResourcesGathered; + int mClientTotalResourcesGathered; + + int mMenuTechSlots; + int mNumActiveHives; + int mNumBuiltCommandStations; + + // Store alerts by message ID. This message ID is used to fetch then when the commander presses the button on the UI. General alerts use MESSAGE_NULL. + typedef map MessageAlertListType; + MessageAlertListType mAlertList; + + EntityListType mGroups[kNumHotkeyGroups]; + AvHUser3 mGroupTypes[kNumHotkeyGroups]; + EntityListType mSelectAllGroup; + + float mTimeReinforcementWaveComplete; +}; + #endif \ No newline at end of file diff --git a/main/source/mod/AvHTeamHierarchy.cpp b/main/source/mod/AvHTeamHierarchy.cpp index f3195db..cb14fa1 100644 --- a/main/source/mod/AvHTeamHierarchy.cpp +++ b/main/source/mod/AvHTeamHierarchy.cpp @@ -44,6 +44,10 @@ void AvHTeamHierarchy::paint() AvHSpriteEnableVGUI(true); AvHSpriteSetVGUIOffset(theDrawInfo.mX, theDrawInfo.mY); + AvHTeamHierarchy* tmp; + if(gHUD.GetManager().GetVGUIComponentNamed(kHierarchy, tmp)) + if ( tmp==this && gHUD.GetHUDUser3() == AVH_USER3_COMMANDER_PLAYER) + theDrawInfo.mCommander=true; gHUD.GetOverviewMap().Draw(theDrawInfo); // Evil awful hack that must be done for mouse cursors to work :( @@ -67,7 +71,8 @@ void AvHTeamHierarchy::GetDrawInfo(AvHOverviewMap::DrawInfo& outDrawInfo) theOverviewMap.GetMapExtents(theMapExtents); outDrawInfo.mFullScreen = mFullScreen; - + outDrawInfo.mCommander=false; + outDrawInfo.mZoomScale = 1.0f; if (this->mFullScreen) { diff --git a/main/source/mod/AvHTechTree.cpp b/main/source/mod/AvHTechTree.cpp index ae8cb77..4b6ffea 100644 --- a/main/source/mod/AvHTechTree.cpp +++ b/main/source/mod/AvHTechTree.cpp @@ -1,619 +1,619 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: AvHTechTree.cpp $ -// $Date: 2002/09/23 22:36:08 $ -// -//=============================================================================== - -#include "mod/AvHTechTree.h" - -bool AvHSHUGetIsResearchTech(AvHMessageID inMessageID); - -AvHTechTree::AvHTechTree(void) { Init(); } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -AvHTechTree::AvHTechTree(const AvHTechTree& other) -{ - //because pointers in mNodesByMsg are deleted by - //destruction, we need to make our own copy on - //construction - TechNodeMap::const_iterator current, end = other.mNodesByMsg.end(); - for( current = other.mNodesByMsg.begin(); current != end; ++current ) - { - mNodesByMsg[current->first] = current->second->clone(); - } - Init(); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -AvHTechTree::~AvHTechTree(void) { Clear(); } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::Init(void) -{ -#ifdef SERVER - BALANCE_LISTENER(this); - compoundChangeInProgress = false; -#endif -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::swap(AvHTechTree& other) -{ - TechNodeMap tempMap = mNodesByMsg; mNodesByMsg = other.mNodesByMsg; other.mNodesByMsg = tempMap; - TechIDMap tempIDMap = mNodesByTech; mNodesByTech = other.mNodesByTech; other.mNodesByTech = tempIDMap; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::InsertNode(const AvHTechNode* inTechNode) -{ - this->RemoveNode(inTechNode->getMessageID()); //remove old node to prevent memory leak - this->mNodesByMsg[inTechNode->getMessageID()] = inTechNode->clone(); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::RemoveNode(const AvHMessageID inMessageID) -{ - AvHTechNode* node = GetNode(inMessageID); - if( node ) - { - mNodesByMsg.erase(node->getMessageID()); - //remove from tech map if it's a match before we delete it! - TechIDMap::iterator item = mNodesByTech.find(node->getTechID()); - if( item != mNodesByTech.end() && item->second == node ) - { mNodesByTech.erase(item); } - delete node; - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::Clear(void) -{ - TechNodeMap::iterator current, end = mNodesByMsg.end(); - for( current = mNodesByMsg.begin(); current != end; ++current ) - { - delete current->second; - } - mNodesByMsg.clear(); - mNodesByTech.clear(); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -AvHTechNode* AvHTechTree::GetNode(const AvHMessageID message) -{ - AvHTechNode* returnVal = NULL; - TechNodeMap::iterator item = mNodesByMsg.find(message); - if( item != mNodesByMsg.end() ) - { returnVal = item->second; } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -const AvHTechNode* AvHTechTree::GetNode(const AvHMessageID message) const -{ - const AvHTechNode* returnVal = NULL; - TechNodeMap::const_iterator item = mNodesByMsg.find(message); - if( item != mNodesByMsg.end() ) - { returnVal = item->second; } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -AvHTechNode* AvHTechTree::GetNodeByTech(const AvHTechID inTech) -{ - AvHTechNode* returnVal = NULL; - TechIDMap::iterator item = mNodesByTech.find(inTech); - if( item != mNodesByTech.end() ) - { returnVal = item->second; } - else - { - TechNodeMap::iterator current, end = mNodesByMsg.end(); - for( current = mNodesByMsg.begin(); current != end; ++current ) - { - if( current->second->getTechID() == inTech ) - { - mNodesByTech[inTech] = current->second; - returnVal = current->second; - break; - } - } - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -const AvHTechNode* AvHTechTree::GetNodeByTech(const AvHTechID inTech) const -{ - const AvHTechNode* returnVal = NULL; - TechIDMap::const_iterator item = mNodesByTech.find(inTech); - if( item != mNodesByTech.end() ) - { returnVal = item->second; } - else - { - TechNodeMap::const_iterator current, end = mNodesByMsg.end(); - for( current = mNodesByMsg.begin(); current != end; ++current ) - { - if( current->second->getTechID() == inTech ) - { - mNodesByTech[inTech] = current->second; - returnVal = current->second; - break; - } - } - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -const int AvHTechTree::GetSize(void) const -{ - return (int)mNodesByTech.size(); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetAllowMultiples(const AvHMessageID inMessageID) const -{ - const AvHTechNode* Node = GetNode(inMessageID); - return (Node != NULL) && Node->getAllowMultiples(); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetIsMessageInTechTree(const AvHMessageID inMessageID) const -{ - const AvHTechNode* Node = GetNode(inMessageID); - return (Node != NULL); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetIsMessageAvailable(const AvHMessageID inMessageID) const -{ - bool returnVal = true; - - const AvHTechNode* Subnode; - const AvHTechNode* Node = GetNode(inMessageID); - - // tankefugl: 73 - // HACK to bypass the node checks when issuing one of the CC UI impulses - // Could probably be reworked prettier by assigning nodes to each of these - // messages, but this will have to do for now - if (inMessageID == COMMANDER_SELECTALL || - inMessageID == COMMANDER_NEXTAMMO || - inMessageID == COMMANDER_NEXTHEALTH || - inMessageID == COMMANDER_NEXTIDLE || - inMessageID == GROUP_SELECT_1 || - inMessageID == GROUP_SELECT_2 || - inMessageID == GROUP_SELECT_3 || - inMessageID == GROUP_SELECT_4 || - inMessageID == GROUP_SELECT_5) - return true; - // :tankefugl - if( Node == NULL ) //not found - { returnVal = false; } - else if( Node->getIsResearched() && !Node->getAllowMultiples() ) //can only research once? - { returnVal = false; } - else - { - AvHTechID prereq = Node->getPrereqTechID1(); - if( prereq != TECH_NULL && ( (Subnode = GetNodeByTech(prereq)) == NULL || !Subnode->getIsResearched() ) ) - { returnVal = false; } - else - { - prereq = Node->getPrereqTechID2(); - if( prereq != TECH_NULL && ( (Subnode = GetNodeByTech(prereq)) == NULL || !Subnode->getIsResearched() ) ) - { returnVal = false; } - } - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetIsMessageAvailableForSelection(const AvHMessageID inMessageID, const EntityListType& inSelection) const -{ - return GetIsMessageAvailable(inMessageID); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetIsTechResearched(AvHTechID inTech) const -{ - const AvHTechNode* Node = GetNodeByTech(inTech); - return (Node != NULL) && Node->getIsResearched(); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::SetFirstNodeWithTechResearchState(const AvHTechID inTech, bool inState) -{ - bool returnVal = false; - AvHTechNode* Node = GetNodeByTech(inTech); - if( Node != NULL ) - { - Node->setResearchState(inState); - returnVal = true; - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetMessageForTech(const AvHTechID inTech, AvHMessageID& outMessageID) const -{ - bool returnVal = false; - const AvHTechNode* Node = GetNodeByTech(inTech); - if( Node != NULL ) - { - outMessageID = Node->getMessageID(); - returnVal = true; - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -AvHMessageID AvHTechTree::GetNextMessageNeededFor(const AvHMessageID inMessageID) const -{ - //perform depth first search for item that hasn't been researched but can be; returns - //original ID if no match is found... would be better to return boolean false with an - //output AvHMessageID specified as a parameter. - AvHMessageID returnVal = inMessageID; - const AvHTechNode* Node = GetNode(inMessageID); - bool prereq1_researched = false; - - if( Node != NULL && !Node->getIsResearched() ) - { - vector ParentStack; - ParentStack.push_back(Node); - const AvHTechNode* Child; - AvHTechID prereq; - - while(!ParentStack.empty()) - { - Node = ParentStack.back(); - ParentStack.pop_back(); - - //First child - prereq1_researched = false; - prereq = Node->getPrereqTechID1(); - if( prereq == TECH_NULL ) - { prereq1_researched = true; } - else - { - Child = GetNodeByTech(prereq); - if( Child != NULL ) - { - if( Child->getIsResearched() ) - { prereq1_researched = true; } - else - { ParentStack.push_back(Child); } - } - } - - //Second child - prereq = Node->getPrereqTechID2(); - if( prereq == TECH_NULL && prereq1_researched ) - { - returnVal = Node->getMessageID(); - break; - } - else - { - Child = GetNodeByTech(prereq); - if( Child != NULL ) - { - if( Child->getIsResearched() ) - { - if( prereq1_researched ) - { - returnVal = Node->getMessageID(); - break; - } - } - else - { ParentStack.push_back(Child); } - } - } - } - } - - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::GetResearchNodesDependentOn(AvHTechID inTechID, TechNodeMap& outTechNodes) const -{ - //move up tree from supplied base tech; this won't be a cheap operation - worst case is (tree size)^2 - if(inTechID != TECH_NULL) - { - TechNodeMap::const_iterator current, end = mNodesByMsg.end(); - for( current = mNodesByMsg.begin(); current != end; ++current ) - { - if( !AvHSHUGetIsResearchTech(current->first) || current->second->getTechID() == TECH_NULL ) - { continue; } - - if( current->second->getPrereqTechID1() == inTechID || current->second->getPrereqTechID2() == inTechID ) - { - outTechNodes[current->first] = current->second; //don't clone here, caller not responsible for deletion - GetResearchNodesDependentOn(current->second->getTechID(), outTechNodes); //recurse - } - } - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetTechForMessage(const AvHMessageID inMessageID, AvHTechID& outTechID) const -{ - bool returnVal = false; - const AvHTechNode* Node = GetNode(inMessageID); - if( Node != NULL ) - { - outTechID = Node->getTechID(); - returnVal = true; - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetPrequisitesForMessage(const AvHMessageID inMessageID, AvHTechID& outTech1, AvHTechID& outTech2) const -{ - bool returnVal = false; - const AvHTechNode* Node = GetNode(inMessageID); - if( Node != NULL ) - { - outTech1 = Node->getPrereqTechID1(); - outTech2 = Node->getPrereqTechID2(); - returnVal = (outTech1 != TECH_NULL) || (outTech2 != TECH_NULL); - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::GetResearchInfo(AvHMessageID inMessageID, bool& outIsResearchable, int& outCost, float& outTime) const -{ - bool returnVal = false; - const AvHTechNode* Node = GetNode(inMessageID); - if( Node != NULL ) - { - outIsResearchable = Node->getIsResearchable(); - outCost = Node->getCost(); - outTime = Node->getBuildTime(); - returnVal = true; - } - else - { - outIsResearchable = false; - outCost = -1; - outTime = -1; - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::SetIsResearchable(AvHMessageID inMessageID, bool inState) -{ - bool returnVal = false; - AvHTechNode* Node = GetNode(inMessageID); - if( Node != NULL ) - { - Node->setResearchable(inState); - returnVal = true; - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::SetResearchDone(AvHMessageID inMessageID, bool inState) -{ - bool returnVal = false; - AvHTechNode* Node = GetNode(inMessageID); - if( Node != NULL ) - { - Node->setResearchState(inState); - returnVal = true; - } - return returnVal; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::TriggerAddTech(AvHTechID inTechID) -{ - AvHTechNode* Node = GetNodeByTech(inTechID); - if( Node != NULL ) - { - Node->setResearchState(true); - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::TriggerRemoveTech(AvHTechID inTechID) -{ - AvHTechNode* Node = GetNodeByTech(inTechID); - if( Node != NULL ) - { - Node->setResearchState(false); - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void AvHTechTree::GetDelta(const AvHTechTree& other, MessageIDListType& delta) const -{ - - delta.clear(); - TechNodeMap::const_iterator current = mNodesByMsg.begin(); - TechNodeMap::const_iterator end = mNodesByMsg.end(); - TechNodeMap::const_iterator other_current = other.mNodesByMsg.begin(); - TechNodeMap::const_iterator other_end = other.mNodesByMsg.end(); - while( current != end && other_current != other_end ) - { - if( current->first < other_current->first ) - { - delta.push_back(current->first); - ++current; - continue; - } - - if( current->first > other_current->first ) - { - delta.push_back(current->first); - ++other_current; - continue; - } - - if( *current->second != *other_current->second ) - { - delta.push_back(current->first); - } - - ++current; - ++other_current; - } - - while( current != end ) - { - delta.push_back(current->first); - ++current; - } - - while( other_current != other_end ) - { - delta.push_back(other_current->first); - ++other_current; - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::operator!=(const AvHTechTree& inTree) const -{ - return !this->operator==(inTree); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -bool AvHTechTree::operator==(const AvHTechTree& inTree) const -{ - return mNodesByMsg == inTree.mNodesByMsg; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -AvHTechTree& AvHTechTree::operator=(const AvHTechTree& inTree) -{ - AvHTechTree temp(inTree); - swap(temp); - return *this; -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#ifdef AVH_SERVER -bool AvHTechTree::shouldNotify(const string& name, const BalanceValueType type) const { return true; } - -void AvHTechTree::balanceCleared(void) const {} - -void AvHTechTree::balanceValueInserted(const string& name, const float value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceValueInserted(const string& name, const int value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceValueInserted(const string& name, const string& value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceValueRemoved(const string& name, const float old_value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceValueRemoved(const string& name, const int old_value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceValueRemoved(const string& name, const string& old_value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceValueChanged(const string& name, const float old_value, const float new_value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceValueChanged(const string& name, const int old_value, const int new_value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceValueChanged(const string& name, const string& old_value, const string& default_value) const -{ - if( !compoundChangeInProgress ) - { const_cast(this)->processBalanceChange(); } -} - -void AvHTechTree::balanceStartCompoundChange(void) const -{ compoundChangeInProgress = true; } - -void AvHTechTree::balanceEndCompoundChange(void) const -{ compoundChangeInProgress = false; } - -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "mod/AvHGamerules.h" -void AvHTechTree::processBalanceChange(void) -{ - // Run through our tech nodes and update cost and build time - TechNodeMap::iterator current, end = mNodesByMsg.end(); - for( current = mNodesByMsg.begin(); current != end; ++current ) - { - current->second->setBuildTime(GetGameRules()->GetBuildTimeForMessageID(current->first)); - current->second->setCost(GetGameRules()->GetCostForMessageID(current->first)); - } -} - +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: AvHTechTree.cpp $ +// $Date: 2002/09/23 22:36:08 $ +// +//=============================================================================== + +#include "mod/AvHTechTree.h" + +bool AvHSHUGetIsResearchTech(AvHMessageID inMessageID); + +AvHTechTree::AvHTechTree(void) { Init(); } + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AvHTechTree::AvHTechTree(const AvHTechTree& other) +{ + //because pointers in mNodesByMsg are deleted by + //destruction, we need to make our own copy on + //construction + TechNodeMap::const_iterator current, end = other.mNodesByMsg.end(); + for( current = other.mNodesByMsg.begin(); current != end; ++current ) + { + mNodesByMsg[current->first] = current->second->clone(); + } + Init(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AvHTechTree::~AvHTechTree(void) { Clear(); } + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::Init(void) +{ +#ifdef SERVER + BALANCE_LISTENER(this); + compoundChangeInProgress = false; +#endif +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::swap(AvHTechTree& other) +{ + TechNodeMap tempMap = mNodesByMsg; mNodesByMsg = other.mNodesByMsg; other.mNodesByMsg = tempMap; + TechIDMap tempIDMap = mNodesByTech; mNodesByTech = other.mNodesByTech; other.mNodesByTech = tempIDMap; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::InsertNode(const AvHTechNode* inTechNode) +{ + this->RemoveNode(inTechNode->getMessageID()); //remove old node to prevent memory leak + this->mNodesByMsg[inTechNode->getMessageID()] = inTechNode->clone(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::RemoveNode(const AvHMessageID inMessageID) +{ + AvHTechNode* node = GetNode(inMessageID); + if( node ) + { + mNodesByMsg.erase(node->getMessageID()); + //remove from tech map if it's a match before we delete it! + TechIDMap::iterator item = mNodesByTech.find(node->getTechID()); + if( item != mNodesByTech.end() && item->second == node ) + { mNodesByTech.erase(item); } + delete node; + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::Clear(void) +{ + TechNodeMap::iterator current, end = mNodesByMsg.end(); + for( current = mNodesByMsg.begin(); current != end; ++current ) + { + delete current->second; + } + mNodesByMsg.clear(); + mNodesByTech.clear(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AvHTechNode* AvHTechTree::GetNode(const AvHMessageID message) +{ + AvHTechNode* returnVal = NULL; + TechNodeMap::iterator item = mNodesByMsg.find(message); + if( item != mNodesByMsg.end() ) + { returnVal = item->second; } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +const AvHTechNode* AvHTechTree::GetNode(const AvHMessageID message) const +{ + const AvHTechNode* returnVal = NULL; + TechNodeMap::const_iterator item = mNodesByMsg.find(message); + if( item != mNodesByMsg.end() ) + { returnVal = item->second; } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AvHTechNode* AvHTechTree::GetNodeByTech(const AvHTechID inTech) +{ + AvHTechNode* returnVal = NULL; + TechIDMap::iterator item = mNodesByTech.find(inTech); + if( item != mNodesByTech.end() ) + { returnVal = item->second; } + else + { + TechNodeMap::iterator current, end = mNodesByMsg.end(); + for( current = mNodesByMsg.begin(); current != end; ++current ) + { + if( current->second->getTechID() == inTech ) + { + mNodesByTech[inTech] = current->second; + returnVal = current->second; + break; + } + } + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +const AvHTechNode* AvHTechTree::GetNodeByTech(const AvHTechID inTech) const +{ + const AvHTechNode* returnVal = NULL; + TechIDMap::const_iterator item = mNodesByTech.find(inTech); + if( item != mNodesByTech.end() ) + { returnVal = item->second; } + else + { + TechNodeMap::const_iterator current, end = mNodesByMsg.end(); + for( current = mNodesByMsg.begin(); current != end; ++current ) + { + if( current->second->getTechID() == inTech ) + { + mNodesByTech[inTech] = current->second; + returnVal = current->second; + break; + } + } + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +const int AvHTechTree::GetSize(void) const +{ + return (int)mNodesByTech.size(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetAllowMultiples(const AvHMessageID inMessageID) const +{ + const AvHTechNode* Node = GetNode(inMessageID); + return (Node != NULL) && Node->getAllowMultiples(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetIsMessageInTechTree(const AvHMessageID inMessageID) const +{ + const AvHTechNode* Node = GetNode(inMessageID); + return (Node != NULL); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetIsMessageAvailable(const AvHMessageID inMessageID) const +{ + bool returnVal = true; + + const AvHTechNode* Subnode; + const AvHTechNode* Node = GetNode(inMessageID); + + // : 73 + // HACK to bypass the node checks when issuing one of the CC UI impulses + // Could probably be reworked prettier by assigning nodes to each of these + // messages, but this will have to do for now + if (inMessageID == COMMANDER_SELECTALL || + inMessageID == COMMANDER_NEXTAMMO || + inMessageID == COMMANDER_NEXTHEALTH || + inMessageID == COMMANDER_NEXTIDLE || + inMessageID == GROUP_SELECT_1 || + inMessageID == GROUP_SELECT_2 || + inMessageID == GROUP_SELECT_3 || + inMessageID == GROUP_SELECT_4 || + inMessageID == GROUP_SELECT_5) + return true; + // : + if( Node == NULL ) //not found + { returnVal = false; } + else if( Node->getIsResearched() && !Node->getAllowMultiples() ) //can only research once? + { returnVal = false; } + else + { + AvHTechID prereq = Node->getPrereqTechID1(); + if( prereq != TECH_NULL && ( (Subnode = GetNodeByTech(prereq)) == NULL || !Subnode->getIsResearched() ) ) + { returnVal = false; } + else + { + prereq = Node->getPrereqTechID2(); + if( prereq != TECH_NULL && ( (Subnode = GetNodeByTech(prereq)) == NULL || !Subnode->getIsResearched() ) ) + { returnVal = false; } + } + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetIsMessageAvailableForSelection(const AvHMessageID inMessageID, const EntityListType& inSelection) const +{ + return GetIsMessageAvailable(inMessageID); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetIsTechResearched(AvHTechID inTech) const +{ + const AvHTechNode* Node = GetNodeByTech(inTech); + return (Node != NULL) && Node->getIsResearched(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::SetFirstNodeWithTechResearchState(const AvHTechID inTech, bool inState) +{ + bool returnVal = false; + AvHTechNode* Node = GetNodeByTech(inTech); + if( Node != NULL ) + { + Node->setResearchState(inState); + returnVal = true; + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetMessageForTech(const AvHTechID inTech, AvHMessageID& outMessageID) const +{ + bool returnVal = false; + const AvHTechNode* Node = GetNodeByTech(inTech); + if( Node != NULL ) + { + outMessageID = Node->getMessageID(); + returnVal = true; + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AvHMessageID AvHTechTree::GetNextMessageNeededFor(const AvHMessageID inMessageID) const +{ + //perform depth first search for item that hasn't been researched but can be; returns + //original ID if no match is found... would be better to return boolean false with an + //output AvHMessageID specified as a parameter. + AvHMessageID returnVal = inMessageID; + const AvHTechNode* Node = GetNode(inMessageID); + bool prereq1_researched = false; + + if( Node != NULL && !Node->getIsResearched() ) + { + vector ParentStack; + ParentStack.push_back(Node); + const AvHTechNode* Child; + AvHTechID prereq; + + while(!ParentStack.empty()) + { + Node = ParentStack.back(); + ParentStack.pop_back(); + + //First child + prereq1_researched = false; + prereq = Node->getPrereqTechID1(); + if( prereq == TECH_NULL ) + { prereq1_researched = true; } + else + { + Child = GetNodeByTech(prereq); + if( Child != NULL ) + { + if( Child->getIsResearched() ) + { prereq1_researched = true; } + else + { ParentStack.push_back(Child); } + } + } + + //Second child + prereq = Node->getPrereqTechID2(); + if( prereq == TECH_NULL && prereq1_researched ) + { + returnVal = Node->getMessageID(); + break; + } + else + { + Child = GetNodeByTech(prereq); + if( Child != NULL ) + { + if( Child->getIsResearched() ) + { + if( prereq1_researched ) + { + returnVal = Node->getMessageID(); + break; + } + } + else + { ParentStack.push_back(Child); } + } + } + } + } + + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::GetResearchNodesDependentOn(AvHTechID inTechID, TechNodeMap& outTechNodes) const +{ + //move up tree from supplied base tech; this won't be a cheap operation - worst case is (tree size)^2 + if(inTechID != TECH_NULL) + { + TechNodeMap::const_iterator current, end = mNodesByMsg.end(); + for( current = mNodesByMsg.begin(); current != end; ++current ) + { + if( !AvHSHUGetIsResearchTech(current->first) || current->second->getTechID() == TECH_NULL ) + { continue; } + + if( current->second->getPrereqTechID1() == inTechID || current->second->getPrereqTechID2() == inTechID ) + { + outTechNodes[current->first] = current->second; //don't clone here, caller not responsible for deletion + GetResearchNodesDependentOn(current->second->getTechID(), outTechNodes); //recurse + } + } + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetTechForMessage(const AvHMessageID inMessageID, AvHTechID& outTechID) const +{ + bool returnVal = false; + const AvHTechNode* Node = GetNode(inMessageID); + if( Node != NULL ) + { + outTechID = Node->getTechID(); + returnVal = true; + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetPrequisitesForMessage(const AvHMessageID inMessageID, AvHTechID& outTech1, AvHTechID& outTech2) const +{ + bool returnVal = false; + const AvHTechNode* Node = GetNode(inMessageID); + if( Node != NULL ) + { + outTech1 = Node->getPrereqTechID1(); + outTech2 = Node->getPrereqTechID2(); + returnVal = (outTech1 != TECH_NULL) || (outTech2 != TECH_NULL); + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::GetResearchInfo(AvHMessageID inMessageID, bool& outIsResearchable, int& outCost, float& outTime) const +{ + bool returnVal = false; + const AvHTechNode* Node = GetNode(inMessageID); + if( Node != NULL ) + { + outIsResearchable = Node->getIsResearchable(); + outCost = Node->getCost(); + outTime = Node->getBuildTime(); + returnVal = true; + } + else + { + outIsResearchable = false; + outCost = -1; + outTime = -1; + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::SetIsResearchable(AvHMessageID inMessageID, bool inState) +{ + bool returnVal = false; + AvHTechNode* Node = GetNode(inMessageID); + if( Node != NULL ) + { + Node->setResearchable(inState); + returnVal = true; + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::SetResearchDone(AvHMessageID inMessageID, bool inState) +{ + bool returnVal = false; + AvHTechNode* Node = GetNode(inMessageID); + if( Node != NULL ) + { + Node->setResearchState(inState); + returnVal = true; + } + return returnVal; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::TriggerAddTech(AvHTechID inTechID) +{ + AvHTechNode* Node = GetNodeByTech(inTechID); + if( Node != NULL ) + { + Node->setResearchState(true); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::TriggerRemoveTech(AvHTechID inTechID) +{ + AvHTechNode* Node = GetNodeByTech(inTechID); + if( Node != NULL ) + { + Node->setResearchState(false); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void AvHTechTree::GetDelta(const AvHTechTree& other, MessageIDListType& delta) const +{ + + delta.clear(); + TechNodeMap::const_iterator current = mNodesByMsg.begin(); + TechNodeMap::const_iterator end = mNodesByMsg.end(); + TechNodeMap::const_iterator other_current = other.mNodesByMsg.begin(); + TechNodeMap::const_iterator other_end = other.mNodesByMsg.end(); + while( current != end && other_current != other_end ) + { + if( current->first < other_current->first ) + { + delta.push_back(current->first); + ++current; + continue; + } + + if( current->first > other_current->first ) + { + delta.push_back(current->first); + ++other_current; + continue; + } + + if( *current->second != *other_current->second ) + { + delta.push_back(current->first); + } + + ++current; + ++other_current; + } + + while( current != end ) + { + delta.push_back(current->first); + ++current; + } + + while( other_current != other_end ) + { + delta.push_back(other_current->first); + ++other_current; + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::operator!=(const AvHTechTree& inTree) const +{ + return !this->operator==(inTree); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bool AvHTechTree::operator==(const AvHTechTree& inTree) const +{ + return mNodesByMsg == inTree.mNodesByMsg; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AvHTechTree& AvHTechTree::operator=(const AvHTechTree& inTree) +{ + AvHTechTree temp(inTree); + swap(temp); + return *this; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifdef AVH_SERVER +bool AvHTechTree::shouldNotify(const string& name, const BalanceValueType type) const { return true; } + +void AvHTechTree::balanceCleared(void) const {} + +void AvHTechTree::balanceValueInserted(const string& name, const float value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceValueInserted(const string& name, const int value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceValueInserted(const string& name, const string& value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceValueRemoved(const string& name, const float old_value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceValueRemoved(const string& name, const int old_value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceValueRemoved(const string& name, const string& old_value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceValueChanged(const string& name, const float old_value, const float new_value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceValueChanged(const string& name, const int old_value, const int new_value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceValueChanged(const string& name, const string& old_value, const string& default_value) const +{ + if( !compoundChangeInProgress ) + { const_cast(this)->processBalanceChange(); } +} + +void AvHTechTree::balanceStartCompoundChange(void) const +{ compoundChangeInProgress = true; } + +void AvHTechTree::balanceEndCompoundChange(void) const +{ compoundChangeInProgress = false; } + +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "mod/AvHGamerules.h" +void AvHTechTree::processBalanceChange(void) +{ + // Run through our tech nodes and update cost and build time + TechNodeMap::iterator current, end = mNodesByMsg.end(); + for( current = mNodesByMsg.begin(); current != end; ++current ) + { + current->second->setBuildTime(GetGameRules()->GetBuildTimeForMessageID(current->first)); + current->second->setCost(GetGameRules()->GetCostForMessageID(current->first)); + } +} + #endif \ No newline at end of file diff --git a/main/source/mod/AvHTitles.h b/main/source/mod/AvHTitles.h index df16266..791d088 100644 --- a/main/source/mod/AvHTitles.h +++ b/main/source/mod/AvHTitles.h @@ -81,6 +81,7 @@ #define kMustGestateUp "MustGestateUp" #define kMustGestateOnGround "MustGestateOnGround" #define kNotWhileDigesting "NotWhileDigesting" +#define kSurfaceTooSteep "SurfaceTooSteep" #define kNoReadyRoomWhileDigested "NoReadyRoomWhileDigested" #define kNeedMoreRoomToGestate "NeedMoreRoomToGestate" #define kNeedOneHiveToGestate "NeedOneHiveToGestate" @@ -94,6 +95,7 @@ #define kUpgradeNotAvailable "UpgradeNotAvailable" #define kTooManyStructuresInArea "TooManyStructuresInArea" #define kTooManyStructuresOfThisTypeNearby "TooManyStructuresOfThisTypeNearby" +#define kTooManyStructuresOnServer "TooManyStructuresOnServer" #define kTooManyWebs "TooManyWebs" #define kTooManyWebsNearby "TooManyWebsNearby" diff --git a/main/source/mod/AvHTurret.cpp b/main/source/mod/AvHTurret.cpp index 2abfb90..9a58e64 100644 --- a/main/source/mod/AvHTurret.cpp +++ b/main/source/mod/AvHTurret.cpp @@ -426,7 +426,7 @@ void AvHTurret::ActiveThink(void) if(!FNullEnt(this->m_hEnemy)) { // If enemy is in FOV - Vector theVecMid = this->pev->origin + this->pev->view_ofs; + Vector theVecMid = this->EyePosition(); //AvHSUPlayParticleEvent("JetpackEffect", this->edict(), theVecMid); CBaseEntity* theEnemyEntity = this->m_hEnemy; diff --git a/main/source/mod/AvHWorldUpdate.cpp b/main/source/mod/AvHWorldUpdate.cpp index 80518ee..6bedbd0 100644 --- a/main/source/mod/AvHWorldUpdate.cpp +++ b/main/source/mod/AvHWorldUpdate.cpp @@ -1,530 +1,531 @@ -//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= -// -// The copyright to the contents herein is the property of Charles G. Cleveland. -// The contents may be used and/or copied only with the written permission of -// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//------------------------------------------------------------------------------- -// $Log: $ -//=============================================================================== -#include "util/nowarnings.h" -#include "dlls/extdll.h" -#include "dlls/util.h" -#include "dlls/cbase.h" -#include "dlls/player.h" -#include "dlls/weapons.h" -#include "mod/AvHGamerules.h" -#include "mod/AvHMarineEquipment.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHAlienEquipment.h" -#include "mod/AvHAlienEquipmentConstants.h" -#include "mod/AvHAlienWeapons.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHServerUtil.h" -#include "util/MathUtil.h" - -typedef vector AvHPlayerListType; -typedef vector AvHObservatoryListType; -typedef vector AvHScanListType; -typedef vector AvHSensoryChamberListType; -typedef vector AvHUmbraCloudListType; - -BaseEntityListType gBaseEntityList; -AvHPlayerListType gPlayerList; -AvHPlayerListType gRelevantPlayerList; -AvHObservatoryListType gObservatoryList; -AvHScanListType gScanList; -AvHSensoryChamberListType gSensoryChamberList; -AvHUmbraCloudListType gUmbraCloudList; - -const float kMovementVisibilityThreshold = 10.0f; - -bool AvHSUGetInViewOfEnemy(CBaseEntity* inEntity, int& outSightedStatus) -{ - bool theInViewOfEnemy = false; - - if(inEntity->pev->iuser4 & MASK_TOPDOWN) - { - // Top down players are never visible - } - else - { - if(GetGameRules()->GetDrawInvisibleEntities()) - { - outSightedStatus |= MASK_VIS_SIGHTED; - theInViewOfEnemy = true; - } - else if(GetGameRules()->GetIsCheatEnabled(kcDetectAll)) - { - outSightedStatus |= MASK_VIS_DETECTED; - theInViewOfEnemy = true; - } - else - { - AvHPlayer* theInPlayer = dynamic_cast(inEntity); - AvHCloakable* theCloakable = dynamic_cast(inEntity); - if(!theInPlayer || !theInPlayer->GetIsCloaked()) - { - if(!theCloakable || (theCloakable->GetOpacity() > 0.0f)) - { - // Loop through enemy players, check if we are in view of any of them - for(AvHPlayerListType::iterator thePlayerIter = gPlayerList.begin(); thePlayerIter != gPlayerList.end(); thePlayerIter++) - { - if((*thePlayerIter)->GetTeam() != inEntity->pev->team) - { - // Commanders can't "see" enemies - if(((*thePlayerIter)->GetUser3() != AVH_USER3_COMMANDER_PLAYER) && ((*thePlayerIter)->GetIsRelevant())) - { - if((*thePlayerIter)->GetIsEntityInSight(inEntity)) - { - outSightedStatus |= MASK_VIS_SIGHTED; - theInViewOfEnemy = true; - break; - } - } - } - } - } - - // Loop through observatories, uncloaking and detecting all enemy players in range - for(AvHObservatoryListType::iterator theObservatoryIter = gObservatoryList.begin(); theObservatoryIter != gObservatoryList.end(); theObservatoryIter++) - { - if((*theObservatoryIter)->pev->team != inEntity->pev->team && !(*theObservatoryIter)->GetIsRecycling() ) - { - // Check that entity is in range of scan (only check XY distance, for commander's purposes) - float theDistance = VectorDistance2D((*theObservatoryIter)->pev->origin, inEntity->pev->origin); - if(theDistance < BALANCE_VAR(kObservatoryXYDetectionRadius)) - { - outSightedStatus |= MASK_VIS_DETECTED; - theInViewOfEnemy = true; - - if(theCloakable) - { - theCloakable->Uncloak(); - } - break; - } - } - } - - // Loop through all active scans on our team - for(AvHScanListType::iterator theScanIter = gScanList.begin(); theScanIter != gScanList.end(); theScanIter++) - { - if((*theScanIter)->pev->team != inEntity->pev->team) - { - // Check that entity is in range of scan - float theDistance = VectorDistance((*theScanIter)->pev->origin, inEntity->pev->origin); - if(theDistance < BALANCE_VAR(kScanRadius)) - { - outSightedStatus |= MASK_VIS_SIGHTED; - theInViewOfEnemy = true; - break; - } - } - } - } - } - - // If not in sight, check for motion-tracking - if(!theInViewOfEnemy) - { - bool theEnemyTeamHasMotionTracking = false; - AvHTeamNumber teamA = GetGameRules()->GetTeamA()->GetTeamNumber(); - AvHTeamNumber teamB = GetGameRules()->GetTeamB()->GetTeamNumber(); - if((inEntity->pev->team == teamA) || (inEntity->pev->team == teamB)) - { - AvHTeamNumber theEnemyTeamNumber = (inEntity->pev->team == teamA) ? teamB : teamA; - AvHTeam* theEnemyTeam = GetGameRules()->GetTeam(theEnemyTeamNumber); - if(theEnemyTeam) - { - if(theEnemyTeam->GetResearchManager().GetTechNodes().GetIsTechResearched(TECH_RESEARCH_MOTIONTRACK) || GetGameRules()->GetIsCombatMode()) - { - // Motion-tracking doesn't pick up cloaked entities (players) - bool theIsCloaked = false; - AvHCloakable* theCloakable = dynamic_cast(inEntity); - if(theCloakable && (theCloakable->GetOpacity() < 0.1f)) - { - theIsCloaked = true; - } - - float theVelocity = inEntity->pev->velocity.Length(); - - //ELVEN - WE HAVE TO CHECK FOR EXISTANT OBSERVATORIES BEFORE WE CAN FLAG THIS. - //voogru: Fixed combat mode problems & slight perfoamance issue (no need to loop thru every obs). - bool obsExists = false; - - if(!GetGameRules()->GetIsCombatMode()) - { - FOR_ALL_ENTITIES(kwsObservatory, AvHObservatory*) - if(theEntity->GetIsBuilt()) - { - obsExists = true; - break; - } - END_FOR_ALL_ENTITIES(kwsObservatory) - } - else - { - obsExists = true; - } - - if((theVelocity > kMovementVisibilityThreshold) && !theIsCloaked && obsExists) - { - outSightedStatus |= MASK_VIS_DETECTED; - theInViewOfEnemy = true; - } - } - } - } - } - } - - return theInViewOfEnemy; -} - -bool AvHSUGetInRangeOfFriendlyPrimalScream(CBaseEntity* inEntity) -{ - bool inRangeOfPrimalScream = false; - - int theTeamNumber = inEntity->pev->team; - if(theTeamNumber) - { - // If team is of type alien - const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(theTeamNumber)); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && inEntity->IsAlive()) - { - // Loop through all players on our team - for(AvHPlayerListType::iterator theIter = gPlayerList.begin(); theIter != gPlayerList.end(); theIter++) - { - AvHPlayer* thePlayer = *theIter; - - // See if any of them are screaming - if(thePlayer && (thePlayer->pev->team == theTeamNumber) && (thePlayer->GetIsScreaming())) - { - // Are they in range of us? - float theDistance = VectorDistance(inEntity->pev->origin, thePlayer->pev->origin); - if(theDistance < BALANCE_VAR(kPrimalScreamRange)) - { - inRangeOfPrimalScream = true; - break; - } - } - } - } - } - - return inRangeOfPrimalScream; -} - -bool AvHSUGetInRangeOfFriendlySensoryChamber(CBaseEntity* inEntity) -{ - bool inRangeOfSensoryChamber = false; - - int theTeamNumber = inEntity->pev->team; - if(theTeamNumber) - { - // If team is of type alien - const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(theTeamNumber)); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && inEntity->IsAlive()) - { - // Loop through all SensoryChamber clouds on our team - for(AvHSensoryChamberListType::const_iterator theIter = gSensoryChamberList.begin(); theIter != gSensoryChamberList.end(); theIter++) - { - AvHSensoryChamber* theChamber = *theIter; - if(theChamber && (theChamber->pev->team == theTeamNumber)) - { - // Are we in range? - float theDistance = VectorDistance(inEntity->pev->origin, theChamber->pev->origin); - if(theDistance < BALANCE_VAR(kSensoryChamberRange)) - { - AvHBaseBuildable* theBuildable = dynamic_cast(theChamber); - if(theBuildable && theBuildable->GetIsBuilt()) - { - inRangeOfSensoryChamber = true; - break; - } - } - } - } - } - } - - return inRangeOfSensoryChamber; -} - -bool AvHSUGetInRangeOfEnemySensoryChamber(CBaseEntity* inEntity) -{ - bool inRangeOfSensoryChamber = false; - int theTeamNumber = inEntity->pev->team; - if(theTeamNumber) - { - // If team is of type marine - const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(theTeamNumber)); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) && inEntity->IsAlive()) - { - // Loop through all SensoryChamber clouds on our team - for(AvHSensoryChamberListType::const_iterator theIter = gSensoryChamberList.begin(); theIter != gSensoryChamberList.end(); theIter++) - { - AvHSensoryChamber* theChamber = *theIter; - if(theChamber && (theChamber->pev->team != theTeamNumber)) - { - // Are we in range? - float theDistance = VectorDistance(inEntity->pev->origin, theChamber->pev->origin); - if(theDistance < BALANCE_VAR(kSensoryChamberRange)) - { - AvHBaseBuildable* theBuildable = dynamic_cast(theChamber); - if(theBuildable && theBuildable->GetIsBuilt()) - { - inRangeOfSensoryChamber = true; - break; - } - } - } - } - } - } - return inRangeOfSensoryChamber; -} - -bool AvHSUGetInRangeOfFriendlyUmbra(CBaseEntity* inEntity) -{ - bool inRangeOfUmbra = false; - - int theTeamNumber = inEntity->pev->team; - if(theTeamNumber) - { - // If team is of type alien - const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(theTeamNumber)); - if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && inEntity->IsAlive()) - { - // Loop through all umbra clouds on our team - for(AvHUmbraCloudListType::iterator theIter = gUmbraCloudList.begin(); theIter != gUmbraCloudList.end(); theIter++) - { - AvHUmbraCloud* theUmbraCloud = *theIter; - if(theUmbraCloud && (theUmbraCloud->pev->team == theTeamNumber)) - { - // Are we in range? - float theDistance = VectorDistance(inEntity->pev->origin, theUmbraCloud->pev->origin); - if(theDistance < BALANCE_VAR(kUmbraCloudRadius)) - { - inRangeOfUmbra = true; - break; - } - } - } - } - } - - return inRangeOfUmbra; -} - -void UpdateWorldEntity(CBaseEntity* inBaseEntity) -{ - // Visibility - inBaseEntity->pev->iuser4 &= ~MASK_VIS_SIGHTED; - inBaseEntity->pev->iuser4 &= ~MASK_VIS_DETECTED; - - if(AvHSUGetIsSubjectToVisibilityRules(inBaseEntity)) - { - int theSightedStatus = 0; - if(AvHSUGetInViewOfEnemy(inBaseEntity, theSightedStatus)) - { - inBaseEntity->pev->iuser4 |= theSightedStatus; - if(inBaseEntity->pev->classname == MAKE_STRING(kesParticlesCustom)) - { - int a = 0; - } - } - } - - // Don't clear buff flag on marines, as it means catalysts for them and they expire in AvHPlayer::InternalMarineThink - AvHUser3 theUser3 = AvHUser3(inBaseEntity->pev->iuser3); - if(theUser3 != AVH_USER3_MARINE_PLAYER) - { - inBaseEntity->pev->iuser4 &= ~MASK_BUFFED; - } - - // Primal scream bonuses - if(AvHSUGetInRangeOfFriendlyPrimalScream(inBaseEntity)) - { - inBaseEntity->pev->iuser4 |= MASK_BUFFED; - } - - - // Deteted by sensory chambers - if(theUser3 == AVH_USER3_MARINE_PLAYER ) - { - if(AvHSUGetInRangeOfEnemySensoryChamber(inBaseEntity)) - { - SetUpgradeMask(&inBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY, true); - } - else - { - SetUpgradeMask(&inBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY, false); - } - } - - // Cloaking near sensory chambers - AvHCloakable* theCloakable = dynamic_cast(inBaseEntity); - if(theCloakable ) - { - if ( theUser3 != AVH_USER3_MARINE_PLAYER ) - { - if(AvHSUGetInRangeOfFriendlySensoryChamber(inBaseEntity)) - { - theCloakable->SetSpeeds(0.0f, 0.0f, 0.0f); - theCloakable->Cloak(); - - SetUpgradeMask(&inBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY, true); - } - else - { - // Don't uncloak if we are cloaking via the upgrade - int theCloakingLevel = AvHGetAlienUpgradeLevel(inBaseEntity->pev->iuser4, MASK_UPGRADE_7); - if(theCloakingLevel == 0) - { - theCloakable->Uncloak(); - } - - SetUpgradeMask(&inBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY, false); - } - } - else - { - theCloakable->Uncloak(); - } - } - - // Umbra defense - inBaseEntity->pev->iuser4 &= ~MASK_UMBRA; - if(AvHSUGetInRangeOfFriendlyUmbra(inBaseEntity)) - { - inBaseEntity->pev->iuser4 |= MASK_UMBRA; - } - - // Update tech slots periodically so UI shows what's available - AvHBaseBuildable* theBaseBuildable = dynamic_cast(inBaseEntity); - if(theBaseBuildable) - { - theBaseBuildable->WorldUpdate(); - } -} - -void AvHGamerules::UpdateWorldEntities() -{ - // Prepare for many calls to AvHSUGetInViewOfEnemy - ASSERT(gPlayerList.size() == 0); - ASSERT(gRelevantPlayerList.size() == 0); - ASSERT(gObservatoryList.size() == 0); - ASSERT(gScanList.size() == 0); - ASSERT(gSensoryChamberList.size() == 0); - ASSERT(gUmbraCloudList.size() == 0); - ASSERT(gBaseEntityList.size() == 0); - - PROFILE_START() - - FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) - gPlayerList.push_back(theEntity); - if(theEntity->GetIsRelevant(true)) - { - gRelevantPlayerList.push_back(theEntity); - } - END_FOR_ALL_ENTITIES(kAvHPlayerClassName) - - FOR_ALL_ENTITIES(kwsObservatory, AvHObservatory*) - if(theEntity->GetIsBuilt()) - { - gObservatoryList.push_back(theEntity); - } - END_FOR_ALL_ENTITIES(kwsObservatory) - - FOR_ALL_ENTITIES(kwsScan, AvHScan*) - gScanList.push_back(theEntity); - END_FOR_ALL_ENTITIES(kwsScan) - - FOR_ALL_ENTITIES(kwsSensoryChamber, AvHSensoryChamber*) - gSensoryChamberList.push_back(theEntity); - END_FOR_ALL_ENTITIES(kwsSensoryChamber) - - FOR_ALL_ENTITIES(kwsUmbraCloud, AvHUmbraCloud*) - gUmbraCloudList.push_back(theEntity); - END_FOR_ALL_ENTITIES(kwsUmbraCloud) - - PROFILE_END(kUpdateWorldEntitiesBuildLists) - - //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::UpdateWorldEntities\n"); - - if(GET_RUN_CODE(2048)) - { - PROFILE_START() - FOR_ALL_BASEENTITIES() - UpdateWorldEntity(theBaseEntity); - gBaseEntityList.push_back(theBaseEntity); - END_FOR_ALL_BASEENTITIES() - PROFILE_END(kUpdateWorldEntitiesUpdateWorldEntities) - } - - // Rebuild this->mTeamAEntityHierarchy and this->mTeamBEntityHierarchy if changed - PROFILE_START() - this->mTeamAEntityHierarchy.BuildFromTeam(&this->mTeamA, gBaseEntityList); - this->mTeamBEntityHierarchy.BuildFromTeam(&this->mTeamB, gBaseEntityList); - PROFILE_END(kUpdateWorldEntitiesBuildEntityHierarchies) - - // Update blips - if(GET_RUN_CODE(1024)) - { - PROFILE_START() - for(AvHPlayerListType::iterator theIter = gPlayerList.begin(); theIter != gPlayerList.end(); theIter++) - { - // Reset their blips - (*theIter)->ClearBlips(); - } - - // For all entities in the world - for(BaseEntityListType::iterator theBaseIter = gBaseEntityList.begin(); theBaseIter != gBaseEntityList.end(); theBaseIter++) - { - CBaseEntity* theBaseEntity = *theBaseIter; - - // If entity has a team (allow hives so aliens can find hive locations) - bool theIsHive = (theBaseEntity->pev->iuser3 == AVH_USER3_HIVE); - if((theBaseEntity->pev->team != 0) || theIsHive) - { - // Only process players, parasited entities and hives, as they are the only things that ever show up as blips - int theEntIndex = theBaseEntity->entindex(); - - bool theIsParasited = GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_PARASITED); - bool theIsNearSensory = GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY); - bool theIsBuildable = dynamic_cast(theBaseEntity); - - if(((theEntIndex > 0) && (theEntIndex <= kMaxPlayers)) || theIsNearSensory || theIsHive || theIsParasited || theIsBuildable) - { - // For all relevant players in list - for(AvHPlayerListType::iterator thePlayerIterator = gRelevantPlayerList.begin(); thePlayerIterator != gRelevantPlayerList.end(); thePlayerIterator++) - { - // Call ProcessEntityBlip - (*thePlayerIterator)->ProcessEntityBlip(theBaseEntity); - } - } - } - } - - PROFILE_END(kUpdateWorldEntitiesUpdateBlips) - } - - // End after many calls to AvHSUGetInViewOfEnemy - gPlayerList.clear(); - gRelevantPlayerList.clear(); - gObservatoryList.clear(); - gScanList.clear(); - gSensoryChamberList.clear(); - gUmbraCloudList.clear(); - gBaseEntityList.clear(); -} - - +//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= +// +// The copyright to the contents herein is the property of Charles G. Cleveland. +// The contents may be used and/or copied only with the written permission of +// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//------------------------------------------------------------------------------- +// $Log: $ +//=============================================================================== +#include "util/nowarnings.h" +#include "dlls/extdll.h" +#include "dlls/util.h" +#include "dlls/cbase.h" +#include "dlls/player.h" +#include "dlls/weapons.h" +#include "mod/AvHGamerules.h" +#include "mod/AvHMarineEquipment.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHAlienEquipment.h" +#include "mod/AvHAlienEquipmentConstants.h" +#include "mod/AvHAlienWeapons.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHServerUtil.h" +#include "util/MathUtil.h" + +typedef vector AvHPlayerListType; +typedef vector AvHObservatoryListType; +typedef vector AvHScanListType; +typedef vector AvHSensoryChamberListType; +typedef vector AvHUmbraCloudListType; + +BaseEntityListType gBaseEntityList; +AvHPlayerListType gPlayerList; +AvHPlayerListType gRelevantPlayerList; +AvHObservatoryListType gObservatoryList; +AvHScanListType gScanList; +AvHSensoryChamberListType gSensoryChamberList; +AvHUmbraCloudListType gUmbraCloudList; + +const float kMovementVisibilityThreshold = 10.0f; + +bool AvHSUGetInViewOfEnemy(CBaseEntity* inEntity, int& outSightedStatus) +{ + bool theInViewOfEnemy = false; + + if(inEntity->pev->iuser4 & MASK_TOPDOWN) + { + // Top down players are never visible + } + else + { + if(GetGameRules()->GetDrawInvisibleEntities()) + { + outSightedStatus |= MASK_VIS_SIGHTED; + theInViewOfEnemy = true; + } + else if(GetGameRules()->GetIsCheatEnabled(kcDetectAll)) + { + outSightedStatus |= MASK_VIS_DETECTED; + theInViewOfEnemy = true; + } + else + { + AvHPlayer* theInPlayer = dynamic_cast(inEntity); + AvHCloakable* theCloakable = dynamic_cast(inEntity); + if(!theInPlayer || !theInPlayer->GetIsCloaked()) + { + if(!theCloakable || (theCloakable->GetOpacity() > 0.0f)) + { + // Loop through enemy players, check if we are in view of any of them + for(AvHPlayerListType::iterator thePlayerIter = gPlayerList.begin(); thePlayerIter != gPlayerList.end(); thePlayerIter++) + { + if((*thePlayerIter)->GetTeam() != inEntity->pev->team) + { + // Commanders can't "see" enemies + if(((*thePlayerIter)->GetUser3() != AVH_USER3_COMMANDER_PLAYER) && ((*thePlayerIter)->GetIsRelevant())) + { + if((*thePlayerIter)->GetIsEntityInSight(inEntity)) + { + outSightedStatus |= MASK_VIS_SIGHTED; + theInViewOfEnemy = true; + break; + } + } + } + } + } + + // Loop through observatories, uncloaking and detecting all enemy players in range + for(AvHObservatoryListType::iterator theObservatoryIter = gObservatoryList.begin(); theObservatoryIter != gObservatoryList.end(); theObservatoryIter++) + { + if((*theObservatoryIter)->pev->team != inEntity->pev->team && ( inEntity->pev->team != TEAM_IND ) && !(*theObservatoryIter)->GetIsRecycling() ) + { + // Check that entity is in range of scan (only check XY distance, for commander's purposes) + float theDistance = VectorDistance2D((*theObservatoryIter)->pev->origin, inEntity->pev->origin); + if(theDistance < BALANCE_VAR(kObservatoryXYDetectionRadius)) + { + outSightedStatus |= MASK_VIS_DETECTED; + theInViewOfEnemy = true; + + if(theCloakable) + { + theCloakable->Uncloak(); + } + break; + } + } + } + + // Loop through all active scans on our team + for(AvHScanListType::iterator theScanIter = gScanList.begin(); theScanIter != gScanList.end(); theScanIter++) + { + if((*theScanIter)->pev->team != inEntity->pev->team) + { + // Check that entity is in range of scan + float theDistance = VectorDistance((*theScanIter)->pev->origin, inEntity->pev->origin); + if(theDistance < BALANCE_VAR(kScanRadius)) + { + outSightedStatus |= MASK_VIS_SIGHTED; + theInViewOfEnemy = true; + break; + } + } + } + } + } + + // If not in sight, check for motion-tracking + if(!theInViewOfEnemy) + { + bool theEnemyTeamHasMotionTracking = false; + AvHTeamNumber teamA = GetGameRules()->GetTeamA()->GetTeamNumber(); + AvHTeamNumber teamB = GetGameRules()->GetTeamB()->GetTeamNumber(); + if((inEntity->pev->team == teamA) || (inEntity->pev->team == teamB)) + { + AvHTeamNumber theEnemyTeamNumber = (inEntity->pev->team == teamA) ? teamB : teamA; + AvHTeam* theEnemyTeam = GetGameRules()->GetTeam(theEnemyTeamNumber); + if(theEnemyTeam) + { + if(theEnemyTeam->GetResearchManager().GetTechNodes().GetIsTechResearched(TECH_RESEARCH_MOTIONTRACK) || GetGameRules()->GetIsCombatMode()) + { + // Motion-tracking doesn't pick up cloaked entities (players) + bool theIsCloaked = false; + AvHCloakable* theCloakable = dynamic_cast(inEntity); + if(theCloakable && (theCloakable->GetOpacity() < 0.1f)) + { + theIsCloaked = true; + } + + float theVelocity = inEntity->pev->velocity.Length(); + + //ELVEN - WE HAVE TO CHECK FOR EXISTANT OBSERVATORIES BEFORE WE CAN FLAG THIS. + //: Fixed combat mode problems & slight perfoamance issue (no need to loop thru every obs). + bool obsExists = false; + + if(!GetGameRules()->GetIsCombatMode()) + { + FOR_ALL_ENTITIES(kwsObservatory, AvHObservatory*) + if(theEntity->GetIsBuilt()) + { + obsExists = true; + break; + } + END_FOR_ALL_ENTITIES(kwsObservatory) + } + else + { + obsExists = true; + } + + if((theVelocity > kMovementVisibilityThreshold) && !theIsCloaked && obsExists) + { + outSightedStatus |= MASK_VIS_DETECTED; + theInViewOfEnemy = true; + } + } + } + } + } + } + + return theInViewOfEnemy; +} + +bool AvHSUGetInRangeOfFriendlyPrimalScream(CBaseEntity* inEntity) +{ + bool inRangeOfPrimalScream = false; + + int theTeamNumber = inEntity->pev->team; + if(theTeamNumber) + { + // If team is of type alien + const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(theTeamNumber)); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && inEntity->IsAlive()) + { + // Loop through all players on our team + for(AvHPlayerListType::iterator theIter = gPlayerList.begin(); theIter != gPlayerList.end(); theIter++) + { + AvHPlayer* thePlayer = *theIter; + + // See if any of them are screaming + if(thePlayer && (thePlayer->pev->team == theTeamNumber) && (thePlayer->GetIsScreaming())) + { + // Are they in range of us? + float theDistance = VectorDistance(inEntity->pev->origin, thePlayer->pev->origin); + if(theDistance < BALANCE_VAR(kPrimalScreamRange)) + { + inRangeOfPrimalScream = true; + break; + } + } + } + } + } + + return inRangeOfPrimalScream; +} + +bool AvHSUGetInRangeOfFriendlySensoryChamber(CBaseEntity* inEntity) +{ + bool inRangeOfSensoryChamber = false; + + int theTeamNumber = inEntity->pev->team; + if(theTeamNumber) + { + // If team is of type alien + const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(theTeamNumber)); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && inEntity->IsAlive()) + { + // Loop through all SensoryChamber clouds on our team + for(AvHSensoryChamberListType::const_iterator theIter = gSensoryChamberList.begin(); theIter != gSensoryChamberList.end(); theIter++) + { + AvHSensoryChamber* theChamber = *theIter; + if(theChamber && (theChamber->pev->team == theTeamNumber)) + { + // Are we in range? + float theDistance = VectorDistance(inEntity->pev->origin, theChamber->pev->origin); + if(theDistance < BALANCE_VAR(kSensoryChamberRange)) + { + AvHBaseBuildable* theBuildable = dynamic_cast(theChamber); + if(theBuildable && theBuildable->GetIsBuilt()) + { + inRangeOfSensoryChamber = true; + break; + } + } + } + } + } + } + + return inRangeOfSensoryChamber; +} + +bool AvHSUGetInRangeOfEnemySensoryChamber(CBaseEntity* inEntity) +{ + bool inRangeOfSensoryChamber = false; + int theTeamNumber = inEntity->pev->team; + if(theTeamNumber) + { + // If team is of type marine + const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(theTeamNumber)); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) && inEntity->IsAlive()) + { + // Loop through all SensoryChamber clouds on our team + for(AvHSensoryChamberListType::const_iterator theIter = gSensoryChamberList.begin(); theIter != gSensoryChamberList.end(); theIter++) + { + AvHSensoryChamber* theChamber = *theIter; + if(theChamber && (theChamber->pev->team != theTeamNumber)) + { + // Are we in range? + float theDistance = VectorDistance(inEntity->pev->origin, theChamber->pev->origin); + if(theDistance < BALANCE_VAR(kSensoryChamberRange)) + { + AvHBaseBuildable* theBuildable = dynamic_cast(theChamber); + if(theBuildable && theBuildable->GetIsBuilt()) + { + inRangeOfSensoryChamber = true; + break; + } + } + } + } + } + } + return inRangeOfSensoryChamber; +} + +bool AvHSUGetInRangeOfFriendlyUmbra(CBaseEntity* inEntity) +{ + bool inRangeOfUmbra = false; + + int theTeamNumber = inEntity->pev->team; + if(theTeamNumber) + { + // If team is of type alien + const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(theTeamNumber)); + if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_ALIEN) && inEntity->IsAlive()) + { + // Loop through all umbra clouds on our team + for(AvHUmbraCloudListType::iterator theIter = gUmbraCloudList.begin(); theIter != gUmbraCloudList.end(); theIter++) + { + AvHUmbraCloud* theUmbraCloud = *theIter; + if(theUmbraCloud && (theUmbraCloud->pev->team == theTeamNumber)) + { + // Are we in range? + float theDistance = VectorDistance(inEntity->pev->origin, theUmbraCloud->pev->origin); + if(theDistance < BALANCE_VAR(kUmbraCloudRadius)) + { + inRangeOfUmbra = true; + break; + } + } + } + } + } + + return inRangeOfUmbra; +} + +void UpdateWorldEntity(CBaseEntity* inBaseEntity) +{ + // Visibility + inBaseEntity->pev->iuser4 &= ~MASK_VIS_SIGHTED; + inBaseEntity->pev->iuser4 &= ~MASK_VIS_DETECTED; + + if(AvHSUGetIsSubjectToVisibilityRules(inBaseEntity)) + { + int theSightedStatus = 0; + if(AvHSUGetInViewOfEnemy(inBaseEntity, theSightedStatus)) + { + inBaseEntity->pev->iuser4 |= theSightedStatus; +// if(inBaseEntity->pev->classname == MAKE_STRING(kesParticlesCustom)) +// { +// int a = 0; +// } + } + } + + // Don't clear buff flag on marines, as it means catalysts for them and they expire in AvHPlayer::InternalMarineThink + AvHUser3 theUser3 = AvHUser3(inBaseEntity->pev->iuser3); + if(theUser3 != AVH_USER3_MARINE_PLAYER) + { + inBaseEntity->pev->iuser4 &= ~MASK_BUFFED; + } + + // Primal scream bonuses + if(AvHSUGetInRangeOfFriendlyPrimalScream(inBaseEntity)) + { + inBaseEntity->pev->iuser4 |= MASK_BUFFED; + } + + + // Deteted by sensory chambers + if(theUser3 == AVH_USER3_MARINE_PLAYER ) + { + if(AvHSUGetInRangeOfEnemySensoryChamber(inBaseEntity)) + { + SetUpgradeMask(&inBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY, true); + } + else + { + SetUpgradeMask(&inBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY, false); + } + } + + // Cloaking near sensory chambers + AvHCloakable* theCloakable = dynamic_cast(inBaseEntity); + if(theCloakable ) + { + if ( theUser3 != AVH_USER3_MARINE_PLAYER ) + { + if(AvHSUGetInRangeOfFriendlySensoryChamber(inBaseEntity)) + { + theCloakable->SetSpeeds(0.0f, 0.0f, 0.0f); + theCloakable->Cloak(); + + SetUpgradeMask(&inBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY, true); + } + else + { + // Don't uncloak if we are cloaking via the upgrade + int theCloakingLevel = AvHGetAlienUpgradeLevel(inBaseEntity->pev->iuser4, MASK_UPGRADE_7); + if(theCloakingLevel == 0) + { + theCloakable->Uncloak(); + } + + SetUpgradeMask(&inBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY, false); + } + } + else + { + theCloakable->Uncloak(); + } + } + + // Umbra defense + inBaseEntity->pev->iuser4 &= ~MASK_UMBRA; + if(AvHSUGetInRangeOfFriendlyUmbra(inBaseEntity)) + { + inBaseEntity->pev->iuser4 |= MASK_UMBRA; + } + + // Update tech slots periodically so UI shows what's available + AvHBaseBuildable* theBaseBuildable = dynamic_cast(inBaseEntity); + if(theBaseBuildable) + { + theBaseBuildable->WorldUpdate(); + } +} + +void AvHGamerules::UpdateWorldEntities() +{ + // Prepare for many calls to AvHSUGetInViewOfEnemy + ASSERT(gPlayerList.size() == 0); + ASSERT(gRelevantPlayerList.size() == 0); + ASSERT(gObservatoryList.size() == 0); + ASSERT(gScanList.size() == 0); + ASSERT(gSensoryChamberList.size() == 0); + ASSERT(gUmbraCloudList.size() == 0); + ASSERT(gBaseEntityList.size() == 0); + + PROFILE_START() + + FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*) + gPlayerList.push_back(theEntity); + if(theEntity->GetIsRelevant(true)) + { + gRelevantPlayerList.push_back(theEntity); + } + END_FOR_ALL_ENTITIES(kAvHPlayerClassName) + + FOR_ALL_ENTITIES(kwsObservatory, AvHObservatory*) + if(theEntity->GetIsBuilt()) + { + gObservatoryList.push_back(theEntity); + } + END_FOR_ALL_ENTITIES(kwsObservatory) + + FOR_ALL_ENTITIES(kwsScan, AvHScan*) + gScanList.push_back(theEntity); + END_FOR_ALL_ENTITIES(kwsScan) + + FOR_ALL_ENTITIES(kwsSensoryChamber, AvHSensoryChamber*) + gSensoryChamberList.push_back(theEntity); + END_FOR_ALL_ENTITIES(kwsSensoryChamber) + + FOR_ALL_ENTITIES(kwsUmbraCloud, AvHUmbraCloud*) + gUmbraCloudList.push_back(theEntity); + END_FOR_ALL_ENTITIES(kwsUmbraCloud) + + PROFILE_END(kUpdateWorldEntitiesBuildLists) + + //AvHSUPrintDevMessage("FOR_ALL_BASEENTITIES: AvHGamerules::UpdateWorldEntities\n"); + + if(GET_RUN_CODE(2048)) + { + PROFILE_START() + FOR_ALL_BASEENTITIES() + UpdateWorldEntity(theBaseEntity); + gBaseEntityList.push_back(theBaseEntity); + END_FOR_ALL_BASEENTITIES() + PROFILE_END(kUpdateWorldEntitiesUpdateWorldEntities) + } + + // Rebuild this->mTeamAEntityHierarchy and this->mTeamBEntityHierarchy if changed + PROFILE_START() + this->mTeamAEntityHierarchy.BuildFromTeam(&this->mTeamA, gBaseEntityList); + this->mTeamBEntityHierarchy.BuildFromTeam(&this->mTeamB, gBaseEntityList); + this->mSpecEntityHierarchy.BuildForSpec(gBaseEntityList); + PROFILE_END(kUpdateWorldEntitiesBuildEntityHierarchies) + + // Update blips + if(GET_RUN_CODE(1024)) + { + PROFILE_START() + for(AvHPlayerListType::iterator theIter = gPlayerList.begin(); theIter != gPlayerList.end(); theIter++) + { + // Reset their blips + (*theIter)->ClearBlips(); + } + + // For all entities in the world + for(BaseEntityListType::iterator theBaseIter = gBaseEntityList.begin(); theBaseIter != gBaseEntityList.end(); theBaseIter++) + { + CBaseEntity* theBaseEntity = *theBaseIter; + + // If entity has a team (allow hives so aliens can find hive locations) + bool theIsHive = (theBaseEntity->pev->iuser3 == AVH_USER3_HIVE); + if((theBaseEntity->pev->team != 0) || theIsHive) + { + // Only process players, parasited entities and hives, as they are the only things that ever show up as blips + int theEntIndex = theBaseEntity->entindex(); + + bool theIsParasited = GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_PARASITED); + bool theIsNearSensory = GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_SENSORY_NEARBY); + bool theIsBuildable = dynamic_cast(theBaseEntity); + + if(((theEntIndex > 0) && (theEntIndex <= kMaxPlayers)) || theIsNearSensory || theIsHive || theIsParasited || theIsBuildable) + { + // For all relevant players in list + for(AvHPlayerListType::iterator thePlayerIterator = gRelevantPlayerList.begin(); thePlayerIterator != gRelevantPlayerList.end(); thePlayerIterator++) + { + // Call ProcessEntityBlip + (*thePlayerIterator)->ProcessEntityBlip(theBaseEntity); + } + } + } + } + + PROFILE_END(kUpdateWorldEntitiesUpdateBlips) + } + + // End after many calls to AvHSUGetInViewOfEnemy + gPlayerList.clear(); + gRelevantPlayerList.clear(); + gObservatoryList.clear(); + gScanList.clear(); + gSensoryChamberList.clear(); + gUmbraCloudList.clear(); + gBaseEntityList.clear(); +} + + diff --git a/main/source/mod/NetworkMeter.cpp b/main/source/mod/NetworkMeter.cpp index 0ee3b83..1c7bfd6 100644 --- a/main/source/mod/NetworkMeter.cpp +++ b/main/source/mod/NetworkMeter.cpp @@ -39,6 +39,7 @@ // Revision 1.2 2002/05/01 02:34:41 Charlie //=============================================================================== #include "mod/NetworkMeter.h" +#include "mod/AvHServerVariables.h" //////////////////// // Hook functions // @@ -46,7 +47,12 @@ void NetworkMeterMessageBegin(int msg_dest, int msg_type, const float* pOrigin, edict_t* ed) { - if(CVAR_GET_FLOAT("mp_networkdebug") > 0) + bool networkDebug=false; +#ifdef USE_NETWORK_METERING + if(ns_cvar_float(&avh_networkdebug) > 0) + networkDebug=true; +#endif + if(networkDebug) { char theDebugString[512]; sprintf(theDebugString, "MessageBegin(%d, %d...)\n", msg_dest, msg_type); @@ -323,7 +329,13 @@ void NetworkMessage::AddData(const NetworkData& inData) void NetworkMessage::Execute(int theNumMessagesQueued) { - if(CVAR_GET_FLOAT("mp_networkdebug") > 0) + bool networkDebug=false; +#ifdef USE_NETWORK_METERING + if(ns_cvar_float(&avh_networkdebug) > 0) + networkDebug=true; +#endif + + if(networkDebug) { string theMessageName = NetworkMeter::Instance()->LookupMessageID(this->mMessageType); string theMessageDest = "-"; diff --git a/main/source/pm_shared/pm_shared.cpp b/main/source/pm_shared/pm_shared.cpp index 4b3b8a0..2f324c2 100644 --- a/main/source/pm_shared/pm_shared.cpp +++ b/main/source/pm_shared/pm_shared.cpp @@ -1,6976 +1,7203 @@ -// Copyright (c) 1999, Valve LLC. All rights reserved. -// -// This product contains software technology licensed from Id -// Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -// All Rights Reserved. -// -// Use, distribution, and modification of this source code and/or resulting -// object code is restricted to non-commercial enhancements to products from -// Valve LLC. All other use, distribution, or modification is prohibited -// without written permission from Valve LLC. -// -// -// $Workfile: pm_shared.cpp $ -// $Date: 2002/10/24 21:20:28 $ -// -//------------------------------------------------------------------------------- -// $Log: pm_shared.cpp,v $ -// Revision 1.39 2002/10/24 21:20:28 Flayra -// - Reworked jetpack effect -// - Extended spectating distance -// - Only play blink effect once -// - Update alien energy in shared code -// -// Revision 1.38 2002/10/18 22:16:51 Flayra -// - Level5 crouching fix -// - Skulks can't use ladders -// -// Revision 1.37 2002/10/16 20:50:28 Flayra -// - Updated onos footsteps with new sound -// -// Revision 1.36 2002/10/16 00:42:55 Flayra -// - Removed blink fail event -// -// Revision 1.35 2002/10/03 18:30:31 Flayra -// - Moved alien energy to fuser3 -// - Flying/ladder fix (lerks can't use them) -// - When players are frozen, don't allow them to press buttons either -// -// Revision 1.34 2002/09/25 20:41:45 Flayra -// - Removed marine strafing slowdown -// -// Revision 1.33 2002/09/23 22:05:58 Flayra -// - Added code for rotation skulk model (removed it though) -// - Removed inverse lighting for skulks -// - Started to add speed abuse code, but realized the physics are inprecise and going over maxspeed is normal -// -// Revision 1.32 2002/09/09 19:45:30 Flayra -// - Blink fixes, removed level 5 custom step size -// -// Revision 1.31 2002/08/16 02:28:03 Flayra -// - Removed overwatch code -// - Webs now prevent jumping and jetpacking -// -// Revision 1.30 2002/08/09 01:12:16 Flayra -// - Turned on wall-climbing by default -// - Tweaked jetpack physics -// - Only update predicted player origin when runfuncs is true (fixes bug with flickering ghost unit in CM) -// -// Revision 1.29 2002/08/02 21:45:46 Flayra -// - Fixed jetpack energy problem, moved wall-running back to duck -// -// Revision 1.28 2002/07/25 17:28:37 Flayra -// - More linux changes -// -// Revision 1.27 2002/07/23 16:51:49 Flayra -// - MrBlonde's fix for player shaking (not sure how well it works) -// -// Revision 1.26 2002/07/10 14:37:18 Flayra -// - Removed large differing step-sizes (bug #255) -// -// Revision 1.25 2002/07/08 16:23:28 Flayra -// - Added debug code to fix solidity problems, fixed commander/hera-landing-pad bug, added document header -// -//=============================================================================== -#include -#include "common/mathlib.h" -#include "common/const.h" -#include "common/usercmd.h" -#include "pm_defs.h" -#include "pm_shared.h" -#include "pm_movevars.h" -#include "pm_debug.h" -#include // NULL -#include // sqrt -#include // strcpy -#include // atoi -#include // isspace -#include "mod/AvHSpecials.h" -#include "mod/AvHMarineEquipmentConstants.h" -#include "mod/AvHMessage.h" -#include "util/MathUtil.h" -#include "util/Quat.h" -#include "util/Mat3.h" -#include "common/event_flags.h" -#include "mod/AvHSoundConstants.h" -#include "mod/AvHMovementUtil.h" -#include "mod/AvHHulls.h" -#include "mod/AvHAlienAbilityConstants.h" -#include "mod/AvHAlienWeaponConstants.h" -#include "mod/AvHMovementUtil.h" -#include "mod/AvHMapExtents.h" -#include "mod/AvHSharedMovementInfo.h" -#include "util/Balance.h" - - - -#include "common/const.h" -#include "common/com_model.h" -#include "common/mathlib.h" - -#include "mod/CollisionUtil.h" -#include "engine/studio.h" - - -//#ifdef AVH_SERVER -#include "engine/edict.h" -#include "engine/eiface.h" -#include "dlls/enginecallback.h" -//#endif - -#ifdef AVH_SERVER -void AvHSUAddDebugPoint(float inX, float inY, float inZ); -#endif - -void AvHSHUGetFirstNonSolidPoint(float* inStartPos, float* inEndPos, float* outNonSolidPoint); -bool AvHSHUGetCenterPositionForGroup(int inGroupNumber, float* inPlayerOrigin, float* outCenterPosition); - -#ifdef AVH_CLIENT -////#include "cl_dll/cl_dll.h" -////#include "cl_dll/hud.h" -//#include "cl_dll/cl_util.h" -//#include "cl_dll/cl_dll.h" -#include "cl_dll/eventscripts.h" -#endif - -float PM_GetDesiredTopDownCameraHeight(qboolean& outFoundEntity); -qboolean PM_CanWalljump(); -qboolean PM_CanFlap(); -void PM_Overwatch(); -bool PM_TopDown(); -void PM_Jump(void); -void PM_PreventMegaBunnyJumping(bool inIgnoreZ); -bool GetIsEntityAPlayer(int inPhysIndex); - -#ifdef AVH_CLIENT -#include "pm_shared/pm_debug.h" -#include "..\common\hltv.h" -void PM_DebugLocations(); -typedef vector< DebugPoint > PositionListType; -DebugPointListType gTriDebugLocations; -DebugPointListType gSquareDebugLocations; -DebugEntityListType gCubeDebugEntities; -#endif - -//extern int gWallJumpEventID; -//extern int gFlightEventID; - -extern int gJetpackEventID; -extern int gBlinkEffectSuccessEventID; - -#define Vector vec3_t -#include "mod/AvHSelectionHelper.h" - -int gHeightLevel = 0; - -const float kMarineBackpedalSpeedScalar = .4f; -const float kMarineSidestepSpeedScalar = 1.0f; - -//physent_t* AvHSUGetEntity(int inPhysIndex); - -const AvHMapExtents& GetMapExtents(); - -#ifdef AVH_CLIENT -// Spectator Mode -int iJumpSpectator; -float vJumpOrigin[3]; -float vJumpAngles[3]; -//float gTopDownHeight = 0; - -float gTopDownViewOrigin[3]; -float gTopDownViewAngles[3]; -//int iHasNewViewOrigin = 0; -//int iHasNewViewAngles = 0; -#endif - -static int pm_shared_initialized = 0; - -//void InterpolateAngles( float *start, float *end, float *output, float frac ); - -#pragma warning( disable : 4305 ) - - -playermove_t *pmove = NULL; - -extern "C" -{ - Vector gPredictedPlayerOrigin; - Vector gPredictedPlayerVOfs; -} - -#ifdef AVH_CLIENT -float gOverwatchTargetRange; -#endif - - -/////////////////////////////// -// Begin Max's Code -/////////////////////////////// - -const float kSkulkRotationRate = (float)(2.5 * M_PI); // Radians per second. -const float kSkulkRotationLookAhead = 75; - -// Distance around the skulk to look each in direction for wallsticking. -const vec3_t kWallstickingDistanceCheck = { 5, 5, 5 }; -// tankefugl: 0000972 -vec3_t gSurfaceNormal = { 0, 0, 0 }; -bool canWallJump = false; -// :tankefugl - -#ifdef AVH_CLIENT -extern vec3_t gPlayerAngles; -extern vec3_t gTargetPlayerAngles; -extern vec3_t gWorldViewAngles; -#endif - -/////////////////////////////// -// End Max's Code -/////////////////////////////// - -// Ducking time -#define TIME_TO_DUCK (0.4 * 1000) -//#define VEC_DUCK_HULL_MIN -18 -//#define VEC_DUCK_HULL_MAX 18 -//#define VEC_DUCK_VIEW 12 -#define PM_DEAD_VIEWHEIGHT -8 -#define MAX_CLIMB_SPEED 120 -#define STUCK_MOVEUP 1 -#define STUCK_MOVEDOWN -1 -//#define VEC_HULL_MIN -36 -//#define VEC_HULL_MAX 36 -//#define VEC_VIEW 28 -#define STOP_EPSILON 0.1 - -#define CTEXTURESMAX 512 // max number of textures loaded -#define CBTEXTURENAMEMAX 13 // only load first n chars of name - -#define CHAR_TEX_CONCRETE 'C' // texture types -#define CHAR_TEX_METAL 'M' -#define CHAR_TEX_DIRT 'D' -#define CHAR_TEX_VENT 'V' -#define CHAR_TEX_GRATE 'G' -#define CHAR_TEX_TILE 'T' -#define CHAR_TEX_SLOSH 'S' -#define CHAR_TEX_WOOD 'W' -#define CHAR_TEX_COMPUTER 'P' -#define CHAR_TEX_GLASS 'Y' -#define CHAR_TEX_FLESH 'F' - -#define STEP_CONCRETE 0 // default step sound -#define STEP_METAL 1 // metal floor -#define STEP_DIRT 2 // dirt, sand, rock -#define STEP_VENT 3 // ventillation duct -#define STEP_GRATE 4 // metal grating -#define STEP_TILE 5 // floor tiles -#define STEP_SLOSH 6 // shallow liquid puddle -#define STEP_WADE 7 // wading in liquid -#define STEP_LADDER 8 // climbing ladder - -#define PLAYER_FATAL_FALL_SPEED 1024// approx 60 feet -#define PLAYER_MAX_SAFE_FALL_SPEED 580// approx 20 feet -#define DAMAGE_FOR_FALL_SPEED (float) 100 / ( PLAYER_FATAL_FALL_SPEED - PLAYER_MAX_SAFE_FALL_SPEED )// damage per unit per second. -#define PLAYER_MIN_BOUNCE_SPEED 200 -#define PLAYER_FALL_PENALTY_THRESHHOLD (float)200 // If player lands with this much speed or more, slow them down as penalty for jumping -#define PLAYER_FALL_PUNCH_THRESHHOLD (float)350 // won't punch player's screen/make scrape noise unless player falling at least this fast. - -#define PLAYER_LONGJUMP_SPEED 350 // how fast we longjump - -const float kAlienEnergyFlap = .025f; - -// double to float warning -#pragma warning(disable : 4244) -#define max(a, b) (((a) > (b)) ? (a) : (b)) -#define min(a, b) (((a) < (b)) ? (a) : (b)) -// up / down -#define PITCH 0 -// left / right -#define YAW 1 -// fall over -#define ROLL 2 - -#define MAX_CLIENTS 32 - -#define CONTENTS_CURRENT_0 -9 -#define CONTENTS_CURRENT_90 -10 -#define CONTENTS_CURRENT_180 -11 -#define CONTENTS_CURRENT_270 -12 -#define CONTENTS_CURRENT_UP -13 -#define CONTENTS_CURRENT_DOWN -14 - -#define CONTENTS_TRANSLUCENT -15 - -static vec3_t rgv3tStuckTable[54]; -static int rgStuckLast[MAX_CLIENTS][2]; - -// Texture names -static int gcTextures = 0; -static char grgszTextureName[CTEXTURESMAX][CBTEXTURENAMEMAX]; -static char grgchTextureType[CTEXTURESMAX]; - -int g_onladder[MAX_CLIENTS]; -bool gIsJetpacking[MAX_CLIENTS]; - - -// Borrowed from Quake1. - -static hull_t box_hull; -static dclipnode_t box_clipnodes[6]; -static mplane_t box_planes[6]; - - -bool PM_GetIsBlinking() -{ - if (pmove->cmd.weaponselect == 255 || pmove->flags & FL_FAKECLIENT) - { - return (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4) && (pmove->cmd.impulse == ALIEN_ABILITY_BLINK); - } - return false; -} - -bool PM_GetIsLeaping() -{ - if (pmove->cmd.weaponselect == 255 || pmove->flags & FL_FAKECLIENT) - { - return (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1) && (pmove->cmd.impulse == ALIEN_ABILITY_LEAP); - } - return false; -} - -bool PM_GetIsCharging() -{ - if (pmove->cmd.weaponselect == 255 || pmove->flags & FL_FAKECLIENT) - { - return (pmove->cmd.impulse == ALIEN_ABILITY_CHARGE); - } - return false; -} - - -/* -=================== -PM_InitBoxHull - -Set up the planes and clipnodes so that the six floats of a bounding box -can just be stored out and get a proper hull_t structure. -=================== -*/ -void PM_InitBoxHull (void) -{ - int i; - int side; - - box_hull.clipnodes = box_clipnodes; - box_hull.planes = box_planes; - box_hull.firstclipnode = 0; - box_hull.lastclipnode = 5; - - for (i=0 ; i<6 ; i++) - { - box_clipnodes[i].planenum = i; - - side = i&1; - - box_clipnodes[i].children[side] = CONTENTS_EMPTY; - if (i != 5) - box_clipnodes[i].children[side^1] = i + 1; - else - box_clipnodes[i].children[side^1] = CONTENTS_SOLID; - - } - -} - - -/* -=================== -PM_HullForBox - -To keep everything totally uniform, bounding boxes are turned into small -BSP trees instead of being compared directly. -=================== -*/ -hull_t* PM_HullForBox (vec3_t mins, vec3_t maxs) -{ - - for (int i=0 ; i<6 ; i++) - { - - box_planes[i].type = i>>1; - - box_planes[i].normal[0] = 0; - box_planes[i].normal[1] = 0; - box_planes[i].normal[2] = 0; - box_planes[i].normal[i>>1] = 1; - - } - - box_planes[0].dist = maxs[0]; - box_planes[1].dist = mins[0]; - box_planes[2].dist = maxs[1]; - box_planes[3].dist = mins[1]; - box_planes[4].dist = maxs[2]; - box_planes[5].dist = mins[2]; - - return &box_hull; -} - - -void NS_DrawBoundingBox(const vec3_t mins, - const vec3_t maxs, - const vec3_t inEntityOrigin, - const vec3_t inEntityAngles) -{ - -#ifdef AVH_SERVER - - void PM_DrawRectangle(vec3_t tl, vec3_t bl, vec3_t tr, vec3_t br, int pcolor, float life); - extern int PM_boxpnt[6][4]; - - int pcolor = 132; - float plife = 0.1; - - vec3_t tmp; - vec3_t p[8]; - float gap = 0; - int j; - - for (j = 0; j < 8; j++) - { - tmp[0] = (j & 1) ? mins[0] - gap : maxs[0] + gap; - tmp[1] = (j & 2) ? mins[1] - gap : maxs[1] + gap; - tmp[2] = (j & 4) ? mins[2] - gap : maxs[2] + gap; - - VectorCopy(tmp, p[j]); - } - - // If the bbox should be rotated, do that - vec3_t forward, right, up; - - AngleVectorsTranspose(inEntityAngles, forward, right, up); - for (j = 0; j < 8; j++) - { - VectorCopy(p[j], tmp); - p[j][0] = DotProduct ( tmp, forward ); - p[j][1] = DotProduct ( tmp, right ); - p[j][2] = DotProduct ( tmp, up ); - } - - // Offset by entity origin, if any. - for (j = 0; j < 8; j++) - VectorAdd(p[j], inEntityOrigin, p[j]); - - for (j = 0; j < 6; j++) - { - PM_DrawRectangle( - p[PM_boxpnt[j][1]], - p[PM_boxpnt[j][0]], - p[PM_boxpnt[j][2]], - p[PM_boxpnt[j][3]], - pcolor, plife); - } - -#endif - -} - - -void NS_DrawBoundingBox(const OBBox& inBox) -{ - - vec3_t theAngles; - VectorsToAngles(inBox.mAxis[1], inBox.mAxis[0], inBox.mAxis[2], theAngles); - - vec3_t theMin; - VectorScale(inBox.mExtents, -1, theMin); - - vec3_t theMax; - VectorCopy(inBox.mExtents, theMax); - - NS_DrawBoundingBox(theMin, theMax, inBox.mOrigin, theAngles); - -} - - - -/** - * Computes the hull for an oriented bounding box. - */ -hull_t* NS_HullForBox(vec3_t inEntityMins, vec3_t inEntityMaxs, - vec3_t inEntityAngles, vec3_t inPlayerExtents) -{ - - float thePlayerRadius = max(inPlayerExtents[0], inPlayerExtents[1]); - - // Players "feel" too large so artificially shrink them relative to the - // structures. - thePlayerRadius *= 0.8; - - vec3_t axis[3]; - AngleVectors(inEntityAngles, axis[0], axis[1], axis[2]); - - // For some reason the y-axis is negated so flip it. - - axis[1][0] = -axis[1][0]; - axis[1][1] = -axis[1][1]; - axis[1][2] = -axis[1][2]; - - for (int i = 0; i < 3; ++i) - { - - int p1 = i * 2; - int p2 = p1 + 1; - - vec3_t p; - - // Compute the offset the planes to account for the size of the player. - - /* - float offset = fabs(inPlayerExtents[0] * axis[i][0]) + - fabs(inPlayerExtents[1] * axis[i][1]) + - fabs(inPlayerExtents[2] * axis[i][2]); - */ - - // Use a cylindrical player instead of a square one. - float offset = thePlayerRadius; - - // Positive plane. - - VectorCopy(axis[i], box_planes[p1].normal); - VectorScale(box_planes[p1].normal, inEntityMaxs[i], p); - - box_planes[p1].dist = DotProduct(p, box_planes[p1].normal) + offset; - box_planes[p1].type = 3; // Not axis aligned. - - // Negative plane. - - VectorCopy(axis[i], box_planes[p2].normal); - VectorScale(box_planes[p2].normal, inEntityMins[i], p); - - box_planes[p2].dist = DotProduct(p, box_planes[p2].normal) - offset; - box_planes[p2].type = 3; // Not axis aligned. - - } - - return &box_hull; - -} - - -bool NS_GetCollisionSizeForUser3(AvHUser3 inUser3, vec3_t outMinSize, vec3_t outMaxSize) -{ - - switch (inUser3) - { - - case AVH_USER3_COMMANDER_STATION: - outMinSize[0] = -45; - outMinSize[1] = -16; - outMinSize[2] = 0; - outMaxSize[0] = 45; - outMaxSize[1] = 16; - outMaxSize[2] = 70.34; - return true; - - case AVH_USER3_INFANTRYPORTAL: - outMinSize[0] = -25; - outMinSize[1] = -25; - outMinSize[2] = 0; - outMaxSize[0] = 25; - outMaxSize[1] = 25; - outMaxSize[2] = 5; - return true; - - case AVH_USER3_PHASEGATE: - outMinSize[0] = -55; - outMinSize[1] = -40; - outMinSize[2] = 0; - outMaxSize[0] = 40; - outMaxSize[1] = 50; - outMaxSize[2] = 14.49; - return true; - - case AVH_USER3_ARMORY: - case AVH_USER3_ADVANCED_ARMORY: - outMinSize[0] = -20; - outMinSize[1] = -20; - outMinSize[2] = 0; - outMaxSize[0] = 20; - outMaxSize[1] = 20; - outMaxSize[2] = 62.1931; - return true; - - case AVH_USER3_ARMSLAB: - outMinSize[0] = -30; - outMinSize[1] = -20; - outMinSize[2] = 0; - outMaxSize[0] = 30; - outMaxSize[1] = 20; - outMaxSize[2] = 60; - return true; - - case AVH_USER3_PROTOTYPE_LAB: - outMinSize[0] = -16; - outMinSize[1] = -16; - outMinSize[2] = 0; - outMaxSize[0] = 16; - outMaxSize[1] = 16; - outMaxSize[2] = 67.7443; - return true; - - case AVH_USER3_OBSERVATORY: - outMinSize[0] = -16; - outMinSize[1] = -16; - outMinSize[2] = 0; - outMaxSize[0] = 16; - outMaxSize[1] = 16; - outMaxSize[2] = 80.7443; - return true; - - /* - case AVH_USER3_RESTOWER: - outMinSize[0] = -35; - outMinSize[1] = -35; - outMinSize[2] = 0; - outMaxSize[0] = 35; - outMaxSize[1] = 35; - outMaxSize[2] = 60; - return true; - */ - - case AVH_USER3_TURRET_FACTORY: - case AVH_USER3_ADVANCED_TURRET_FACTORY: - outMinSize[0] = -40; - outMinSize[1] = -25; - outMinSize[2] = 0; - outMaxSize[0] = 40; - outMaxSize[1] = 25; - outMaxSize[2] = 50; - return true; - - case AVH_USER3_TURRET: - outMinSize[0] = -16; - outMinSize[1] = -16; - outMinSize[2] = 0; - outMaxSize[0] = 16; - outMaxSize[1] = 16; - outMaxSize[2] = 42; - return true; - - case AVH_USER3_SIEGETURRET: - outMinSize[0] = -16; - outMinSize[1] = -16; - outMinSize[2] = 0; - outMaxSize[0] = 16; - outMaxSize[1] = 16; - outMaxSize[2] = 62.1931; - return true; - - /* - case AVH_USER3_ALIENRESTOWER: - outMinSize[0] = -30; - outMinSize[1] = -35; - outMinSize[2] = 0; - outMaxSize[0] = 30; - outMaxSize[1] = 75; - outMaxSize[2] = 100; - return true; - - case AVH_USER3_OFFENSE_CHAMBER: - case AVH_USER3_DEFENSE_CHAMBER: - case AVH_USER3_SENSORY_CHAMBER: - case AVH_USER3_MOVEMENT_CHAMBER: - outMinSize[0] = -16; - outMinSize[1] = -16; - outMinSize[2] = 0; - outMaxSize[0] = 16; - outMaxSize[1] = 16; - outMaxSize[2] = 44; - return true; - - case AVH_USER3_HIVE: - outMinSize[0] = -50; - outMinSize[1] = -80; - outMinSize[2] = -145; - outMaxSize[0] = 50; - outMaxSize[1] = 80; - outMaxSize[2] = 50; - return true; - */ - - } - - return false; - -} - -void NS_GetClosestPlaneNormal(vec3_t inDirection, mplane_t inPlanes[], int inNumPlanes, vec3_t outNormal) -{ - - VectorCopy(inDirection, outNormal); - float best = 0; - - for (int i = 0; i < inNumPlanes; ++i) - { - - float d = fabs(DotProduct(inDirection, inPlanes[i].normal)); - - if (d >= best) - { - VectorCopy(inPlanes[i].normal, outNormal); - best = d; - } - - } - -} - -void NS_TraceLine(float* start, float* end, int hull, int traceFlags, int ignore_pe, bool ignorePlayers, trace_t& result) -{ - - memset(&result, 0, sizeof(trace_t)); - - result.fraction = 1; - result.ent = NULL; - - VectorCopy(end, result.endpos); - - int hullNumber = NS_GetValveHull(hull); - - for (int i = 0; i < pmove->numphysent; ++i) //Elven - why is this ++i? could this be an off by 1 error? - { - if (i != ignore_pe && pmove->physents[i].solid != SOLID_NOT && (!ignorePlayers || !pmove->physents[i].player)) - { - - hull_t* hull = NULL; - bool ignoreRotation = false; - - if (pmove->physents[i].model != NULL) - { - hull = &pmove->physents[i].model->hulls[hullNumber]; - } - else if (!(traceFlags & PM_WORLD_ONLY)) - { - - vec3_t min; - vec3_t max; - - VectorAdd(pmove->physents[i].mins, pmove->player_mins[pmove->usehull], min); - VectorAdd(pmove->physents[i].maxs, pmove->player_maxs[pmove->usehull], max); - - vec3_t offset; - VectorAdd(pmove->physents[i].maxs, pmove->physents[i].mins, offset); - - vec3_t center; - VectorMA(pmove->physents[i].origin, 0.5, offset, center); - - vec3_t theEntityMins; - vec3_t theEntityMaxs; - - if (NS_GetCollisionSizeForUser3((AvHUser3)pmove->physents[i].iuser3, - theEntityMins, theEntityMaxs)) - { - - vec3_t thePlayerExtents; - VectorSubtract(pmove->player_maxs[hullNumber], pmove->player_mins[hullNumber], thePlayerExtents); - VectorScale(thePlayerExtents, 0.5, thePlayerExtents); - - hull = NS_HullForBox(theEntityMins, - theEntityMaxs, - pmove->physents[i].angles, - thePlayerExtents); - - // We've already taken the rotation of the entity into account. - ignoreRotation = true; - - } - else - { - - vec3_t min; - vec3_t max; - - VectorAdd(pmove->physents[i].mins, pmove->player_mins[hullNumber], min); - VectorAdd(pmove->physents[i].maxs, pmove->player_maxs[hullNumber], max); - - hull = PM_HullForBox(min, max); - - } - - } - - if (hull != NULL) - { - - vec3_t localStart; - vec3_t localEnd; - - // Translate the start and end into the model's frame of reference. - - VectorSubtract(start, pmove->physents[i].origin, localStart); - VectorSubtract(end, pmove->physents[i].origin, localEnd); - - // Rotate the start and end into the model's frame of reference. - - bool rotated; - - if (pmove->physents[i].angles[0] || - pmove->physents[i].angles[1] || - pmove->physents[i].angles[2]) - { - rotated = !ignoreRotation; - } - else - { - rotated = false; - } - - vec3_t forward; - vec3_t right; - vec3_t up; - - if (rotated) - { - - AngleVectors(pmove->physents[i].angles, forward, right, up); - - vec3_t temp; - - VectorCopy(localStart, temp); - localStart[0] = DotProduct(temp, forward); - localStart[1] = -DotProduct(temp, right); - localStart[2] = DotProduct(temp, up); - - VectorCopy(localEnd, temp); - localEnd[0] = DotProduct(temp, forward); - localEnd[1] = -DotProduct(temp, right); - localEnd[2] = DotProduct(temp, up); - - } - - trace_t trace; - NS_TraceLine(hull, localStart, localEnd, &trace); - - if (trace.fraction < result.fraction) - { - memcpy(&result, &trace, sizeof(trace_t)); - - if (rotated) - { - - vec3_t invAngles; - - VectorScale(pmove->physents[i].angles, -1, invAngles); - AngleVectors(invAngles, forward, right, up); - - vec3_t temp; - VectorCopy(result.plane.normal, temp); - - result.plane.normal[0] = DotProduct(temp, forward); - result.plane.normal[1] = -DotProduct(temp, right); - result.plane.normal[2] = DotProduct(temp, up); - - // TODO result.plane.dst needs to be updated. - - } - - } - - } - - } - } - - // Compute the end position of the trace. - - result.endpos[0] = start[0] + result.fraction * (end[0] - start[0]); - result.endpos[1] = start[1] + result.fraction * (end[1] - start[1]); - result.endpos[2] = start[2] + result.fraction * (end[2] - start[2]); - -} - -pmtrace_t NS_PlayerTrace(playermove_t* pmove, float* start, float* end, int traceFlags, int ignore_pe) -{ - - //return pmove->PM_PlayerTrace(start, end, traceFlags, ignore_pe); - - pmtrace_t result = { 0 }; - - result.fraction = 1; - result.ent = -1; - - VectorCopy(end, result.endpos); - - int hullNumber = NS_GetValveHull(pmove->usehull); - - - for (int i = 0; i < pmove->numphysent; ++i) //wtf is this again? elven. - { - if (i != ignore_pe && pmove->physents[i].solid != SOLID_NOT) - { - - hull_t* hull = NULL; - bool ignoreRotation = false; - - vec3_t awayDirection; - - if (pmove->physents[i].model != NULL) - { - hull = &pmove->physents[i].model->hulls[hullNumber]; - } - else if (!(traceFlags & PM_WORLD_ONLY)) - { - - - /* - OBBox theBox[50]; - int theNumBoxes; - NS_GetHitBoxesForEntity(pmove->physents[i].info, 50, theBox, theNumBoxes, pmove->time); - - for (int z = 0; z < theNumBoxes; ++z) - { - NS_DrawBoundingBox(theBox[z]); - } - */ - - vec3_t min; - vec3_t max; - - VectorAdd(pmove->physents[i].mins, pmove->player_mins[pmove->usehull], min); - VectorAdd(pmove->physents[i].maxs, pmove->player_maxs[pmove->usehull], max); - - vec3_t offset; - VectorAdd(pmove->physents[i].maxs, pmove->physents[i].mins, offset); - - vec3_t center; - VectorMA(pmove->physents[i].origin, 0.5, offset, center); - - // Check if the player is moving away from the object. If they - // are, don't check for collision. This makes it so that objects - // can still move apart even if they are intersecting. - - vec3_t direction; - VectorSubtract(end, start, direction); - - VectorSubtract(start, center, awayDirection); - VectorNormalize(awayDirection); - - vec3_t theEntityMins; - vec3_t theEntityMaxs; - - if (NS_GetCollisionSizeForUser3((AvHUser3)pmove->physents[i].iuser3, - theEntityMins, theEntityMaxs)) - { - - vec3_t thePlayerExtents; - VectorSubtract(pmove->player_maxs[pmove->usehull], pmove->player_mins[pmove->usehull], thePlayerExtents); - VectorScale(thePlayerExtents, 0.5, thePlayerExtents); - - hull = NS_HullForBox(theEntityMins, - theEntityMaxs, - pmove->physents[i].angles, - thePlayerExtents); - - /* - NS_DrawBoundingBox(theEntityMins, - theEntityMaxs, - pmove->physents[i].origin, - pmove->physents[i].angles); - */ - - - // We've already taken the rotation of the entity into account. - ignoreRotation = true; - - } - else - { - - vec3_t min; - vec3_t max; - - VectorAdd(pmove->physents[i].mins, pmove->player_mins[pmove->usehull], min); - VectorAdd(pmove->physents[i].maxs, pmove->player_maxs[pmove->usehull], max); - - hull = PM_HullForBox(min, max); - - } - - vec3_t localStart; - VectorSubtract(start, pmove->physents[i].origin, localStart); - - if (NS_PointContents(hull, 0, localStart) == CONTENTS_SOLID) - { - // If the player is stuck but trying to move away, let them. - if (DotProduct(direction, awayDirection) >= 0) - { - hull = NULL; - } - } - - } - - if (hull != NULL) - { - - vec3_t localStart; - vec3_t localEnd; - - // Translate the start and end into the model's frame of reference. - - VectorSubtract(start, pmove->physents[i].origin, localStart); - VectorSubtract(end, pmove->physents[i].origin, localEnd); - - // Rotate the start and end into the model's frame of reference. - - bool rotated; - - if (pmove->physents[i].model == NULL) - { - rotated = false; - } - else - { - if (pmove->physents[i].angles[0] || - pmove->physents[i].angles[1] || - pmove->physents[i].angles[2]) - { - rotated = !ignoreRotation; - } - else - { - rotated = false; - } - } - - vec3_t forward; - vec3_t right; - vec3_t up; - - if (rotated) - { - - AngleVectors(pmove->physents[i].angles, forward, right, up); - - vec3_t temp; - - VectorCopy(localStart, temp); - localStart[0] = DotProduct(temp, forward); - localStart[1] = -DotProduct(temp, right); - localStart[2] = DotProduct(temp, up); - - VectorCopy(localEnd, temp); - localEnd[0] = DotProduct(temp, forward); - localEnd[1] = -DotProduct(temp, right); - localEnd[2] = DotProduct(temp, up); - - } - - trace_t trace; - NS_TraceLine(hull, localStart, localEnd, &trace); - - if (trace.fraction < result.fraction) - { - - result.allsolid = trace.allsolid; - result.startsolid = trace.startsolid; - result.inopen = trace.inopen; - result.inwater = trace.inwater; - result.fraction = trace.fraction; - result.ent = i; - result.hitgroup = trace.hitgroup; - - if (pmove->physents[i].model == NULL && result.allsolid) - { - - // We don't have a normal, so use the plane normal - // closest to the away direction. - - NS_GetClosestPlaneNormal(awayDirection, box_planes, 6, result.plane.normal); - result.plane.dist = 0; - - //VectorCopy(awayDirection, result.plane.normal); - - result.allsolid = false; - - } - else - { - VectorCopy(trace.plane.normal, result.plane.normal); - result.plane.dist = trace.plane.dist; - } - - if (rotated) - { - - vec3_t invAngles; - - VectorScale(pmove->physents[i].angles, -1, invAngles); - AngleVectors(invAngles, forward, right, up); - - vec3_t temp; - VectorCopy(result.plane.normal, temp); - - result.plane.normal[0] = DotProduct(temp, forward); - result.plane.normal[1] = -DotProduct(temp, right); - result.plane.normal[2] = DotProduct(temp, up); - - // TODO result.plane.dst needs to be updated. - - } - - } - - } - - } - } - - // Compute the end position of the trace. - - result.endpos[0] = start[0] + result.fraction * (end[0] - start[0]); - result.endpos[1] = start[1] + result.fraction * (end[1] - start[1]); - result.endpos[2] = start[2] + result.fraction * (end[2] - start[2]); - - // Compute the delta velocity - - if (result.fraction < 1) - { - - float s = DotProduct(result.plane.normal, pmove->velocity); - - vec3_t antiVelocity; - VectorScale(result.plane.normal, s, antiVelocity); - - VectorSubtract(pmove->velocity, antiVelocity, result.deltavelocity); - - } - - return result; - -} - -int NS_TestPlayerPosition(playermove_t* pmove, float* origin, pmtrace_t* trace) -{ - - //return pmove->PM_TestPlayerPosition(origin, trace); - - pmtrace_t tempTrace = NS_PlayerTrace(pmove, origin, origin, PM_WORLD_ONLY, -1); - - if (trace != NULL) - { - memcpy(trace, &tempTrace, sizeof(pmtrace_t)); - } - - return tempTrace.ent; - -} - - - -//#ifdef AVH_CLIENT -void PM_DebugLocations(int theRandomNumber) -{ -#ifdef AVH_CLIENT - gTriDebugLocations.clear(); -#endif - - //int theNumEnts = pmove->numphysent; - int theNumEnts = pmove->numvisent; - - //if(pmove->server) - //{ - // pmove->Con_Printf("DEBUGLOCATIONS (server, %d ents):\n", theNumEnts); - //} - //else - //{ - // pmove->Con_Printf("DEBUGLOCATIONS (client, %d ents):\n", theNumEnts); - //} - - physent_t* theEntity = NULL; - for (int i = 0; i < theNumEnts; i++) - { - //theEntity = pmove->physents + i;; - theEntity = pmove->visents + i;; - if(theEntity)// && (theEntity->info >= 1) && (theEntity->info <= 16)) - { - vec3_t theEntityOrigin; - VectorCopy(theEntity->origin, theEntityOrigin); - - //if(pmove->iuser3 == AVH_USER3_FUNC_RESOURCE) - //{ - // pmove->Con_Printf(" - entnumber(%d) iuser3 (%d) %d origin(%f, %f, %f) mins(%f, %f, %f) maxs(%f %f %f)\n", theEntity->info, pmove->iuser3, theEntityOrigin[0], theEntityOrigin[1], theEntityOrigin[2], theEntity->mins[0], theEntity->mins[1], theEntity->mins[2], theEntity->maxs[0], theEntity->maxs[1], theEntity->maxs[2]); - //} - //pmove->Con_Printf("mins(%f, %f, %f) maxs(%f %f %f)\n", i, theEntityOrigin[0], theEntityOrigin[1], theEntityOrigin[2]); - -#ifdef AVH_CLIENT - DebugPoint thePoint; - thePoint.x = theEntityOrigin[0]; - thePoint.y = theEntityOrigin[1]; - thePoint.z = theEntityOrigin[2]; - - gTriDebugLocations.push_back(thePoint); -#endif - -#ifdef AVH_SERVER - AvHSUAddDebugPoint(theEntityOrigin[0], theEntityOrigin[1], theEntityOrigin[2]); -#endif - // float theOriginX = (theEntity->maxs.x + theEntity->mins.x)/2.0f; - // float theOriginY = (theEntity->maxs.y + theEntity->mins.y)/2.0f; - // float theOriginZ = (theEntity->maxs.z + theEntity->mins.z)/2.0f; - //DrawCircleOnGroundAtPoint(theEntityOrigin, 4, 0, 12, 1, 1, 0, .5f); - } - } - pmove->Con_Printf("\n"); - - // if(theRandomNumber < 10) - // { - // pmove->Con_Printf("Using hull: %d\n", pmove->usehull); - // } -} -//#endif - -void PM_GetWishVelocity(vec3_t& outWishVelocity) -{ - VectorNormalize(pmove->forward); - VectorNormalize(pmove->right); - VectorNormalize(pmove->up); - - float fmove = pmove->cmd.forwardmove; - float smove = pmove->cmd.sidemove; - float umove = pmove->cmd.upmove; - - for(int i=0 ; i<3 ; i++) - { - outWishVelocity[i] = pmove->forward[i]*fmove + pmove->right[i]*smove + pmove->up[i]*umove; - } -} - -bool NS_CheckOffsetFromOrigin(float inX, float inY, float inZ, vec3_t& outNormal) -{ - - /////////////////////////////// - // Begin Max's Code - /////////////////////////////// - - bool thePointIsSolid = false; - - const float kScalar = 1.3f; - - vec3_t thePoint; - - thePoint[0] = pmove->origin[0] + inX*kScalar; - thePoint[1] = pmove->origin[1] + inY*kScalar; - thePoint[2] = pmove->origin[2] + inZ*kScalar; - - // Trace against the world geometry. - - trace_t trace; - NS_TraceLine(pmove->origin, thePoint, 1, PM_WORLD_ONLY, -1, true, trace); - - if (trace.fraction < 1) - { - // tankefugl: Don't weight it if surface normal is too plane - if (trace.plane.normal[2] > 0.7) - return true; - - // Weight the normal based on how close the intersection is. - - vec3_t normal; - VectorScale(trace.plane.normal, 1.0f / (trace.fraction + 0.1f), normal); - VectorAdd(normal, outNormal, outNormal); - - thePointIsSolid = true; - - #ifdef AVH_SERVER - //PM_ParticleLine(pmove->origin, thePoint, 63, 0.1, 5.0); - #endif - - } - else - { - #ifdef AVH_SERVER - //PM_ParticleLine(pmove->origin, thePoint, 0, 0.1, 5.0); - #endif - } - - // If there was no intersection with the world, trace the entities. - - // We prioritize world tracing over entity tracing so that small entities (like - // IPs, resource nozzles and switches) don't cause changes in the skulk's orientation. - - if (!thePointIsSolid) - { - - const int pointSizeHull = 2; - - trace_t trace; - NS_TraceLine(pmove->origin, thePoint, pointSizeHull, 0, -1, true, trace); - - /* - struct pmtrace_s* theTrace = pmove->PM_TraceLine(pmove->origin, thePoint, - PM_TRACELINE_ANYVISIBLE, pointSizeHull, pmove->player_index); - */ - - //if (theTrace && (theTrace->fraction < 1) && !GetIsEntityAPlayer(theTrace->ent)) - if (trace.fraction < 1) - { - - // Add the normal so that we compute an average normal over all tests. - // Weight the normal based on how close the intersection is. - - vec3_t normal; - - //VectorScale(theTrace->plane.normal, 1.0f / (theTrace->fraction + 0.1f), normal); - VectorScale(trace.plane.normal, 1.0f / (trace.fraction + 0.1f), normal); - VectorAdd(normal, outNormal, outNormal); - - thePointIsSolid = true; - - #ifdef AVH_SERVER - //PM_ParticleLine(pmove->origin, thePoint, 79, 0.1, 5.0); - #endif - - } - - } - - return thePointIsSolid; - - /////////////////////////////// - // End Max's Code - /////////////////////////////// - -} - -void NS_GetWallstickingAngles(const vec3_t surfaceNormal, const vec3_t currentAngles, vec3_t angles) -{ - - vec3_t forward; - vec3_t right; - vec3_t up; - - VectorCopy(surfaceNormal, up); - AngleVectors(currentAngles, forward, NULL, NULL); - - CrossProduct(forward, up, right); - CrossProduct(up, right, forward); - - VectorNormalize(forward); - VectorNormalize(right); - VectorNormalize(up); - - VectorsToAngles(forward, right, up, angles); - -} - -void NS_FixWallstickingAngles(vec3_t angles) -{ - - const float minValue = -1000; - const float maxValue = 1000; - - if (!(angles[0] > minValue && angles[0] < maxValue) || - !(angles[1] > minValue && angles[1] < maxValue) || - !(angles[2] > minValue && angles[2] < maxValue)) - { - - // The angles have become invalid, so reset them. - - angles[0] = 0; - angles[1] = 0; - angles[2] = 0; - - } - -} - -void PM_ParticleAxes(vec3_t origin, vec3_t angles) -{ - - - vec3_t forward; - vec3_t right; - vec3_t up; - - AngleVectors(angles, forward, right, up); - - VectorScale(forward, 100, forward); - VectorScale(right, 100, right); - VectorScale(up, 100, up); - - VectorAdd(origin, forward, forward); - VectorAdd(origin, right, right); - VectorAdd(origin, up, up); - - PM_ParticleLine(origin, forward, 63, 0.1, 2.0); - PM_ParticleLine(origin, right, 79, 0.1, 2.0); - PM_ParticleLine(origin, up, 15, 0.1, 2.0); - -} - - -void NS_UpdateWallsticking() -{ - canWallJump = true; - - SetUpgradeMask(&pmove->iuser4, MASK_WALLSTICKING, false); - - //pmove->effects &= ~EF_INVLIGHT; - - if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1) - { - // tankefugl: 0000972 - pmove->waterjumptime -= pmove->cmd.msec; - if (pmove->waterjumptime < 0) - pmove->waterjumptime = 0; - - if(!(pmove->cmd.buttons & IN_DUCK) && !(pmove->oldbuttons & IN_JUMP) && (pmove->waterlevel < 2)) -// if(!(pmove->cmd.buttons & IN_DUCK)) //&& ((pmove->onground != -1) || (pmove->numtouch > 0))) - // :tankefugl - { - - vec3_t theMinPoint; - vec3_t theMaxPoint; - - // TODO: SCALE BY FPS DEPENDANCY - VectorScale(kWallstickingDistanceCheck, -1, theMinPoint); - VectorCopy(kWallstickingDistanceCheck, theMaxPoint); - - // Trace corners of hull to see if any are touching a solid and - // compute an average surface normal. - - vec3_t theSurfaceNormal = { 0, 0, 0 }; - - bool wallsticking = false; - - // tankefugl: fix to allow skulks to climb walls oriented 45 degrees in the x-y plane - // it also allows for easier climbing around courners - //wallsticking |= NS_CheckOffsetFromOrigin(pmove->forward[0], pmove->forward[1], pmove->forward[2], theSurfaceNormal); - //if (wallsticking) - // VectorScale(theSurfaceNormal, 5, theSurfaceNormal); - // :tankefugl - - //wallsticking |= NS_CheckOffsetFromOrigin(theMinPoint[0], theMinPoint[1], theMinPoint[2], theSurfaceNormal); - //wallsticking |= NS_CheckOffsetFromOrigin(theMinPoint[0], theMaxPoint[1], theMinPoint[2], theSurfaceNormal); - //wallsticking |= NS_CheckOffsetFromOrigin(theMaxPoint[0], theMinPoint[1], theMinPoint[2], theSurfaceNormal); - //wallsticking |= NS_CheckOffsetFromOrigin(theMaxPoint[0], theMaxPoint[1], theMinPoint[2], theSurfaceNormal); - - wallsticking |= NS_CheckOffsetFromOrigin(theMinPoint[0], theMinPoint[1], theMaxPoint[2], theSurfaceNormal); - wallsticking |= NS_CheckOffsetFromOrigin(theMinPoint[0], theMaxPoint[1], theMaxPoint[2], theSurfaceNormal); - wallsticking |= NS_CheckOffsetFromOrigin(theMaxPoint[0], theMinPoint[1], theMaxPoint[2], theSurfaceNormal); - wallsticking |= NS_CheckOffsetFromOrigin(theMaxPoint[0], theMaxPoint[1], theMaxPoint[2], theSurfaceNormal); - - VectorNormalize(theSurfaceNormal); - - if (wallsticking) - { - // tankefugl: 0000972 - if (/*theSurfaceNormal[2] < 0.95 && */ pmove->waterjumptime == 0) - { - //float dotNormalView = DotProduct(pmove->forward, theSurfaceNormal); - vec3_t tempNormal = {0, 0, 0}; - bool checkedDownOffset = NS_CheckOffsetFromOrigin(0, 0, theMinPoint[2], tempNormal); - if (/*dotNormalView < 0.7 && */(checkedDownOffset == false)) - { - VectorCopy(theSurfaceNormal, gSurfaceNormal); - - if (/*theSurfaceNormal[2] < 0.7 && */(pmove->cmd.buttons & IN_WALK)) - { - vec3_t theDispVect; - VectorScale(theSurfaceNormal, theMinPoint[0], theDispVect); - VectorAdd(pmove->origin, theDispVect, theDispVect); - - pmtrace_t wallclimbTrace = NS_PlayerTrace(pmove, pmove->origin, theDispVect, PM_NORMAL, -1); - - if (wallclimbTrace.fraction < 1) - { - VectorCopy(wallclimbTrace.endpos, pmove->origin); - } - } - } - else - { - canWallJump = false; - } - - // Set wall sticking to true. - SetUpgradeMask(&pmove->iuser4, MASK_WALLSTICKING); - } - // :tankefugl - - vec3_t angles; - - #ifdef AVH_SERVER - - vec3_t worldViewAngles; - - Mat3 objectMatrix(pmove->vuser1); - Mat3 viewMatrix(pmove->angles); - - (objectMatrix * viewMatrix).GetEulerAngles(worldViewAngles); - - NS_GetWallstickingAngles(theSurfaceNormal, pmove->angles, angles); - VectorCopy(angles, pmove->vuser2); - - #endif - - #ifdef AVH_CLIENT - NS_GetWallstickingAngles(theSurfaceNormal, gWorldViewAngles, angles); - VectorCopy(angles, gTargetPlayerAngles); - #endif - - } - else - { - - // This seems like a good idea, but it doesn't work too well in - // practice (maybe the constant just need to be tweaked). - - /* - // If the Skulk is not wallsticking, then rotate to align with the - // surface he's moving towards (if there's one nearby). - - vec3_t traceEnd; - vec3_t traceDir; - - VectorCopy(pmove->velocity, traceDir); - VectorNormalize(traceDir); - - VectorScale(traceDir, kSkulkRotationLookAhead, traceDir); - VectorAdd(traceDir, pmove->origin, traceEnd); - - pmtrace_t trace = NS_PlayerTrace(pmove, pmove->origin, traceEnd, PM_NORMAL, -1 ); - - if (trace.fraction < 1) - { - - vec3_t angles; - - #ifdef AVH_SERVER - NS_GetWallstickingAngles(trace.plane.normal, pmove->vuser1, angles); - VectorCopy(angles, pmove->vuser2); - #endif - - #ifdef AVH_CLIENT - NS_GetWallstickingAngles(trace.plane.normal, gWorldViewAngles, angles); - VectorCopy(angles, gTargetPlayerAngles); - #endif - } - */ - - // When the player isn't wall sticking, just revert to the normal - // angles. - - #ifdef AVH_SERVER - VectorCopy(pmove->angles, pmove->vuser2); - #endif - - #ifdef AVH_CLIENT - VectorCopy(pmove->angles, gTargetPlayerAngles); - #endif - - } - - // These buttons have changed this frame - int theButtonsChanged = (pmove->oldbuttons ^ pmove->cmd.buttons); - - // The changed ones still down are "pressed" - //int theButtonPressed = theButtonsChanged & pmove->cmd.buttons; - - // If we're now wallsticking and we JUST pressed our duck button, slow our velocity - if(GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && (theButtonsChanged & IN_DUCK)) - { - //pmove->Con_Printf("Setting vel to 0\n"); - VectorScale(pmove->velocity, .999f, pmove->velocity); - //pmove->velocity[0] = pmove->velocity[1] = pmove->velocity[2] = 0.0f; - - // Trigger footstep immediately - pmove->flTimeStepSound = 0.0f; - } - } - else - { - #ifdef AVH_SERVER - //VectorCopy(pmove->angles, pmove->vuser2); - - vec3_t up = { 0, 0, 1 }; - NS_GetWallstickingAngles(up, pmove->angles, pmove->vuser2); - #endif - } - - } - -} - - -int NS_GetStepsize(int iuser3) -{ - // TODO: Move this into a server variable? - const int kDefaultStepsize = 18; - int theStepSize = kDefaultStepsize; - - //if((pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1) || (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER2)) - //{ - // theStepSize = kDefaultStepsize;//.66f*kDefaultStepsize; - //} - //else if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4) - //{ - // theStepSize = 1.3f*kDefaultStepsize; - //} - //else if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5) - //{ - // theStepSize = 1.6*kDefaultStepsize; - //} - //if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5) - //{ - // theStepSize = 1.3f*kDefaultStepsize; - //} - - return theStepSize; -} - - -void PM_SwapTextures( int i, int j ) -{ - char chTemp; - char szTemp[ CBTEXTURENAMEMAX ]; - - strcpy( szTemp, grgszTextureName[ i ] ); - chTemp = grgchTextureType[ i ]; - - strcpy( grgszTextureName[ i ], grgszTextureName[ j ] ); - grgchTextureType[ i ] = grgchTextureType[ j ]; - - strcpy( grgszTextureName[ j ], szTemp ); - grgchTextureType[ j ] = chTemp; -} - -void PM_SortTextures( void ) -{ - // Bubble sort, yuck, but this only occurs at startup and it's only 512 elements... - // - int i, j; - - for ( i = 0 ; i < gcTextures; i++ ) - { - for ( j = i + 1; j < gcTextures; j++ ) - { - if ( stricmp( grgszTextureName[ i ], grgszTextureName[ j ] ) > 0 ) - { - // Swap - // - PM_SwapTextures( i, j ); - } - } - } -} - -void PM_InitTextureTypes() -{ - char buffer[512]; - int i, j; - byte *pMemFile; - int fileSize, filePos; - static qboolean bTextureTypeInit = false; - - if ( bTextureTypeInit ) - return; - - memset(&(grgszTextureName[0][0]), 0, CTEXTURESMAX * CBTEXTURENAMEMAX); - memset(grgchTextureType, 0, CTEXTURESMAX); - - gcTextures = 0; - memset(buffer, 0, 512); - - fileSize = pmove->COM_FileSize( "sound/materials.txt" ); - pMemFile = pmove->COM_LoadFile( "sound/materials.txt", 5, NULL ); - if ( !pMemFile ) - return; - - filePos = 0; - // for each line in the file... - while ( pmove->memfgets( pMemFile, fileSize, &filePos, buffer, 511 ) != NULL && (gcTextures < CTEXTURESMAX) ) - { - // skip whitespace - i = 0; - while(buffer[i] && isspace(buffer[i])) - i++; - - if (!buffer[i]) - continue; - - // skip comment lines - if (buffer[i] == '/' || !isalpha(buffer[i])) - continue; - - // get texture type - grgchTextureType[gcTextures] = toupper(buffer[i++]); - - // skip whitespace - while(buffer[i] && isspace(buffer[i])) - i++; - - if (!buffer[i]) - continue; - - // get sentence name - j = i; - while (buffer[j] && !isspace(buffer[j])) - j++; - - if (!buffer[j]) - continue; - - // null-terminate name and save in sentences array - j = min (j, CBTEXTURENAMEMAX-1+i); - buffer[j] = 0; - strcpy(&(grgszTextureName[gcTextures++][0]), &(buffer[i])); - } - - // Must use engine to free since we are in a .dll - pmove->COM_FreeFile ( pMemFile ); - - PM_SortTextures(); - - bTextureTypeInit = true; -} - -char PM_FindTextureType( char *name ) -{ - int left, right, pivot; - int val; - - assert( pm_shared_initialized ); - - left = 0; - right = gcTextures - 1; - - while ( left <= right ) - { - pivot = ( left + right ) / 2; - - val = strnicmp( name, grgszTextureName[ pivot ], CBTEXTURENAMEMAX-1 ); - if ( val == 0 ) - { - return grgchTextureType[ pivot ]; - } - else if ( val > 0 ) - { - left = pivot + 1; - } - else if ( val < 0 ) - { - right = pivot - 1; - } - } - - return CHAR_TEX_CONCRETE; -} - -float PM_GetDesiredTopDownCameraHeight(qboolean& outFoundEntity) -{ -// vec3_t theTraceStart; -// vec3_t theTraceEnd; -// vec3_t theForward; -// pmtrace_t* theTrace = NULL; -// physent_t* theTarget = NULL; -// float theHeight = 320;//180; -// qboolean theDone = false; -// int theEntityHit = -1; -// -// // Start tracing at player, but at the highest point in the map -// VectorCopy(pmove->origin, theTraceStart); -// theTraceStart[2] = 750; -// -// VectorCopy(pmove->origin, theTraceEnd); -// theTraceEnd[2] = -500; -// -// //AngleVectors(pmove->angles, theForward, NULL, NULL); -// //VectorNormalize(theForward); -// theForward[0] = theForward[1] = 0; -// theForward[2] = -1; -// -// //VectorMA(pmove->origin, theHeight, theForward, theTraceEnd); -// -// do -// { -// //struct pmtrace_s *(*PM_TraceLine)( float *start, float *end, int flags, int usehull, int ignore_pe ); -// theTrace = pmove->PM_TraceLine(theTraceStart, theTraceEnd, PM_TRACELINE_ANYVISIBLE, 2, theEntityHit); -// -// // Find out if there are any view height entities at our x,y -// theEntityHit = theTrace->ent; -// if(theEntityHit > 0) -// { -// //theTarget = AvHSUGetEntity(theEntityHit); -// for (int i = 0; i < pmove->numphysent; i++) -// { -// if ( pmove->physents[i].info == theEntityHit) -// { -// theTarget = pmove->physents + i; -// } -// } -// } -// -// // If so, pick the nearest one and set our height to it -// if(theTarget && (theTarget->iuser3 == AVH_USER3_VIEWHEIGHT)) -// { -// // Use it's center for the height -// theHeight = (theTarget->maxs[2] + theTarget->mins[2])/2.0f; -// -// outFoundEntity = true; -// -// // We're done -// theDone = true; -// } -// //if(!theTarget) -// //{ -// // theDone = true; -// //} -// -// // Do the trace again but starting at this point -// VectorCopy(theTrace->endpos, theTraceStart); -//// if(theTrace->startsolid) -//// { -//// VectorMA(theTrace->endpos, 10, theForward, theTraceStart); -//// } -// } -// // While the end point is not a view entity and not below -// //while(theTrace->fraction > kFloatTolerance && !theDone); -// while((theTrace->fraction != 1.0f) && !theDone /*&& (!theTrace->startsolid || theTrace->fraction != 0)*/); -// -// // Return the result -// return theHeight; - return 640; -} - -bool NS_GetIsPlayerAlien(string& outExtension, float* outVolume = NULL) -{ - int theUser3 = pmove->iuser3; - - // Default alien extension - bool theIsAlien1 = (theUser3 == AVH_USER3_ALIEN_PLAYER1); - bool theIsAlien2 = (theUser3 == AVH_USER3_ALIEN_PLAYER2); - bool theIsAlien3 = (theUser3 == AVH_USER3_ALIEN_PLAYER3); - bool theIsAlien4 = (theUser3 == AVH_USER3_ALIEN_PLAYER4); - bool theIsAlien5 = (theUser3 == AVH_USER3_ALIEN_PLAYER5); - - if(theIsAlien1) - { - outExtension = kAlien1FootstepExtension; - } - else if(theIsAlien5) - { - outExtension = kAlien5FootstepExtension; - if(outVolume) - { - *outVolume = .3f; - } - } - else if(theIsAlien2 || theIsAlien3 || theIsAlien4) - { - outExtension = kAlienFootstepExtension; - } - - return theIsAlien1 || theIsAlien2 || theIsAlien3 || theIsAlien4 || theIsAlien5; -} - -void NS_PlayStepSound(int inMaterialType, int inSoundNumber, float inVolume) -{ - // Is this an alien step sound? - string theExtension; - bool theIsAlien = NS_GetIsPlayerAlien(theExtension, &inVolume); - - // If we have heavy armor - if((pmove->iuser3 == AVH_USER3_MARINE_PLAYER) && GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_13)) - { - theExtension = kHeavyFootstepExtension; - - // Can't be too quiet with heavy armor - inVolume = max(inVolume, .5f); - } - - // Build base name - string theBaseName; - - // Switch material type - switch(inMaterialType) - { - case STEP_CONCRETE: - theBaseName = kConcreteBaseName; - break; - - case STEP_METAL: - theBaseName = kMetalBaseName; - break; - - case STEP_DIRT: - theBaseName = kDirtBaseName; - break; - - case STEP_VENT: - theBaseName = kDuctBaseName; - break; - - case STEP_GRATE: - theBaseName = kGrateBaseName; - break; - - case STEP_TILE: - theBaseName = kTileBaseName; - break; - - case STEP_SLOSH: - theBaseName = kSloshBaseName; - break; - - case STEP_WADE: - theBaseName = kWadeBaseName; - break; - - case STEP_LADDER: - theBaseName = kLadderBaseName; - break; - } - - // Map random number to indices Valve uses - int theFinalNumber; - switch(inSoundNumber) - { - case 0: - theFinalNumber = 1; - break; - case 1: - theFinalNumber = 3; - break; - case 2: - theFinalNumber = 2; - break; - case 3: - theFinalNumber = 4; - break; - case 4: - theFinalNumber = 5; - break; - } - - // If player is walking, don't play sound - float theWalkFactor = .5f;//AvHMUGetWalkSpeedFactor((AvHUser3)pmove->iuser3); - - int theCurrentSpeed = Length(pmove->velocity);//sqrt( pmove->cmd.forwardmove * pmove->cmd.forwardmove ) + ( pmove->cmd.sidemove * pmove->cmd.sidemove ) + ( pmove->cmd.upmove * pmove->cmd.upmove ); - int theMaxSpeed = pmove->clientmaxspeed; - theMaxSpeed = pmove->maxspeed; - float theSpeedFraction = theCurrentSpeed/(float)theMaxSpeed; - - bool thePlayingFootstep = true; - if(theSpeedFraction > theWalkFactor) - { - // Construct full name using base name and sound number - char theFinalName[128]; - sprintf(theFinalName, "%s%s%d", kFootstepDirectory, theBaseName.c_str(), theFinalNumber); - - //if(theIsAlien && (inMaterialType != STEP_WADE)) - float theNorm = ATTN_NORM; - if((theExtension != "") && (inMaterialType == STEP_CONCRETE)) - { - //theNorm = ATTN_IDLE; - strcat(theFinalName, theExtension.c_str()); - } - strcat(theFinalName, ".wav"); - - // If alien has silencio upgrade, mute footstep volume - float theSilenceUpgradeFactor = 0.0f; - - if(theIsAlien) - { - int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); - theSilenceUpgradeFactor = theSilenceUpgradeLevel/3.0f; - } - - inVolume = inVolume - inVolume*theSilenceUpgradeFactor; - - // Play it at the specified volume - pmove->PM_PlaySound(CHAN_BODY, theFinalName, inVolume, theNorm, 0, PITCH_NORM); - } - else - { -// pmove->Con_DPrintf("Skipping footstep sound\n"); - thePlayingFootstep = false; - } - -// pmove->Con_DPrintf("current speed: %d, max speed: %d, fraction %f, played footstep: %d\n", theCurrentSpeed, theMaxSpeed, theSpeedFraction, thePlayingFootstep); -} - -void PM_PlayStepSound( int step, float fvol ) -{ - static int iSkipStep = 0; - int irand; - vec3_t hvel; - - pmove->iStepLeft = !pmove->iStepLeft; - - if ( !pmove->runfuncs ) - { - return; - } - - irand = pmove->RandomLong(0,1) + ( pmove->iStepLeft * 2 ); - - // Don't play footsteps when ducked - // FIXME mp_footsteps needs to be a movevar - if((pmove->multiplayer && !pmove->movevars->footsteps) || (pmove->flags & FL_DUCKING)) - { - return; - } - - VectorCopy( pmove->velocity, hvel ); - hvel[2] = 0.0; - - //if ( pmove->multiplayer && ( !g_onladder && Length( hvel ) <= 220 ) ) (use CBasePlayer::GetMaxWalkSpeed somehow if ever uncommenting this) - // return; - - // irand - 0,1 for right foot, 2,3 for left foot - // used to alternate left and right foot - // FIXME, move to player state - - switch (step) - { - default: - case STEP_CONCRETE: - case STEP_METAL: - case STEP_DIRT: - case STEP_VENT: - case STEP_SLOSH: - case STEP_GRATE: - case STEP_WADE: - case STEP_LADDER: - NS_PlayStepSound(step, irand, fvol); - break; - - case STEP_TILE: - if ( !pmove->RandomLong(0,4) ) - irand = 4; - NS_PlayStepSound(step, irand, fvol); - break; - } - -// switch (step) -// { -// default: -// case STEP_CONCRETE: -// switch (irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_step1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_step3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_step2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_step4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// case STEP_METAL: -// switch(irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_metal1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_metal3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_metal2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_metal4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// case STEP_DIRT: -// switch(irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_dirt1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_dirt3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_dirt2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_dirt4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// case STEP_VENT: -// switch(irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_duct1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_duct3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_duct2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_duct4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// case STEP_GRATE: -// switch(irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_grate1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_grate3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_grate2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_grate4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// case STEP_TILE: -// if ( !pmove->RandomLong(0,4) ) -// irand = 4; -// switch(irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_tile1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_tile3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_tile2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_tile4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 4: pmove->PM_PlaySound( CHAN_BODY, "player/pl_tile5.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// case STEP_SLOSH: -// switch(irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_slosh1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_slosh3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_slosh2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_slosh4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// case STEP_WADE: -// if ( iSkipStep == 0 ) -// { -// iSkipStep++; -// break; -// } -// -// if ( iSkipStep++ == 3 ) -// { -// iSkipStep = 0; -// } -// -// switch (irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// case STEP_LADDER: -// switch(irand) -// { -// // right foot -// case 0: pmove->PM_PlaySound( CHAN_BODY, "player/pl_ladder1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 1: pmove->PM_PlaySound( CHAN_BODY, "player/pl_ladder3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// // left foot -// case 2: pmove->PM_PlaySound( CHAN_BODY, "player/pl_ladder2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// case 3: pmove->PM_PlaySound( CHAN_BODY, "player/pl_ladder4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; -// } -// break; -// } -} - -int PM_MapTextureTypeStepType(char chTextureType) -{ - int theTextureType = 0; - - switch (chTextureType) - { - default: - case CHAR_TEX_CONCRETE: - theTextureType = STEP_CONCRETE; - break; - - case CHAR_TEX_METAL: - theTextureType = STEP_METAL; - break; - - case CHAR_TEX_DIRT: - theTextureType = STEP_DIRT; - break; - - case CHAR_TEX_VENT: - theTextureType = STEP_VENT; - break; - - case CHAR_TEX_GRATE: - theTextureType = STEP_GRATE; - break; - - case CHAR_TEX_TILE: - theTextureType = STEP_TILE; - break; - - case CHAR_TEX_SLOSH: - theTextureType = STEP_SLOSH; - break; - } - - return theTextureType; -} - -/* -==================== -PM_CatagorizeTextureType - -Determine texture info for the texture we are standing on. -==================== -*/ -void PM_CatagorizeTextureType( void ) -{ - vec3_t start, end; - const char *pTextureName; - - VectorCopy( pmove->origin, start ); - VectorCopy( pmove->origin, end ); - - // Straight down - end[2] -= 64; - - // Fill in default values, just in case. - pmove->sztexturename[0] = '\0'; - pmove->chtexturetype = CHAR_TEX_CONCRETE; - - pTextureName = pmove->PM_TraceTexture( pmove->onground, start, end ); - if ( !pTextureName ) - return; - - // strip leading '-0' or '+0~' or '{' or '!' - if (*pTextureName == '-' || *pTextureName == '+') - pTextureName += 2; - - if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') - pTextureName++; - // '}}' - - strcpy( pmove->sztexturename, pTextureName); - pmove->sztexturename[ CBTEXTURENAMEMAX - 1 ] = 0; - - // get texture type - pmove->chtexturetype = PM_FindTextureType( pmove->sztexturename ); -} - -void PM_GetSpeeds(float& outVelWalk, float& outVelRun) -{ -} - -float PM_SetStepInterval() -{ - //int theCurrentSpeed = Length(pmove->velocity); - int theCurrentSpeed = ( pmove->cmd.forwardmove * pmove->cmd.forwardmove ) + - ( pmove->cmd.sidemove * pmove->cmd.sidemove ) + - ( pmove->cmd.upmove * pmove->cmd.upmove ); - theCurrentSpeed = sqrt( (double)theCurrentSpeed ); - - int theMaxSpeed = kMaxGroundPlayerSpeed;//pmove->clientmaxspeed; - -// int kFastestFootstepInterval = BALANCE_VAR(kFootstepFastInterval); -// int kSlowestFootstepInterval = BALANCE_VAR(kFootstepSlowInterval); -// -// if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5) -// { -// kFastestFootstepInterval = 200; -// kSlowestFootstepInterval = 800; -// } - - // Play louder as we get closer to max speed - float theFraction = (float)theCurrentSpeed/theMaxSpeed; - -// int theCurrentInterval = kSlowestFootstepInterval - (kSlowestFootstepInterval - kFastestFootstepInterval)*theFraction; - - int theScalar = 800; - - switch(pmove->iuser3) - { - case AVH_USER3_MARINE_PLAYER: - if(GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_13)) - { - theScalar = BALANCE_VAR(kFootstepHeavyScalar); - } - else - { - theScalar = BALANCE_VAR(kFootstepMarineScalar); - } - break; - case AVH_USER3_ALIEN_PLAYER1: - theScalar = BALANCE_VAR(kFootstepSkulkScalar); - break; - case AVH_USER3_ALIEN_PLAYER2: - theScalar = BALANCE_VAR(kFootstepGorgeScalar); - break; - case AVH_USER3_ALIEN_PLAYER3: - theScalar = BALANCE_VAR(kFootstepLerkScalar); - break; - case AVH_USER3_ALIEN_PLAYER4: - theScalar = BALANCE_VAR(kFootstepFadeScalar); - break; - case AVH_USER3_ALIEN_PLAYER5: - theScalar = BALANCE_VAR(kFootstepOnosScalar); - break; - } - - int theCurrentInterval = max(((float)theScalar/theCurrentSpeed)*100, 200); - - pmove->flTimeStepSound = theCurrentInterval; - - return theFraction; -} - -void PM_SetHulls( void ) -{ - pmove->player_mins[0][0] = HULL0_MINX; - pmove->player_mins[0][1] = HULL0_MINY; - pmove->player_mins[0][2] = HULL0_MINZ; - - pmove->player_maxs[0][0] = HULL0_MAXX; - pmove->player_maxs[0][1] = HULL0_MAXY; - pmove->player_maxs[0][2] = HULL0_MAXZ; - - pmove->player_mins[1][0] = HULL1_MINX; - pmove->player_mins[1][1] = HULL1_MINY; - pmove->player_mins[1][2] = HULL1_MINZ; - - pmove->player_maxs[1][0] = HULL1_MAXX; - pmove->player_maxs[1][1] = HULL1_MAXY; - pmove->player_maxs[1][2] = HULL1_MAXZ; - - pmove->player_mins[2][0] = HULL2_MINX; - pmove->player_mins[2][1] = HULL2_MINY; - pmove->player_mins[2][2] = HULL2_MINZ; - - pmove->player_maxs[2][0] = HULL2_MAXX; - pmove->player_maxs[2][1] = HULL2_MAXY; - pmove->player_maxs[2][2] = HULL2_MAXZ; - - pmove->player_mins[3][0] = HULL3_MINX; - pmove->player_mins[3][1] = HULL3_MINY; - pmove->player_mins[3][2] = HULL3_MINZ; - - pmove->player_maxs[3][0] = HULL3_MAXX; - pmove->player_maxs[3][1] = HULL3_MAXY; - pmove->player_maxs[3][2] = HULL3_MAXZ; - - - // Work around for a problem in the engine where it always uses the default HL - // hull sizes for the clip_mins and clip_maxs. - - for (int i = 0; i < pmove->numphysent; ++i) - { - - // HACK: for some reason the player field isn't correct on the clients. - // This is either a problem with HL or with NS's propagation of entities. - - if (pmove->physents[i].info >= 1 && pmove->physents[i].info <= 32) - { - pmove->physents[i].player = 1; - } - else - { - pmove->physents[i].player = 0; - } - - model_s* model = pmove->physents[i].model; - - if (model) - { - - model->hulls[0].clip_mins[0] = HULL2_MINX; - model->hulls[0].clip_mins[1] = HULL2_MINY; - model->hulls[0].clip_mins[2] = HULL2_MINZ; - - model->hulls[0].clip_maxs[0] = HULL2_MAXX; - model->hulls[0].clip_maxs[1] = HULL2_MAXY; - model->hulls[0].clip_maxs[2] = HULL2_MAXZ; - - model->hulls[1].clip_mins[0] = HULL0_MINX; - model->hulls[1].clip_mins[1] = HULL0_MINY; - model->hulls[1].clip_mins[2] = HULL0_MINZ; - - model->hulls[1].clip_maxs[0] = HULL0_MAXX; - model->hulls[1].clip_maxs[1] = HULL0_MAXY; - model->hulls[1].clip_maxs[2] = HULL0_MAXZ; - - model->hulls[2].clip_mins[0] = HULL3_MINX; - model->hulls[2].clip_mins[1] = HULL3_MINY; - model->hulls[2].clip_mins[2] = HULL3_MINZ; - - model->hulls[2].clip_maxs[0] = HULL3_MAXX; - model->hulls[2].clip_maxs[1] = HULL3_MAXY; - model->hulls[2].clip_maxs[2] = HULL3_MAXZ; - - model->hulls[3].clip_mins[0] = HULL1_MINX; - model->hulls[3].clip_mins[1] = HULL1_MINY; - model->hulls[3].clip_mins[2] = HULL1_MINZ; - - model->hulls[3].clip_maxs[0] = HULL1_MAXX; - model->hulls[3].clip_maxs[1] = HULL1_MAXY; - model->hulls[3].clip_maxs[2] = HULL1_MAXZ; - - } - - } - -} - -void PM_UpdateStepSound( void ) -{ - if(!GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - { - int fWalking; - float fvol; - vec3_t knee; - vec3_t feet; - vec3_t center; - float height; - float speed; - float velrun; - float velwalk; -// float flduck; - int fLadder; - int step; - - if ( pmove->flTimeStepSound > 0 ) - return; - - if ( pmove->flags & FL_FROZEN ) - return; - - PM_CatagorizeTextureType(); - - speed = Length( pmove->velocity ); - - // determine if we are on a ladder - fLadder = ( pmove->movetype == MOVETYPE_FLY );// IsOnLadder(); - - // UNDONE: need defined numbers for run, walk, crouch, crouch run velocities!!!! - if ( !GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && (( pmove->flags & FL_DUCKING) || fLadder) ) - { - velwalk = 60; // These constants should be based on cl_movespeedkey * cl_forwardspeed somehow - velrun = 80; // UNDONE: Move walking to server -// flduck = 100; - } - else - { - velwalk = 120; - velrun = 210; -// flduck = 0; - } - - // If we're on a ladder or on the ground, and we're moving fast enough, - // play step sound. Also, if pmove->flTimeStepSound is zero, get the new - // sound right away - we just started moving in new level. - if ( (fLadder || GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) || ( pmove->onground != -1 ) ) && ( Length( pmove->velocity ) > 0.0 )) - { - fWalking = speed < velrun; - - VectorCopy( pmove->origin, center ); - VectorCopy( pmove->origin, knee ); - VectorCopy( pmove->origin, feet ); - - height = pmove->player_maxs[ pmove->usehull ][ 2 ] - pmove->player_mins[ pmove->usehull ][ 2 ]; - - knee[2] = pmove->origin[2] - 0.3 * height; - feet[2] = pmove->origin[2] - 0.5 * height; - - // find out what we're stepping in or on... - if (fLadder) - { - step = STEP_LADDER; - fvol = 0.35; - pmove->flTimeStepSound = 350; - } - else if ( pmove->PM_PointContents ( knee, NULL ) == CONTENTS_WATER ) - { - step = STEP_WADE; - fvol = 0.65; - pmove->flTimeStepSound = 600; - } - else if ( pmove->PM_PointContents ( feet, NULL ) == CONTENTS_WATER ) - { - step = STEP_SLOSH; - fvol = fWalking ? 0.2 : 0.5; - pmove->flTimeStepSound = fWalking ? 400 : 300; - } - else - { - // find texture under player, if different from current texture, - // get material type - step = PM_MapTextureTypeStepType( pmove->chtexturetype ); - float theFraction = PM_SetStepInterval(); - - switch ( pmove->chtexturetype ) - { - default: - case CHAR_TEX_CONCRETE: - case CHAR_TEX_METAL: - case CHAR_TEX_GRATE: - case CHAR_TEX_TILE: - case CHAR_TEX_SLOSH: - //fvol = .2f + .3f*theFraction; - fvol = .7f*theFraction; - break; - - case CHAR_TEX_DIRT: - fvol = .75f*theFraction; - //fvol = .25f + .3f*theFraction; - //fvol = fWalking ? 0.25 : 0.55; - //pmove->flTimeStepSound = fWalking ? 400 : 300; - break; - - case CHAR_TEX_VENT: - fvol = .3f + .5f*theFraction; - //fvol = .5f + .3f*theFraction; - //fvol = fWalking ? 0.4 : 0.7; - //pmove->flTimeStepSound = fWalking ? 400 : 300; - break; - } - } - -// pmove->flTimeStepSound += flduck; // slower step time if ducking - - // play the sound - // 35% volume if ducking - if (!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && (pmove->flags & FL_DUCKING)) - { - fvol *= 0.35; - } - - PM_PlayStepSound( step, fvol ); - } - } -} - -/* -================ -PM_AddToTouched - -Add's the trace result to touch list, if contact is not already in list. -================ -*/ -qboolean PM_AddToTouched(pmtrace_t tr, vec3_t impactvelocity) -{ - int i; - - for (i = 0; i < pmove->numtouch; i++) - { - if (pmove->touchindex[i].ent == tr.ent) - break; - } - if (i != pmove->numtouch) // Already in list. - return false; - - VectorCopy( impactvelocity, tr.deltavelocity ); - - if (pmove->numtouch >= MAX_PHYSENTS) - pmove->Con_DPrintf("Too many entities were touched!\n"); - - pmove->touchindex[pmove->numtouch++] = tr; - return true; -} - -/* -================ -PM_CheckVelocity - -See if the player has a bogus velocity value. -================ -*/ -void PM_CheckVelocity () -{ - int i; - -// -// bound velocity -// - for (i=0 ; i<3 ; i++) - { - // See if it's bogus. - if (IS_NAN(pmove->velocity[i])) - { - pmove->Con_Printf ("PM Got a NaN velocity %i\n", i); - pmove->velocity[i] = 0; - } - if (IS_NAN(pmove->origin[i])) - { - pmove->Con_Printf ("PM Got a NaN origin on %i\n", i); - pmove->origin[i] = 0; - } - - // Bound it. - if (pmove->velocity[i] > pmove->movevars->maxvelocity) - { - pmove->Con_DPrintf ("PM Got a velocity too high on %i\n", i); - pmove->velocity[i] = pmove->movevars->maxvelocity; - } - else if (pmove->velocity[i] < -pmove->movevars->maxvelocity) - { - pmove->Con_DPrintf ("PM Got a velocity too low on %i\n", i); - pmove->velocity[i] = -pmove->movevars->maxvelocity; - } - - } -} - -/* -================== -PM_ClipVelocity - -Slide off of the impacting object -returns the blocked flags: -0x01 == floor -0x02 == step / wall -================== -*/ -int PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) -{ - float backoff; - float change; - float angle; - int i, blocked; - - angle = normal[ 2 ]; - - blocked = 0x00; // Assume unblocked. - if (angle > 0) // If the plane that is blocking us has a positive z component, then assume it's a floor. - blocked |= 0x01; // - if (!angle) // If the plane has no Z, it is vertical (wall/step) - blocked |= 0x02; // - - // Determine how far along plane to slide based on incoming direction. - // Scale by overbounce factor. - backoff = DotProduct (in, normal) * overbounce; - - for (i=0 ; i<3 ; i++) - { - change = normal[i]*backoff; - out[i] = in[i] - change; - // If out velocity is too small, zero it out. - if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) - out[i] = 0; - } - - // Return blocking flags. - return blocked; -} - -float PM_GetHorizontalSpeed() -{ - return sqrtf(pmove->velocity[0] * pmove->velocity[0] + - pmove->velocity[1] * pmove->velocity[1]); -} - -void PM_AddCorrectGravity() -{ - float ent_gravity; - - // TODO: Put back in alien glide when alien upgrades worked out -// if(GetHasUpgrade(pmove->iuser4, ALIEN_ABILITY_1)) -// { -// pmove->gravity = .2f; -// } -/* else */ - if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) - { - /* - // Glide down - if((pmove->cmd.buttons & IN_JUMP) && (pmove->velocity[2] <= 0.0f)) - { - pmove->gravity = .10f; - } - else - { - pmove->gravity = .55f; - } - */ - - float theMinGravity = 0.10f; - float theMaxGravity = 0.75f; - float theGravity = theMaxGravity; - - if (pmove->cmd.buttons & IN_JUMP) - { - - float theSpeed = PM_GetHorizontalSpeed(); - float theLift = (theSpeed / 300) * (pmove->forward[2] + 0.5) / 1.5; - - if (theLift < 0) - { - theLift = 0; - } - - theGravity = theMinGravity + (1 - theLift) * (theMaxGravity - theMinGravity); - - } - - pmove->gravity = max(min(theGravity, theMaxGravity), theMinGravity); - } - else - { - pmove->gravity = 1.0f; - } - - // If they're in the air and they have jump held down, and they have the gliding type, - // apply less gravity to them -// if ( pmove->cmd.buttons & IN_JUMP ) -// { -// if(pmove->iuser3 == AVH_USER3_GLIDE) -// { -// // Only glide on the way down -// if(pmove->velocity[2] < 0) -// { -// if(pmove->onground == -1) -// { -// // Glide and drag baybee -// pmove->gravity = .15f; -// pmove->velocity[0] /= 1.01f; -// pmove->velocity[1] /= 1.01f; -// } -// } -// } -// } - - // tankefugl: 0000972 - if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) - // :tankefugl - return; - - if (pmove->gravity) - ent_gravity = pmove->gravity; - else - ent_gravity = 1.0; - - if(GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) || GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - { - pmove->gravity = ent_gravity = 0.0f; - } - - // Add gravity so they'll be in the correct position during movement - // yes, this 0.5 looks wrong, but it's not. - pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * 0.5 * pmove->frametime ); - pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; - pmove->basevelocity[2] = 0; - - PM_CheckVelocity(); -} - - -void PM_FixupGravityVelocity () -{ - float ent_gravity; - - // tankefugl: 0000972 - if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) - // :tankefugl - return; - - if (pmove->gravity) - ent_gravity = pmove->gravity; - else - ent_gravity = 1.0; - - if(GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) || GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - { - pmove->gravity = ent_gravity = 0.0f; - } - - // Get the correct velocity for the end of the dt - pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * pmove->frametime * 0.5 ); - - PM_CheckVelocity(); -} - -/* -============ -PM_FlyMove - -The basic solid body movement clip that slides along multiple planes -============ -*/ -int PM_FlyMove (void) -{ - int bumpcount, numbumps; - vec3_t dir; - float d; - int numplanes; - vec3_t planes[MAX_CLIP_PLANES]; - vec3_t primal_velocity, original_velocity; - vec3_t new_velocity; - int i, j; - pmtrace_t trace; - vec3_t end; - float time_left, allFraction; - int blocked; - - numbumps = 4; // Bump up to four times - - blocked = 0; // Assume not blocked - numplanes = 0; // and not sliding along any planes - VectorCopy (pmove->velocity, original_velocity); // Store original velocity - VectorCopy (pmove->velocity, primal_velocity); - - allFraction = 0; - time_left = pmove->frametime; // Total time for this movement operation. - - // CGC - Removed zeroing of velocity so commander can float around easily outside of world 1/11/02 - bool theIsInTopDown = (pmove->iuser4 & MASK_TOPDOWN); - - for (bumpcount=0 ; bumpcountvelocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) - break; - - // Assume we can move all the way from the current origin to the - // end point. - for (i=0 ; i<3 ; i++) - end[i] = pmove->origin[i] + time_left * pmove->velocity[i]; - - // See if we can make it from origin to end point. - - trace = NS_PlayerTrace (pmove, pmove->origin, end, PM_NORMAL, -1 ); - - if(theIsInTopDown) - { - // Don't collide with anything when we're commander - VectorCopy(end, trace.endpos); - } - - allFraction += trace.fraction; - // If we started in a solid object, or we were in solid space - // the whole way, zero out our velocity and return that we - // are blocked by floor and wall. - if (trace.allsolid && !theIsInTopDown) - { - // entity is trapped in another solid - VectorCopy (vec3_origin, pmove->velocity); - //Con_DPrintf("Trapped 4\n"); - return 4; - } - - // If we moved some portion of the total distance, then - // copy the end position into the pmove->origin and - // zero the plane counter. - if (trace.fraction > 0 || theIsInTopDown) - { // actually covered some distance - VectorCopy (trace.endpos, pmove->origin); - VectorCopy (pmove->velocity, original_velocity); - numplanes = 0; - } - - // If we covered the entire distance, we are done - // and can return. - if (trace.fraction == 1 || theIsInTopDown) - break; // moved the entire distance - - //if (!trace.ent) - // Sys_Error ("PM_PlayerTrace: !trace.ent"); - - // Save entity that blocked us (since fraction was < 1.0) - // for contact - // Add it if it's not already in the list!!! - PM_AddToTouched(trace, pmove->velocity); - - // If the plane we hit has a high z component in the normal, then - // it's probably a floor - if (trace.plane.normal[2] > 0.7) - { - blocked |= 1; // floor - } - // If the plane has a zero z component in the normal, then it's a - // step or wall - if (!trace.plane.normal[2]) - { - blocked |= 2; // step / wall - //Con_DPrintf("Blocked by %i\n", trace.ent); - } - - // Reduce amount of pmove->frametime left by total time left * fraction - // that we covered. - time_left -= time_left * trace.fraction; - - // Did we run out of planes to clip against? - if (numplanes >= MAX_CLIP_PLANES) - { // this shouldn't really happen - // Stop our movement if so. - VectorCopy (vec3_origin, pmove->velocity); - //Con_DPrintf("Too many planes 4\n"); - - break; - } - - // Set up next clipping plane - VectorCopy (trace.plane.normal, planes[numplanes]); - numplanes++; -// - -// modify original_velocity so it parallels all of the clip planes -// - if ( pmove->movetype == MOVETYPE_WALK && - ((pmove->onground == -1) || (pmove->friction != 1)) ) // relfect player velocity - { - for ( i = 0; i < numplanes; i++ ) - { - if ( planes[i][2] > 0.7 ) - {// floor or slope - PM_ClipVelocity( original_velocity, planes[i], new_velocity, 1 ); - VectorCopy( new_velocity, original_velocity ); - } - else - PM_ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + pmove->movevars->bounce * (1-pmove->friction) ); - } - - VectorCopy( new_velocity, pmove->velocity ); - VectorCopy( new_velocity, original_velocity ); - } - else - { - for (i=0 ; ivelocity, - 1); - for (j=0 ; jvelocity, planes[j]) < 0) - break; // not ok - } - if (j == numplanes) // Didn't have to clip, so we're ok - break; - } - - // Did we go all the way through plane set - if (i != numplanes) - { // go along this plane - // pmove->velocity is set in clipping call, no need to set again. - ; - } - else - { // go along the crease - if (numplanes != 2) - { - //Con_Printf ("clip velocity, numplanes == %i\n",numplanes); - VectorCopy (vec3_origin, pmove->velocity); - //Con_DPrintf("Trapped 4\n"); - - break; - } - CrossProduct (planes[0], planes[1], dir); - d = DotProduct (dir, pmove->velocity); - VectorScale (dir, d, pmove->velocity ); - } - - // - // if original velocity is against the original velocity, stop dead - // to avoid tiny occilations in sloping corners - // - if (DotProduct (pmove->velocity, primal_velocity) <= 0) - { - //Con_DPrintf("Back\n"); - VectorCopy (vec3_origin, pmove->velocity); - break; - } - } - } - - if ( allFraction == 0 ) - { - if(!GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - { - VectorCopy (vec3_origin, pmove->velocity); - } - //Con_DPrintf( "Don't stick\n" ); - } - - return blocked; -} - -/* -============== -PM_Accelerate -============== -*/ -void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel) -{ - int i; - float addspeed, accelspeed, currentspeed; - - // Dead player's don't accelerate - if (pmove->dead) - return; - - // If waterjumping, don't accelerate - // tankefugl: 0000972 - if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) - // :tankefugl - return; - - // See if we are changing direction a bit - currentspeed = DotProduct (pmove->velocity, wishdir); - - // Reduce wishspeed by the amount of veer. - addspeed = wishspeed - currentspeed; - - // If not going to add any speed, done. - if (addspeed <= 0) - return; - - // Determine amount of accleration. - accelspeed = accel * pmove->frametime * wishspeed * pmove->friction; - - // Cap at addspeed - if (accelspeed > addspeed) - accelspeed = addspeed; - - // Adjust velocity. - for (i=0 ; i<3 ; i++) - { - pmove->velocity[i] += accelspeed * wishdir[i]; - } -} - -/* -===================== -PM_WalkMove - -Only used by players. Moves along the ground when player is a MOVETYPE_WALK. -====================== -*/ -void PM_WalkMove () -{ - int clip; - int oldonground; - int i; - - vec3_t wishvel; - float spd; - float fmove, smove, umove; - vec3_t wishdir; - float wishspeed; - - vec3_t dest, start; - vec3_t original, originalvel; - vec3_t down, downvel; - float downdist, updist; - - pmtrace_t trace; - - // Copy movement amounts - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - // Added by mmcguire. - umove = pmove->cmd.upmove; - - float theVelocityLength = Length(pmove->velocity); - //pmove->Con_Printf("Max speed: %f, velocity: %f\n", pmove->maxspeed, theVelocityLength); - - // Marines move slower when moving backwards or sideways - if(pmove->iuser3 == AVH_USER3_MARINE_PLAYER) - { - if(fmove < 0) - { - fmove *= kMarineBackpedalSpeedScalar; - } - - smove *= kMarineSidestepSpeedScalar; - } - - if(GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - { - - vec3_t theForwardView, theRightView, theUpView; - - VectorCopy(pmove->forward, theForwardView); - VectorCopy(pmove->up, theUpView); - - VectorNormalize(theForwardView); - VectorNormalize(theUpView); - - CrossProduct(theForwardView, theUpView, theRightView); - - // Changed by mmcguire. Added up movement. - for (i=0 ; i<3 ; i++) - { - wishvel[i] = theForwardView[i]*fmove + theRightView[i]*smove + theUpView[i]*umove; - } - - } - else - { - // Zero out z components of movement vectors - pmove->forward[2] = 0; - pmove->right[2] = 0; - - VectorNormalize (pmove->forward); // Normalize remainder of vectors. - VectorNormalize (pmove->right); // - - //pmove->Con_Printf("PM_WalkMove()->fmove: %f\n", fmove); - - if (PM_GetIsCharging()) - { - SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, true); - - // Modify fmove? - fmove = 500; - } - - for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity - wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; - - wishvel[2] = 0; // Zero out z part of velocity - } - - VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move - wishspeed = VectorNormalize(wishdir); - -// -// Clamp to server defined max speed -// - if (wishspeed > pmove->maxspeed) - { - VectorScale (wishvel, pmove->maxspeed/wishspeed, wishvel); - wishspeed = pmove->maxspeed; - } - - // Set pmove velocity - if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - pmove->velocity[2] = 0; - - PM_Accelerate (wishdir, wishspeed, pmove->movevars->accelerate); - - if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - pmove->velocity[2] = 0; - - // Add in any base velocity to the current velocity. - VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity ); - - spd = Length( pmove->velocity ); - - if (spd < 1.0f) - { - VectorClear( pmove->velocity ); - return; - } - - // If we are not moving, do nothing - //if (!pmove->velocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) - // return; - - oldonground = pmove->onground; - -// first try just moving to the destination - dest[0] = pmove->origin[0] + pmove->velocity[0]*pmove->frametime; - dest[1] = pmove->origin[1] + pmove->velocity[1]*pmove->frametime; - - // Wall-sticking change - //dest[2] = pmove->origin[2]; - dest[2] = pmove->origin[2] + pmove->velocity[2]*pmove->frametime; - - // first try moving directly to the next spot - VectorCopy (dest, start); - - trace = NS_PlayerTrace (pmove, pmove->origin, dest, PM_NORMAL, -1 ); - - - // If we made it all the way, then copy trace end - // as new player position. - if (trace.fraction == 1) - { - VectorCopy (trace.endpos, pmove->origin); - return; - } - - if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - { - if (oldonground == -1 && // Don't walk up stairs if not on ground. - pmove->waterlevel == 0) - return; - } - - // tankefugl: 0000972 - if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) - // :tankefugl - // If we are jumping out of water, don't do anything more. - return; - - // Try sliding forward both on ground and up 16 pixels - // take the move that goes farthest - VectorCopy (pmove->origin, original); // Save out original pos & - VectorCopy (pmove->velocity, originalvel); // velocity. - - // Slide move - clip = PM_FlyMove (); - - // Copy the results out - VectorCopy (pmove->origin , down); - VectorCopy (pmove->velocity, downvel); - - // Reset original values. - VectorCopy (original, pmove->origin); - - VectorCopy (originalvel, pmove->velocity); - - // Start out up one stair height - VectorCopy (pmove->origin, dest); - //dest[2] += pmove->movevars->stepsize; - dest[2] += NS_GetStepsize(pmove->iuser3); - - trace = NS_PlayerTrace (pmove, pmove->origin, dest, PM_NORMAL, -1 ); - - // If we started okay and made it part of the way at least, - // copy the results to the movement start position and then - // run another move try. - if (!trace.startsolid && !trace.allsolid) - { - VectorCopy (trace.endpos, pmove->origin); - } - -// slide move the rest of the way. - clip = PM_FlyMove (); - -// Now try going back down from the end point -// press down the stepheight - VectorCopy (pmove->origin, dest); - //dest[2] -= pmove->movevars->stepsize; - dest[2] -= NS_GetStepsize(pmove->iuser3); - - trace = NS_PlayerTrace (pmove, pmove->origin, dest, PM_NORMAL, -1 ); - - // If we are not on the ground any more then - // use the original movement attempt - if ( trace.plane.normal[2] < 0.7) - goto usedown; - // If the trace ended up in empty space, copy the end - // over to the origin. - if (!trace.startsolid && !trace.allsolid) - { - VectorCopy (trace.endpos, pmove->origin); - } - // Copy this origion to up. - VectorCopy (pmove->origin, pmove->up); - - // decide which one went farther - downdist = (down[0] - original[0])*(down[0] - original[0]) - + (down[1] - original[1])*(down[1] - original[1]); - updist = (pmove->up[0] - original[0])*(pmove->up[0] - original[0]) - + (pmove->up[1] - original[1])*(pmove->up[1] - original[1]); - - if (downdist > updist) - { -usedown: - VectorCopy (down , pmove->origin); - VectorCopy (downvel, pmove->velocity); - } else // copy z value from slide move - pmove->velocity[2] = downvel[2]; -} - -/* -================== -PM_Friction - -Handles both ground friction and water friction -================== -*/ -void PM_Friction (void) -{ - float *vel; - float speed, newspeed, control; - float friction; - float drop; - vec3_t newvel; - - // If we are in water jump cycle, don't apply friction - // tankefugl: 0000972 - if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) - // :tankefugl - return; - - if (PM_GetIsBlinking()) - return; - - // Get velocity - vel = pmove->velocity; - - // Calculate speed - speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]); - - // If too slow, return - if (speed < 0.1f) - { - return; - } - - drop = 0; - -// apply ground friction - if ((pmove->onground != -1) || (GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING))) // On an entity that is the ground - { - vec3_t start, stop; - pmtrace_t trace; - - start[0] = stop[0] = pmove->origin[0] + vel[0]/speed*16; - start[1] = stop[1] = pmove->origin[1] + vel[1]/speed*16; - start[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2]; - stop[2] = start[2] - 34; - - trace = NS_PlayerTrace (pmove, start, stop, PM_NORMAL, -1 ); - - if (trace.fraction == 1.0) - friction = pmove->movevars->friction*pmove->movevars->edgefriction; - else - friction = pmove->movevars->friction; - - // Grab friction value. - //friction = pmove->movevars->friction; - - friction *= pmove->friction; // player friction? - - // Bleed off some speed, but if we have less than the bleed - // threshhold, bleed the theshold amount. - control = (speed < pmove->movevars->stopspeed) ? - pmove->movevars->stopspeed : speed; - // Add the amount to t'he drop amount. - drop += control*friction*pmove->frametime; - } - -// apply water friction -// if (pmove->waterlevel) -// drop += speed * pmove->movevars->waterfriction * waterlevel * pmove->frametime; - -// scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - - // Determine proportion of old speed we are using. - newspeed /= speed; - - // Adjust velocity according to proportion. - newvel[0] = vel[0] * newspeed; - newvel[1] = vel[1] * newspeed; - newvel[2] = vel[2] * newspeed; - - VectorCopy( newvel, pmove->velocity ); -} - -void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel) -{ - int i; - float addspeed, accelspeed, currentspeed, wishspd = wishspeed; - - if (pmove->dead) - return; - // tankefugl: 0000972 - if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) - // :tankefugl - return; - if(GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - return; - - // Cap speed - //wishspd = VectorNormalize (pmove->wishveloc); - - if (wishspd > 30) - wishspd = 30; - // Determine veer amount - currentspeed = DotProduct (pmove->velocity, wishdir); - // See how much to add - addspeed = wishspd - currentspeed; - // If not adding any, done. - if (addspeed <= 0) - return; - // Determine acceleration speed after acceleration - - accelspeed = accel * wishspeed * pmove->frametime * pmove->friction; - // Cap it - if (accelspeed > addspeed) - accelspeed = addspeed; - - // Adjust pmove vel. - for (i=0 ; i<3 ; i++) - { - pmove->velocity[i] += accelspeed*wishdir[i]; - } -} - -/* -=================== -PM_WaterMove - -=================== -*/ -void PM_WaterMove (void) -{ - int i; - vec3_t wishvel; - float wishspeed; - vec3_t wishdir; - vec3_t start, dest; - vec3_t temp; - pmtrace_t trace; - - float speed, newspeed, addspeed, accelspeed; - -// -// user intentions -// - for (i=0 ; i<3 ; i++) - wishvel[i] = pmove->forward[i]*pmove->cmd.forwardmove + pmove->right[i]*pmove->cmd.sidemove; - - // Sinking after no other movement occurs - if (!pmove->cmd.forwardmove && !pmove->cmd.sidemove && !pmove->cmd.upmove) - wishvel[2] -= 60; // drift towards bottom - else // Go straight up by upmove amount. - wishvel[2] += pmove->cmd.upmove; - - // Copy it over and determine speed - VectorCopy (wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // Cap speed. - if (wishspeed > pmove->maxspeed) - { - VectorScale (wishvel, pmove->maxspeed/wishspeed, wishvel); - wishspeed = pmove->maxspeed; - } - // Slow us down a bit. - wishspeed *= 0.8; - - VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity); -// Water friction - VectorCopy(pmove->velocity, temp); - speed = VectorNormalize(temp); - if (speed) - { - newspeed = speed - pmove->frametime * speed * pmove->movevars->friction * pmove->friction; - - if (newspeed < 0) - newspeed = 0; - VectorScale (pmove->velocity, newspeed/speed, pmove->velocity); - } - else - newspeed = 0; - -// -// water acceleration -// - if ( wishspeed < 0.1f ) - { - return; - } - - addspeed = wishspeed - newspeed; - if (addspeed > 0) - { - - VectorNormalize(wishvel); - accelspeed = pmove->movevars->accelerate * wishspeed * pmove->frametime * pmove->friction; - if (accelspeed > addspeed) - accelspeed = addspeed; - - for (i = 0; i < 3; i++) - pmove->velocity[i] += accelspeed * wishvel[i]; - } - -// Now move -// assume it is a stair or a slope, so press down from stepheight above - VectorMA (pmove->origin, pmove->frametime, pmove->velocity, dest); - VectorCopy (dest, start); - - //start[2] += pmove->movevars->stepsize + 1; - start[2] += NS_GetStepsize(pmove->iuser3) + 1; - - trace = NS_PlayerTrace (pmove, start, dest, PM_NORMAL, -1 ); - - if (!trace.startsolid && !trace.allsolid) // FIXME: check steep slope? - { // walked up the step, so just keep result and exit - VectorCopy (trace.endpos, pmove->origin); - return; - } - - // Try moving straight along out normal path. - PM_FlyMove (); -} - - -/* -=================== -PM_AirMove - -=================== -*/ -void PM_AirMove (void) -{ - int i; - vec3_t wishvel; - float fmove, smove; - vec3_t wishdir; - float wishspeed; - - // Copy movement amounts - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - - // Zero out z components of movement vectors - pmove->forward[2] = 0; - pmove->right[2] = 0; - // Renormalize - VectorNormalize (pmove->forward); - VectorNormalize (pmove->right); - - // Determine x and y parts of velocity - for (i=0 ; i<2 ; i++) - { - wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; - } - // Zero out z part of velocity - wishvel[2] = 0; - - // Determine maginitude of speed of move - VectorCopy (wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // Clamp to server defined max speed - if (wishspeed > pmove->maxspeed) - { - VectorScale (wishvel, pmove->maxspeed/wishspeed, wishvel); - wishspeed = pmove->maxspeed; - } - - PM_PreventMegaBunnyJumping(true); - - float theAirAccelerate = gIsJetpacking[pmove->player_index] ? pmove->movevars->airaccelerate*4 : pmove->movevars->airaccelerate; - if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) - { - theAirAccelerate = pmove->movevars->airaccelerate/22.0f; - } - - PM_AirAccelerate (wishdir, wishspeed, theAirAccelerate); - - // Add in any base velocity to the current velocity. - VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity ); - - //pmove->Con_Printf("airmove wish vel: %f %f %f\n", wishvel[0], wishvel[1], wishvel[2]); - //pmove->Con_Printf("airmove vel: %f %f %f\n", pmove->velocity[0], pmove->velocity[1], pmove->velocity[2]); - - PM_FlyMove (); -} - -qboolean PM_InWater( void ) -{ - return ( pmove->waterlevel > 1 ); -} - -/* -============= -PM_CheckWater - -Sets pmove->waterlevel and pmove->watertype values. -============= -*/ -qboolean PM_CheckWater () -{ - vec3_t point; - int cont; - int truecont; - float height; - float heightover2; - - // Pick a spot just above the players feet. - point[0] = pmove->origin[0] + (pmove->player_mins[pmove->usehull][0] + pmove->player_maxs[pmove->usehull][0]) * 0.5; - point[1] = pmove->origin[1] + (pmove->player_mins[pmove->usehull][1] + pmove->player_maxs[pmove->usehull][1]) * 0.5; - point[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2] + 1; - - // Assume that we are not in water at all. - pmove->waterlevel = 0; - pmove->watertype = CONTENTS_EMPTY; - - // Not sure why this is happening, but the commander on hera, above the droppad is returning CONTENTS_WATER - // The commander can never be under water - if(!GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - { - // Grab point contents. - cont = pmove->PM_PointContents (point, &truecont ); - // Are we under water? (not solid and not empty?) - if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT ) - { - // Set water type - pmove->watertype = cont; - - // We are at least at level one - pmove->waterlevel = 1; - - height = (pmove->player_mins[pmove->usehull][2] + pmove->player_maxs[pmove->usehull][2]); - heightover2 = height * 0.5; - - // Now check a point that is at the player hull midpoint. - point[2] = pmove->origin[2] + heightover2; - cont = pmove->PM_PointContents (point, NULL ); - // If that point is also under water... - if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT ) - { - // Set a higher water level. - pmove->waterlevel = 2; - - // Now check the eye position. (view_ofs is relative to the origin) - point[2] = pmove->origin[2] + pmove->view_ofs[2]; - - cont = pmove->PM_PointContents (point, NULL ); - if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT ) - pmove->waterlevel = 3; // In over our eyes - } - - // Adjust velocity based on water current, if any. - if ( ( truecont <= CONTENTS_CURRENT_0 ) && - ( truecont >= CONTENTS_CURRENT_DOWN ) ) - { - // The deeper we are, the stronger the current. - static vec3_t current_table[] = - { - {1, 0, 0}, {0, 1, 0}, {-1, 0, 0}, - {0, -1, 0}, {0, 0, 1}, {0, 0, -1} - }; - - VectorMA (pmove->basevelocity, 50.0*pmove->waterlevel, current_table[CONTENTS_CURRENT_0 - truecont], pmove->basevelocity); - } - } - } - - return pmove->waterlevel > 1; -} - -/* -============= -PM_CategorizePosition -============= -*/ -void PM_CategorizePosition (void) -{ - vec3_t point; - pmtrace_t tr; - -// if the player hull point one unit down is solid, the player -// is on ground - -// see if standing on something solid - - // Doing this before we move may introduce a potential latency in water detection, but - // doing it after can get us stuck on the bottom in water if the amount we move up - // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call - // this several times per frame, so we really need to avoid sticking to the bottom of - // water on each call, and the converse case will correct itself if called twice. - PM_CheckWater(); - - point[0] = pmove->origin[0]; - point[1] = pmove->origin[1]; - point[2] = pmove->origin[2] - 2; - // puzl: 1027 - // Correctly detect that we are climbing - if (pmove->velocity[2] > (MAX_CLIMB_SPEED-10) ) // Shooting up really fast. Definitely not on ground. - { - pmove->onground = -1; - } - else - { - // Try and move down. - tr = NS_PlayerTrace (pmove, pmove->origin, point, PM_NORMAL, -1 ); - -// // If we hit a steep plane, we are not on ground -// if ( tr.plane.normal[2] < 0.7) -// pmove->onground = -1; // too steep -// else -// pmove->onground = tr.ent; // Otherwise, point to index of ent under us. - - // Change from Mr. Blonde, to fix weird problems when standing on buildings or resource nodes - if(tr.ent > 0 && !pmove->physents[tr.ent].model) - { - pmove->onground = tr.ent; // Standing on a point entity - } - else if ( tr.plane.normal[2] < 0.7 ) - { - pmove->onground = -1; // too steep - } - else - { - pmove->onground = tr.ent; // Otherwise, point to index of ent under us. - } - - // If we are on something... - if (pmove->onground != -1) - { - // Then we are not in water jump sequence - pmove->waterjumptime = 0; - // If we could make the move, drop us down that 1 pixel - if (pmove->waterlevel < 2 && !tr.startsolid && !tr.allsolid) - VectorCopy (tr.endpos, pmove->origin); - } - - // Standing on an entity other than the world - if (tr.ent > 0) // So signal that we are touching something. - { - PM_AddToTouched(tr, pmove->velocity); - } - } -} - -/* -================= -PM_GetRandomStuckOffsets - -When a player is stuck, it's costly to try and unstick them -Grab a test offset for the player based on a passed in index -================= -*/ -int PM_GetRandomStuckOffsets(int nIndex, int server, vec3_t offset) -{ - // Last time we did a full - int idx; - idx = rgStuckLast[nIndex][server]++; - - VectorCopy(rgv3tStuckTable[idx % 54], offset); - - return (idx % 54); -} - -void PM_ResetStuckOffsets(int nIndex, int server) -{ - rgStuckLast[nIndex][server] = 0; -} - -/* -================= -NudgePosition - -If pmove->origin is in a solid position, -try nudging slightly on all axis to -allow for the cut precision of the net coordinates -================= -*/ -#define PM_CHECKSTUCK_MINTIME 0.05 // Don't check again too quickly. - -int PM_CheckStuck (void) -{ - // Can't be stuck when you're a commander, allows commander to fly around outside the world. Do the same for observers/spectators? - if(GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - { - return 0; - } - - vec3_t base; - vec3_t offset; - vec3_t test; - int hitent; - int idx; - float fTime; - int i; - pmtrace_t traceresult; - - - -// int theEntNumber = 1; -// -// vec3_t theMins; -// VectorCopy(pmove->physents[theEntNumber].mins, theMins); -// -// vec3_t theMaxs; -// VectorCopy(pmove->physents[theEntNumber].maxs, theMaxs); -// -// //char* theName = pmove->physents[hitent].model -// //int theModType = pmove->PM_GetModelType( pmove->physents[hitent].model ); -// -// int theSolidity = pmove->physents[theEntNumber].solid; -// -// vec3_t theAngles; -// VectorCopy(pmove->physents[theEntNumber].angles, theAngles); -// -// vec3_t theOrigin; -// VectorCopy(pmove->physents[theEntNumber].origin, theOrigin); -// -// if(pmove->server) -// { -// pmove->Con_DPrintf("Server: ent: %d, solidity: %d mins(%f, %f, %f), maxs(%f, %f, %f), origin(%f, %f, %f), angles(%f, %f, %f)\n", theEntNumber, theSolidity, theMins[0], theMins[1], theMins[2], theMaxs[0], theMaxs[1], theMaxs[2], theOrigin[0], theOrigin[1], theOrigin[2], theAngles[0], theAngles[1], theAngles[2]); -// } -// else -// { -// pmove->Con_DPrintf("Client: ent: %d, solidity: %d mins(%f, %f, %f), maxs(%f, %f, %f), origin(%f, %f, %f), angles(%f, %f, %f)\n", theEntNumber, theSolidity, theMins[0], theMins[1], theMins[2], theMaxs[0], theMaxs[1], theMaxs[2], theOrigin[0], theOrigin[1], theOrigin[2], theAngles[0], theAngles[1], theAngles[2]); -// } - - - - - - static float rgStuckCheckTime[MAX_CLIENTS][2]; // Last time we did a full - - // If position is okay, exit - //hitent = pmove->PM_TestPlayerPosition (pmove->origin, &traceresult ); - hitent = NS_TestPlayerPosition(pmove, pmove->origin, &traceresult); - - if (hitent == -1 ) - { - PM_ResetStuckOffsets( pmove->player_index, pmove->server ); - return 0; - } - - VectorCopy (pmove->origin, base); - - // - // Deal with precision error in network. - // - if (!pmove->server) - { - // World or BSP model - if ( ( hitent == 0 ) || - ( pmove->physents[hitent].model != NULL ) ) - { - int nReps = 0; - PM_ResetStuckOffsets( pmove->player_index, pmove->server ); - do - { - i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); - - VectorAdd(base, offset, test); - //if (pmove->PM_TestPlayerPosition (test, &traceresult ) == -1) - if (NS_TestPlayerPosition(pmove, test, &traceresult ) == -1) - { - PM_ResetStuckOffsets( pmove->player_index, pmove->server ); - - VectorCopy ( test, pmove->origin ); - return 0; - } - nReps++; - } while (nReps < 54); - } - } - - // Only an issue on the client. - - if (pmove->server) - idx = 0; - else - idx = 1; - - fTime = pmove->Sys_FloatTime(); - // Too soon? - if (rgStuckCheckTime[pmove->player_index][idx] >= - ( fTime - PM_CHECKSTUCK_MINTIME ) ) - { - return 1; - } - rgStuckCheckTime[pmove->player_index][idx] = fTime; - - pmove->PM_StuckTouch( hitent, &traceresult ); - - i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); - - VectorAdd(base, offset, test); - - if ( ( hitent = NS_TestPlayerPosition (pmove, test, NULL ) ) == -1 ) - //if ( ( hitent = pmove->PM_TestPlayerPosition ( test, NULL ) ) == -1 ) - { - //pmove->Con_DPrintf("Nudged\n"); - - PM_ResetStuckOffsets( pmove->player_index, pmove->server ); - - if (i >= 27) - VectorCopy ( test, pmove->origin ); - - return 0; - } - - // If player is flailing while stuck in another player ( should never happen ), then see - // if we can't "unstick" them forceably. - if ( pmove->cmd.buttons & ( IN_JUMP | IN_DUCK | IN_ATTACK ) && ( pmove->physents[ hitent ].player != 0 ) ) - { - float x, y, z; - float xystep = 8.0; - float zstep = 18.0; - float xyminmax = xystep; - float zminmax = 4 * zstep; - - for ( z = 0; z <= zminmax; z += zstep ) - { - for ( x = -xyminmax; x <= xyminmax; x += xystep ) - { - for ( y = -xyminmax; y <= xyminmax; y += xystep ) - { - VectorCopy( base, test ); - test[0] += x; - test[1] += y; - test[2] += z; - - //if ( pmove->PM_TestPlayerPosition ( test, NULL ) == -1 ) - if (NS_TestPlayerPosition (pmove, test, NULL ) == -1 ) - { - VectorCopy( test, pmove->origin ); - return 0; - } - } - } - } - } - - //VectorCopy (base, pmove->origin); - - return 1; -} - -/* -=============== -PM_SpectatorMove -=============== -*/ -void PM_SpectatorMove (void) -{ - float speed, drop, friction, control, newspeed; - //float accel; - float currentspeed, addspeed, accelspeed; - int i; - vec3_t wishvel; - float fmove, smove; - vec3_t wishdir; - float wishspeed; - // this routine keeps track of the spectators psoition - // there a two different main move types : track player or moce freely (OBS_ROAMING) - // doesn't need excate track position, only to generate PVS, so just copy - // targets position and real view position is calculated on client (saves server CPU) - - if ( pmove->iuser1 == OBS_ROAMING) - { - -#ifdef AVH_CLIENT - // jump only in roaming mode - if ( iJumpSpectator ) - { - VectorCopy( vJumpOrigin, pmove->origin ); - VectorCopy( vJumpAngles, pmove->angles ); - VectorCopy( vec3_origin, pmove->velocity ); - iJumpSpectator = 0; - return; - } - #endif - // Move around in normal spectator method - - speed = Length (pmove->velocity); - if (speed < 1) - { - VectorCopy (vec3_origin, pmove->velocity) - } - else - { - drop = 0; - - friction = pmove->movevars->friction*1.5; // extra friction - control = speed < pmove->movevars->stopspeed ? pmove->movevars->stopspeed : speed; - drop += control*friction*pmove->frametime; - - // scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - - VectorScale (pmove->velocity, newspeed, pmove->velocity); - } - - // accelerate - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - - VectorNormalize (pmove->forward); - VectorNormalize (pmove->right); - - for (i=0 ; i<3 ; i++) - { - wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; - } - wishvel[2] += pmove->cmd.upmove; - - VectorCopy (wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // - // clamp to server defined max speed - // - if (wishspeed > pmove->movevars->spectatormaxspeed) - { - VectorScale (wishvel, pmove->movevars->spectatormaxspeed/wishspeed, wishvel); - wishspeed = pmove->movevars->spectatormaxspeed; - } - - currentspeed = DotProduct(pmove->velocity, wishdir); - addspeed = wishspeed - currentspeed; - if (addspeed <= 0) - return; - - accelspeed = pmove->movevars->accelerate*pmove->frametime*wishspeed; - if (accelspeed > addspeed) - accelspeed = addspeed; - - for (i=0 ; i<3 ; i++) - pmove->velocity[i] += accelspeed*wishdir[i]; - - // move - VectorMA (pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); - } - else - { - // all other modes just track some kind of target, so spectator PVS = target PVS - - int target; - - // no valid target ? - if ( pmove->iuser2 <= 0) - return; - - // Find the client this player's targeting - for (target = 0; target < pmove->numphysent; target++) - { - if ( pmove->physents[target].info == pmove->iuser2 ) - break; - } - - if (target == pmove->numphysent) - return; - - // use targets position as own origin for PVS - VectorCopy( pmove->physents[target].angles, pmove->angles ); - VectorCopy( pmove->physents[target].origin, pmove->origin ); - - // no velocity - VectorCopy( vec3_origin, pmove->velocity ); - } -} - -/* -================== -PM_SplineFraction - -Use for ease-in, ease-out style interpolation (accel/decel) -Used by ducking code. -================== -*/ -float PM_SplineFraction( float value, float scale ) -{ - float valueSquared; - - value = scale * value; - valueSquared = value * value; - - // Nice little ease-in, ease-out spline-like curve - return 3 * valueSquared - 2 * valueSquared * value; -} - -void PM_FixPlayerCrouchStuck( int direction ) -{ - int hitent; - int i; - vec3_t test; - - hitent = pmove->PM_TestPlayerPosition ( pmove->origin, NULL ); - if (hitent == -1 ) - return; - - VectorCopy( pmove->origin, test ); - for ( i = 0; i < 36; i++ ) - { - pmove->origin[2] += direction; - - hitent = NS_TestPlayerPosition (pmove, pmove->origin, NULL ); - //hitent = pmove->PM_TestPlayerPosition ( pmove->origin, NULL ); - if (hitent == -1 ) - return; - } - - VectorCopy( test, pmove->origin ); // Failed -} - -void PM_UnDuck( void ) -{ - if(AvHMUGetCanDuck(pmove->iuser3)) - { - - pmtrace_t trace; - vec3_t newOrigin; - - VectorCopy( pmove->origin, newOrigin ); - - // tankefugl: remove the jump when pressing and releasing duck quickly - if ( pmove->onground != -1 && pmove->flags & FL_DUCKING && pmove->bInDuck == false) - { - int theStandingHull = AvHMUGetHull(false, pmove->iuser3); - int theCrouchingHull = AvHMUGetHull(true, pmove->iuser3); - - newOrigin[2] += ( pmove->player_mins[theCrouchingHull][2] - pmove->player_mins[theStandingHull][2] ); - } - - trace = NS_PlayerTrace(pmove, newOrigin, newOrigin, PM_NORMAL, -1 ); - - if ( !trace.startsolid ) - { - //pmove->usehull = 0; - pmove->usehull = AvHMUGetHull(false, pmove->iuser3); - - // Oh, no, changing hulls stuck us into something, try unsticking downward first. - - trace = NS_PlayerTrace(pmove, newOrigin, newOrigin, PM_NORMAL, -1 ); - - if ( trace.startsolid ) - { - // Trace again for onos, but move us up first. Trying to get him unstick by moving down will never work for him. - if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5) - { - newOrigin[2] += 30; - } - - trace = NS_PlayerTrace(pmove, newOrigin, newOrigin, PM_NORMAL, -1 ); - - if ( trace.startsolid ) - { - - // See if we are stuck? If so, stay ducked with the duck hull until we have a clear spot - //pmove->Con_Printf( "unstick got stuck\n" ); - - //pmove->usehull = 1; - pmove->usehull = AvHMUGetHull(true, pmove->iuser3); - //pmove->Con_Printf("Using hull: %d\n", pmove->usehull); - return; - } - } - //pmove->Con_Printf("Using hull: %d\n", pmove->usehull); - - pmove->flags &= ~FL_DUCKING; - pmove->bInDuck = false; - //pmove->view_ofs[2] = VEC_VIEW; - float theViewHeight = kStandingViewHeightPercentage*pmove->player_maxs[pmove->usehull][2]; - pmove->view_ofs[2] = theViewHeight; - pmove->flDuckTime = 0; - - VectorCopy( newOrigin, pmove->origin ); - - // Recatagorize position since ducking can change origin - PM_CategorizePosition(); - } - } -} - -void PM_Duck( void ) -{ - if(AvHMUGetCanDuck(pmove->iuser3)) - { - - int buttonsChanged = ( pmove->oldbuttons ^ pmove->cmd.buttons ); // These buttons have changed this frame - int nButtonPressed = buttonsChanged & pmove->cmd.buttons; // The changed ones still down are "pressed" - - int duckchange = buttonsChanged & IN_DUCK ? 1 : 0; - int duckpressed = nButtonPressed & IN_DUCK ? 1 : 0; - - if ( pmove->cmd.buttons & IN_DUCK ) - { - pmove->oldbuttons |= IN_DUCK; - } - else - { - pmove->oldbuttons &= ~IN_DUCK; - } - - // Prevent ducking if the iuser3 variable is set - //if ( pmove->iuser3 || pmove->dead ) - //{ - // // Try to unduck - // if ( pmove->flags & FL_DUCKING ) - // { - // PM_UnDuck(); - // } - // return; - //} - - if((pmove->flags & FL_DUCKING) && (!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING))) - { - pmove->cmd.forwardmove *= 0.333; - pmove->cmd.sidemove *= 0.333; - pmove->cmd.upmove *= 0.333; - } - - if ( ( pmove->cmd.buttons & IN_DUCK ) || ( pmove->bInDuck ) || ( pmove->flags & FL_DUCKING ) ) - { - if ( pmove->cmd.buttons & IN_DUCK ) - { - if ( (nButtonPressed & IN_DUCK ) && !( pmove->flags & FL_DUCKING ) ) - { - // Use 1 second so super long jump will work - pmove->flDuckTime = TIME_TO_DUCK; - pmove->bInDuck = true; - } - - float duckFraction = 1 - ((float)pmove->flDuckTime / TIME_TO_DUCK); - - if ( pmove->bInDuck ) - { - - int theStandingHull = AvHMUGetHull(false, pmove->iuser3); - int theCrouchingHull = AvHMUGetHull(true, pmove->iuser3); - - // Finish ducking immediately if duck time is over or not on ground - if ( (pmove->flDuckTime == 0) || (pmove->onground == -1 ) ) - { - - pmove->usehull = theCrouchingHull; - pmove->view_ofs[2] = kDuckingViewHeightPercentage*pmove->player_maxs[pmove->usehull][2]; - - pmove->flags |= FL_DUCKING; - pmove->bInDuck = false; - - // HACKHACK - Fudge for collision bug - no time to fix this properly - if ( pmove->onground != -1 ) - { - pmove->origin[2] -= ( pmove->player_mins[theCrouchingHull][2] - pmove->player_mins[theStandingHull][2] ); - // See if we are stuck? - PM_FixPlayerCrouchStuck( STUCK_MOVEUP ); - - // Recatagorize position since ducking can change origin - PM_CategorizePosition(); - } - - } - else - { - float theDuckingZ = kDuckingViewHeightPercentage*(pmove->player_maxs[theCrouchingHull][2]) +( pmove->player_mins[theStandingHull][2] - pmove->player_mins[theCrouchingHull][2] ); - float theStandingZ = kStandingViewHeightPercentage*(pmove->player_maxs[theStandingHull][2]); - - // Calc parametric time - float duckSplineFraction = PM_SplineFraction( duckFraction, 1); - pmove->view_ofs[2] = theStandingZ + duckSplineFraction * (theDuckingZ - theStandingZ); - - } - } - } - else - { - // Try to unduck - PM_UnDuck(); - } - } - } -} - -/* -bool NS_PositionFree(float inX, float inY, float inZ) -{ - bool thePositionFree = false; - pmtrace_t traceresult; - vec3_t thePosition; - thePosition[0] = inX; - thePosition[1] = inY; - thePosition[2] = inZ; - int hitent = pmove->PM_TestPlayerPosition(thePosition, &traceresult); - if(hitent == -1) - { - thePositionFree = true; - } - return thePositionFree; -} - -bool NS_PositionFreeForPlayer(vec3_t& inPosition) -{ - bool thePositionFree = false; - - // Test center point - if(NS_PositionFree(inPosition[0], inPosition[1], inPosition[2])) - { - // Test 8 corners of hull - vec3_t theMinHull; - VectorCopy(pmove->player_mins[pmove->usehull], theMinHull); - - vec3_t theMaxHull; - VectorCopy(pmove->player_maxs[pmove->usehull], theMaxHull); - - // Don't test hull points exactly (not exactly sure if PM_TestPlayerPosition() takes into account hull or not) - float theFudgeFactor = .5f; - float theMinX = inPosition[0] + theMinHull[0]*theFudgeFactor; - float theMinY = inPosition[1] + theMinHull[1]*theFudgeFactor; - float theMinZ = inPosition[2] + theMinHull[2]*theFudgeFactor; - - float theMaxX = inPosition[0] + theMaxHull[0]*theFudgeFactor; - float theMaxY = inPosition[1] + theMaxHull[1]*theFudgeFactor; - float theMaxZ = inPosition[2] + theMaxHull[2]*theFudgeFactor; - - if(NS_PositionFree(theMinX, theMinY, theMinZ)) - { - if(NS_PositionFree(theMinX, theMinY, theMaxZ)) - { - if(NS_PositionFree(theMinX, theMaxY, theMinZ)) - { - if(NS_PositionFree(theMinX, theMaxY, theMaxZ)) - { - if(NS_PositionFree(theMaxX, theMinY, theMinZ)) - { - if(NS_PositionFree(theMaxX, theMinY, theMaxZ)) - { - if(NS_PositionFree(theMaxX, theMaxY, theMinZ)) - { - if(NS_PositionFree(theMaxX, theMaxY, theMaxZ)) - { - thePositionFree = true; - } - } - } - } - } - } - } - } - } - - return thePositionFree; -} -*/ - - -void PM_AlienAbilities() -{ - // Give some energy back if we're an alien and not evolving - string theExt; - if(NS_GetIsPlayerAlien(theExt)) - { - float theTimePassed = pmove->cmd.msec*0.001f; - AvHMUUpdateAlienEnergy(theTimePassed, pmove->iuser3, pmove->iuser4, pmove->fuser3); - - // Stop charging when we're out of energy - if(GetHasUpgrade(pmove->iuser4, MASK_ALIEN_MOVEMENT)) - { - if(pmove->fuser3 <= 0.0f) - { - SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, false); - } - } - } - - //#endif - - if (PM_GetIsLeaping() || PM_GetIsBlinking()) - { - float theScalar = 500; - float theEnergyCost = 0; - - if (PM_GetIsBlinking()) - { - theScalar = 225; - AvHMUGetEnergyCost(AVH_WEAPON_BLINK, theEnergyCost); - } - else - { - AvHMUGetEnergyCost(AVH_ABILITY_LEAP, theEnergyCost); - // tankefugl: 0000972 - // Add highjacked "watertime" to release leaping skulk from wall - // pmove->waterjumptime = 75; - // :tankefugl - } - - if(AvHMUHasEnoughAlienEnergy(pmove->fuser3, theEnergyCost)) - { - AvHMUDeductAlienEnergy(pmove->fuser3, theEnergyCost); - } - else - { - //voogru: I'd like to kill them or do something evil here (since this can only happen if they try the exploit), but nah. - return; - } - - vec3_t forward, right, up; - AngleVectors(pmove->angles, forward, right, up); - - PM_Jump(); - - vec3_t theAbilityVelocity; - //VectorScale(pmove->forward, theScalar, theAbilityVelocity); - VectorScale(forward, theScalar, theAbilityVelocity); - - vec3_t theFinalVelocity; - VectorAdd(pmove->velocity, theAbilityVelocity, theFinalVelocity); - - VectorCopy(theFinalVelocity, pmove->velocity); - - //pmove->oldbuttons |= IN_JUMP; // don't jump again until released - - - // if(pmove->runfuncs) - // { - // pmove->PM_PlaySound(CHAN_WEAPON, kLeapSound, 1.0f, ATTN_NORM, 0, 94 + pmove->RandomLong(0, 0xf)); - // } - //pmove->velocity[2] += 300; - //} - } - -// else if((pmove->cmd.impulse == ALIEN_ABILITY_BLINK) && (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4)) -// { -// -// vec3_t theBlinkStart; -// VectorCopy(pmove->origin, theBlinkStart); -// theBlinkStart[2] += pmove->view_ofs[2]; -// -// vec3_t theBlinkDirection; -// VectorCopy(pmove->forward, theBlinkDirection); -// -// vec3_t theBlinkEnd; -// VectorMA(pmove->origin, kMaxMapDimension, theBlinkDirection, theBlinkEnd); -// -// // Do traceline through glass, until we hit something (this uses the current player hull, so it's like it's actually flying forward) -// int theTraceFlags = PM_TRACELINE_ANYVISIBLE; -// int theHull = -1;//pmove->usehull; -// int theIgnoreEntity = -1; -// pmtrace_t* theTrace = pmove->PM_TraceLine(theBlinkStart, theBlinkEnd, theTraceFlags, theHull, theIgnoreEntity); -// ASSERT(theTrace); -// -// // While position isn't free, bring distance back a little -// vec3_t theFreeEndPoint; -// VectorCopy(theTrace->endpos, theFreeEndPoint); -// -// // Subtract view height again -// theFreeEndPoint[2] -= pmove->view_ofs[2]; -// -// int theNumIterations = 0; -// bool theSuccess = false; -// while(!theSuccess && (theNumIterations < 5)) -// { -// if(NS_PositionFreeForPlayer(theFreeEndPoint)) -// { -// // If position is still in front of us -// vec3_t theFreeEndPointDirection; -// VectorSubtract(theFreeEndPoint, theBlinkStart, theFreeEndPointDirection); -// if(DotProduct(theBlinkDirection, theFreeEndPointDirection) > 0) -// { -// // Save position so client can use it to draw -// theSuccess = true; -// } -// } -// else -// { -// vec3_t theBackwardsIncrement; -// VectorMA(vec3_origin, -50, theBlinkDirection, theBackwardsIncrement); -// -// VectorAdd(theFreeEndPoint, theBackwardsIncrement, theFreeEndPoint); -// } -// -// theNumIterations++; -// } -// -// // If position is okay, exit -// if(theSuccess) -// { -// // If so, set our new location to it -// VectorCopy(theFreeEndPoint, pmove->origin); -// -// if(pmove->runfuncs) -// { -// pmove->PM_PlaybackEventFull(0, pmove->player_index, gBlinkEffectSuccessEventID, 0, (float *)theBlinkStart, (float *)theFreeEndPoint, 0.0, 0.0, 0, 0, 0, 0 ); -// } -// } -// //else -// //{ -// // // Play a "blink failed" event -// // pmove->PM_PlaybackEventFull(0, pmove->player_index, gBlinkEffectFailEventID, 0, (float *)pmove->origin, (float *)pmove->origin, 0.0, 0.0, 0, 0, 0, 0 ); -// //} -// -// } - -} - -void PM_LadderMove( physent_t *pLadder ) -{ - vec3_t ladderCenter; - trace_t trace; - qboolean onFloor; - vec3_t floor; - vec3_t modelmins, modelmaxs; - - if ( pmove->movetype == MOVETYPE_NOCLIP ) - return; - - pmove->PM_GetModelBounds( pLadder->model, modelmins, modelmaxs ); - - VectorAdd( modelmins, modelmaxs, ladderCenter ); - VectorScale( ladderCenter, 0.5, ladderCenter ); - - pmove->movetype = MOVETYPE_FLY; - - // On ladder, convert movement to be relative to the ladder - - VectorCopy( pmove->origin, floor ); - floor[2] += pmove->player_mins[pmove->usehull][2] - 1; - - if ( pmove->PM_PointContents( floor, NULL ) == CONTENTS_SOLID ) - onFloor = true; - else - onFloor = false; - - pmove->gravity = 0; - pmove->PM_TraceModel( pLadder, pmove->origin, ladderCenter, &trace ); - if ( trace.fraction != 1.0 ) - { - float forward = 0, right = 0; - vec3_t vpn, v_right; - - AngleVectors( pmove->angles, vpn, v_right, NULL ); - if ( pmove->cmd.buttons & IN_BACK ) - forward -= MAX_CLIMB_SPEED; - if ( pmove->cmd.buttons & IN_FORWARD ) - forward += MAX_CLIMB_SPEED; - if ( pmove->cmd.buttons & IN_MOVELEFT ) - right -= MAX_CLIMB_SPEED; - if ( pmove->cmd.buttons & IN_MOVERIGHT ) - right += MAX_CLIMB_SPEED; - - if ( PM_GetIsBlinking() ) - { - pmove->movetype = MOVETYPE_WALK; - VectorScale( trace.plane.normal, 270, pmove->velocity ); - } - else if ( pmove->cmd.buttons & IN_JUMP ) - { - pmove->movetype = MOVETYPE_WALK; - VectorScale( trace.plane.normal, 270, pmove->velocity ); - } - else - { - if ( forward != 0 || right != 0 ) - { - vec3_t velocity, perp, cross, lateral, tmp; - float normal; - - //ALERT(at_console, "pev %.2f %.2f %.2f - ", - // pev->velocity.x, pev->velocity.y, pev->velocity.z); - // Calculate player's intended velocity - //Vector velocity = (forward * gpGlobals->v_forward) + (right * gpGlobals->v_right); - VectorScale( vpn, forward, velocity ); - VectorMA( velocity, right, v_right, velocity ); - - - // Perpendicular in the ladder plane - // Vector perp = CrossProduct( Vector(0,0,1), trace.vecPlaneNormal ); - // perp = perp.Normalize(); - VectorClear( tmp ); - tmp[2] = 1; - CrossProduct( tmp, trace.plane.normal, perp ); - VectorNormalize( perp ); - - - // decompose velocity into ladder plane - normal = DotProduct( velocity, trace.plane.normal ); - // This is the velocity into the face of the ladder - VectorScale( trace.plane.normal, normal, cross ); - - - // This is the player's additional velocity - VectorSubtract( velocity, cross, lateral ); - - // This turns the velocity into the face of the ladder into velocity that - // is roughly vertically perpendicular to the face of the ladder. - // NOTE: It IS possible to face up and move down or face down and move up - // because the velocity is a sum of the directional velocity and the converted - // velocity through the face of the ladder -- by design. - CrossProduct( trace.plane.normal, perp, tmp ); - VectorMA( lateral, -normal, tmp, pmove->velocity ); - if ( onFloor && normal > 0 ) // On ground moving away from the ladder - { - VectorMA( pmove->velocity, MAX_CLIMB_SPEED, trace.plane.normal, pmove->velocity ); - } - //pev->velocity = lateral - (CrossProduct( trace.vecPlaneNormal, perp ) * normal); - } - else - { - VectorClear( pmove->velocity ); - } - } - } -} - -physent_t *PM_Ladder( void ) -{ - int i; - physent_t *pe; - hull_t *hull; - int num; - vec3_t test; - - for ( i = 0; i < pmove->nummoveent; i++ ) - { - pe = &pmove->moveents[i]; - - if ( pe->model && (modtype_t)pmove->PM_GetModelType( pe->model ) == mod_brush && pe->skin == CONTENTS_LADDER ) - { - - hull = (hull_t *)pmove->PM_HullForBsp( pe, test ); - num = hull->firstclipnode; - - // Offset the test point appropriately for this hull. - VectorSubtract ( pmove->origin, test, test); - - // Test the player's hull for intersection with this model - if ( pmove->PM_HullPointContents (hull, num, test) == CONTENTS_EMPTY) - continue; - - return pe; - } - } - - return NULL; -} - - - -void PM_WaterJump (void) -{ - // tankefugl: 0000972 - if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1) - return; - // :tankefugl - - if ( pmove->waterjumptime > 10000 ) - { - pmove->waterjumptime = 10000; - } - - if ( !pmove->waterjumptime ) - return; - - pmove->waterjumptime -= pmove->cmd.msec; - if ( pmove->waterjumptime < 0 || - !pmove->waterlevel ) - { - pmove->waterjumptime = 0; - pmove->flags &= ~FL_WATERJUMP; - } - - pmove->velocity[0] = pmove->movedir[0]; - pmove->velocity[1] = pmove->movedir[1]; -} - -/* -============ -PM_AddGravity - -============ -*/ -void PM_AddGravity () -{ - float ent_gravity; - - if (pmove->gravity) - ent_gravity = pmove->gravity; - else - ent_gravity = 1.0; - - // Add gravity incorrectly - pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * pmove->frametime ); - pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; - pmove->basevelocity[2] = 0; - PM_CheckVelocity(); -} -/* -============ -PM_PushEntity - -Does not change the entities velocity at all -============ -*/ -pmtrace_t PM_PushEntity (vec3_t push) -{ - pmtrace_t trace; - vec3_t end; - - VectorAdd (pmove->origin, push, end); - - trace = NS_PlayerTrace (pmove, pmove->origin, end, PM_NORMAL, -1 ); - - VectorCopy (trace.endpos, pmove->origin); - - // So we can run impact function afterwards. - if (trace.fraction < 1.0 && - !trace.allsolid) - { - PM_AddToTouched(trace, pmove->velocity); - } - - return trace; -} - -/* -============ -PM_Physics_Toss() - -Dead player flying through air., e.g. -============ -*/ -void PM_Physics_Toss() -{ - pmtrace_t trace; - vec3_t move; - float backoff; - - PM_CheckWater(); - - if (pmove->velocity[2] > 0) - pmove->onground = -1; - - // If on ground and not moving, return. - if ( pmove->onground != -1 ) - { - if (VectorCompare(pmove->basevelocity, vec3_origin) && - VectorCompare(pmove->velocity, vec3_origin)) - return; - } - - PM_CheckVelocity (); - -// add gravity - if ( pmove->movetype != MOVETYPE_FLY && - pmove->movetype != MOVETYPE_BOUNCEMISSILE && - pmove->movetype != MOVETYPE_FLYMISSILE ) - PM_AddGravity (); - -// move origin - // Base velocity is not properly accounted for since this entity will move again after the bounce without - // taking it into account - VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity); - - PM_CheckVelocity(); - VectorScale (pmove->velocity, pmove->frametime, move); - VectorSubtract (pmove->velocity, pmove->basevelocity, pmove->velocity); - - trace = PM_PushEntity (move); // Should this clear basevelocity - - PM_CheckVelocity(); - - if (trace.allsolid) - { - // entity is trapped in another solid - pmove->onground = trace.ent; - VectorCopy (vec3_origin, pmove->velocity); - return; - } - - if (trace.fraction == 1) - { - PM_CheckWater(); - return; - } - - - if (pmove->movetype == MOVETYPE_BOUNCE) - backoff = 2.0 - pmove->friction; - else if (pmove->movetype == MOVETYPE_BOUNCEMISSILE) - backoff = 2.0; - else - backoff = 1; - - PM_ClipVelocity (pmove->velocity, trace.plane.normal, pmove->velocity, backoff); - - // stop if on ground - if (trace.plane.normal[2] > 0.7) - { - float vel; - vec3_t base; - - VectorClear( base ); - if (pmove->velocity[2] < pmove->movevars->gravity * pmove->frametime) - { - // we're rolling on the ground, add static friction. - pmove->onground = trace.ent; - pmove->velocity[2] = 0; - } - - vel = DotProduct( pmove->velocity, pmove->velocity ); - - // Con_DPrintf("%f %f: %.0f %.0f %.0f\n", vel, trace.fraction, ent->velocity[0], ent->velocity[1], ent->velocity[2] ); - - if (vel < (30 * 30) || (pmove->movetype != MOVETYPE_BOUNCE && pmove->movetype != MOVETYPE_BOUNCEMISSILE)) - { - pmove->onground = trace.ent; - VectorCopy (vec3_origin, pmove->velocity); - } - else - { - VectorScale (pmove->velocity, (1.0 - trace.fraction) * pmove->frametime * 0.9, move); - trace = PM_PushEntity (move); - } - VectorSubtract( pmove->velocity, base, pmove->velocity ) - } - -// check for in water - PM_CheckWater(); -} - -/* -==================== -PM_NoClip - -==================== -*/ -void PM_NoClip() -{ - int i; - vec3_t wishvel; - float fmove, smove; -// float currentspeed, addspeed, accelspeed; - - // Copy movement amounts - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - - VectorNormalize ( pmove->forward ); - VectorNormalize ( pmove->right ); - - for (i=0 ; i<3 ; i++) // Determine x and y parts of velocity - { - wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; - } - wishvel[2] += pmove->cmd.upmove; - - VectorMA (pmove->origin, pmove->frametime, wishvel, pmove->origin); - - // Zero out the velocity so that we don't accumulate a huge downward velocity from - // gravity, etc. - VectorClear( pmove->velocity ); - -} - -void PM_PlaybackEvent(int inEventID) -{ - int theFlags = 0; - - //#if defined( AVH_SERVER ) - theFlags = FEV_NOTHOST; - //#endif - - vec3_t theZeroVector; - theZeroVector[0] = theZeroVector[1] = theZeroVector[2] = 0.0f; - - // Lame way to get the local player index that playback event full needs...there must be another way? - //vec3_t theTraceEnd; - //VectorMA(pmove->origin, 100, pmove->forward, theTraceEnd); - //pmtrace_t theTrace = pmove->PM_PlayerTrace(pmove->origin, theTraceEnd, PM_TRACELINE_PHYSENTSONLY, -1); - //theTrace = pmove->PM_TraceLine(pmove->origin, theTraceEnd, PM_TRACELINE_PHYSENTSONLY, 2, -1); - - int thisPredictedPlayer = pmove->player_index;/*theTrace.ent;*/ - pmove->PM_PlaybackEventFull(theFlags, thisPredictedPlayer, inEventID, 0, (float *)theZeroVector, (float *)theZeroVector, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); -} - -// Only allow bunny jumping up to 1.1x server / player maxspeed setting -#define BUNNYJUMP_MAX_SPEED_FACTOR 1.7f - -//----------------------------------------------------------------------------- -// Purpose: Corrects bunny jumping ( where player initiates a bunny jump before other -// movement logic runs, thus making onground == -1 thus making PM_Friction get skipped and -// running PM_AirMove, which doesn't crop velocity to maxspeed like the ground / other -// movement logic does. -//----------------------------------------------------------------------------- -void PM_PreventMegaBunnyJumping(bool inAir) -{ - // Current player speed - float spd; - // If we have to crop, apply this cropping fraction to velocity - float fraction; - // Speed at which bunny jumping is limited - float maxscaledspeed; - - maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * pmove->maxspeed; - if(inAir) - { - // Allow flyers, leapers, and JPers to go faster in the air, but still capped - maxscaledspeed = BALANCE_VAR(kAirspeedMultiplier)*pmove->maxspeed; - } - - // Don't divide by zero - if ( maxscaledspeed <= 0.0f ) - return; - - vec3_t theVelVector; - VectorCopy(pmove->velocity, theVelVector); - - if(inAir) - { - theVelVector[2] = 0.0f; - } - - spd = Length( theVelVector ); - - if ( spd <= maxscaledspeed ) - return; - - // Returns the modifier for the velocity - fraction = maxscaledspeed/spd; - - float theCurrentSpeed = Length(pmove->velocity); - - VectorScale( pmove->velocity, fraction, pmove->velocity ); //Crop it down!. - -//#ifdef AVH_CLIENT -// if(pmove->runfuncs) -// { -// pmove->Con_Printf("Preventing speed exploits (max speed: %f, current speed: %f, adjusted speed: %f\n", pmove->maxspeed, theCurrentSpeed, Length(pmove->velocity)); -// } -//#endif - - // Trigger footstep immediately to prevent silent bunny-hopping - //pmove->flTimeStepSound = 0.0f; -} - -/* -============= -PM_Jump -============= -*/ -void PM_Jump (void) -{ - int i; - qboolean tfc = false; - - qboolean cansuperjump = false; - - if (pmove->dead || GetHasUpgrade(pmove->iuser4, MASK_ENSNARED)) - { - pmove->oldbuttons |= IN_JUMP ; // don't jump again until released - return; - } - - tfc = atoi( pmove->PM_Info_ValueForKey( pmove->physinfo, "tfc" ) ) == 1 ? true : false; - - // Spy that's feigning death cannot jump - if ( tfc && - ( pmove->deadflag == ( DEAD_DISCARDBODY + 1 ) ) ) - { - return; - } - - // See if we are waterjumping. If so, decrement count and return. - // tankefugl: 0000972 - if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) - // :tankefugl -// if ( pmove->waterjumptime ) - { - pmove->waterjumptime -= pmove->cmd.msec; - if (pmove->waterjumptime < 0) - { - pmove->waterjumptime = 0; - } - return; - } - - // If we are in the water most of the way... - if (pmove->waterlevel >= 2) - { // swimming, not jumping - pmove->onground = -1; - - if (pmove->watertype == CONTENTS_WATER) // We move up a certain amount - pmove->velocity[2] = 100; - else if (pmove->watertype == CONTENTS_SLIME) - pmove->velocity[2] = 80; - else // LAVA - pmove->velocity[2] = 50; - - // play swiming sound - if ( pmove->flSwimTime <= 0 ) - { - // If alien has silencio upgrade, mute footstep volume - int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); - float theVolumeScalar = 1.0f - theSilenceUpgradeLevel/3.0f; - - // Don't play sound again for 1 second - pmove->flSwimTime = 1000; - switch ( pmove->RandomLong( 0, 3 ) ) - { - case 0: - pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade1.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); - break; - case 1: - pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade2.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); - break; - case 2: - pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade3.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); - break; - case 3: - pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade4.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); - break; - } - } - - return; - } - - // For wall jumping, remove bit above and replace with this (from coding forums) - - - if(PM_CanFlap()) - { - // //pmove->punchangle[0] = -2; - // //float theXComp = (fabs(pmove->velocity[0]) > fabs(pmove->forward[0]) ? pmove->velocity[0] : pmove->forward[0]); - // //float theYComp = (fabs(pmove->velocity[1]) > fabs(pmove->forward[1]) ? pmove->velocity[1] : pmove->forward[1]); - // float theXComp = pmove->forward[0]*(pmove->cmd.forwardmove/pmove->clientmaxspeed) + pmove->right[0]*(pmove->cmd.sidemove/pmove->clientmaxspeed); - // float theYComp = pmove->forward[1]*(pmove->cmd.forwardmove/pmove->clientmaxspeed) + pmove->right[1]*(pmove->cmd.sidemove/pmove->clientmaxspeed); - // - // pmove->velocity[0] += theXComp * 8; - // pmove->velocity[1] += theYComp * 8; - // pmove->velocity[2] += 35; - - AvHMUDeductAlienEnergy(pmove->fuser3, kAlienEnergyFlap); - - // Added by mmcguire. - // Move the lerk in the direction has is facing. - - vec3_t theFlapVelocity; - - float theThrust; - float theLift; - - if (pmove->cmd.forwardmove != 0) - { - - if (pmove->cmd.forwardmove > 0) - { - - theThrust = pmove->cmd.forwardmove * kWingThrustForwardScalar; - theLift = 200 * (pmove->forward[2] + 0.5) / 1.5; - - if (theLift < 0) - { - theLift = 0; - } - - } - else - { - // tankefugl: 0000522 reverse lerk flight - theThrust = pmove->cmd.forwardmove * kWingThrustForwardScalar; //kWingThrustBackwardScalar; - theLift = 200 * (pmove->forward[2] + 0.5) / 1.5; - - if (theLift < 0) - { - theLift = 0; - } - //theThrust = -pmove->cmd.forwardmove * kWingThrustBackwardScalar; - //theLift = 200; - // :tankefugl - } - - } - else - { - theLift = 300; - theThrust = 0; - } - - VectorScale(pmove->forward, theThrust, theFlapVelocity); - theFlapVelocity[2] += theLift; - - int theSpeedUpgradeLevel = 0; - if(GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_4)) - { - theSpeedUpgradeLevel = 1; - - if(GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_12)) - { - theSpeedUpgradeLevel = 2; - } - else if(GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_13)) - { - theSpeedUpgradeLevel = 3; - } - } - int theAdjustment=theSpeedUpgradeLevel * BALANCE_VAR(kAlienCelerityBonus); - float theAscendMax=BALANCE_VAR(kLerkBaseAscendSpeedMax) + theAdjustment; - static float maxVelocity=0; - maxVelocity=max(maxVelocity, pmove->velocity[2]); - if ( pmove->velocity[2] > theAscendMax ) { - theFlapVelocity[2]=0; - } - // cap diving too - if ( -pmove->velocity[2] > theAscendMax*1.3 ) { - theFlapVelocity[2]=0; - } - - vec3_t theNewVelocity; - VectorAdd(pmove->velocity, theFlapVelocity, theNewVelocity); - - VectorCopy(theNewVelocity, pmove->velocity); - /* - - // Old Lerk flight model. - - vec3_t theWishVelocity; - PM_GetWishVelocity(theWishVelocity); - - float theWishXPercent = theWishVelocity[0]/pmove->clientmaxspeed; - float theWishYPercent = theWishVelocity[1]/pmove->clientmaxspeed; - float theWishZPercent = max(0.0f, 1.0f - fabs(theWishXPercent) - fabs(theWishYPercent)); - pmove->velocity[0] += theWishXPercent*kWingFlapLateralScalar; - pmove->velocity[1] += theWishYPercent*kWingFlapLateralScalar; - pmove->velocity[2] += theWishZPercent*pmove->clientmaxspeed; - - */ - - if(pmove->runfuncs) - { -// #ifdef AVH_CLIENT -// int theLong = pmove->RandomLong(0, 20); -// pmove->Con_Printf("Flap %d\n", theLong); -// #endif - - // Pick a random sound to play - int theSoundIndex = pmove->RandomLong(0, 2); - char* theSoundToPlay = NULL; - switch(theSoundIndex) - { - case 0: - theSoundToPlay = kWingFlapSound1; - break; - case 1: - theSoundToPlay = kWingFlapSound2; - break; - case 2: - theSoundToPlay = kWingFlapSound3; - break; - } - - // If alien has silencio upgrade, mute footstep volume - int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); - const float theBaseVolume = .5f; - float theVolumeScalar = theBaseVolume - (theSilenceUpgradeLevel/(float)3)*theBaseVolume; - theVolumeScalar = min(max(theVolumeScalar, 0.0f), 1.0f); - - pmove->PM_PlaySound(CHAN_BODY, theSoundToPlay, theVolumeScalar, ATTN_NORM, 0, PITCH_NORM); - - //pmove->PM_PlaySound(channel, , 1.0f, ATTN_NORM, flags, pitch); - //gEngfuncs.pEventAPI->EV_PlaySound(inArgs->entindex, thePlayer->origin, CHAN_AUTO, kEndCloakSound, 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf )); - - //PM_PlaybackEvent(gFlightEventID); - } - // else if(PM_CanWalljump()) -// { -// pmove->punchangle[0] = -2; -// pmove->velocity[2] += (pmove->forward[2] * 300); -// -// //pmove->velocity[2] = sqrt(2 * 800 * 45.0); -// -// PM_PlaybackEvent(gWallJumpEventID); -// } - - pmove->oldbuttons |= IN_JUMP; // don't jump again until released - return; // in air, so no; effect - } - else - { - - // Added by mmcguire. - // Lerk gliding. - - if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3 && pmove->onground == -1) - { - - // Compute the velocity not in the direction we're facing. - - float theGlideAmount = PM_GetHorizontalSpeed() / 1000; - - if (theGlideAmount > 0.2) - { - theGlideAmount = 0.2; - } - - - float speed = Length(pmove->velocity); - float projectedSpeed = DotProduct(pmove->velocity, pmove->forward); - - // tankefugl: 0000522 reverse lerk flight - if (projectedSpeed < 0) - speed *= -1; - // :tankefugl - vec3_t forwardVelocity; - VectorScale(pmove->forward, speed, forwardVelocity); - - vec3_t glideVelocity; - VectorSubtract(pmove->velocity, forwardVelocity, glideVelocity); - VectorScale(glideVelocity, theGlideAmount, glideVelocity); - - VectorSubtract(pmove->velocity, glideVelocity, pmove->velocity); - - return; - - } - - - } - - // tankefugl: 0000972 walljump - if (canWallJump && (GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && (pmove->cmd.buttons & IN_JUMP) && !(pmove->oldbuttons & IN_JUMP) && (gSurfaceNormal[2] < 0.3))) - { - vec3_t theDirectionVec; - //VectorCopy(pmove->velocity, theDirectionVec); - //if (Length(theDirectionVec) == 0.0f) - VectorCopy(pmove->forward, theDirectionVec); - - VectorNormalize(theDirectionVec); - - VectorScale(theDirectionVec, pmove->maxspeed + 50, pmove->velocity); - pmove->velocity[2] += 100; - - //vec3_t theJumpVect; - //VectorScale(gSurfaceNormal, 50, theJumpVect); - //VectorAdd(theJumpVect, pmove->velocity, pmove->velocity); - - PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), 1.0 ); - - pmove->waterjumptime = 100; - } - // :tankefugl - - // No more effect - if ( pmove->onground == -1 ) - { - // Flag that we jumped. - // HACK HACK HACK - // Remove this when the game .dll no longer does physics code!!!! - pmove->oldbuttons |= IN_JUMP; // don't jump again until released - return; // in air, so no effect - } - - -// string theAlienExtension; -// bool theIsAlien = NS_GetIsPlayerAlien(theAlienExtension); -// if ( pmove->oldbuttons & IN_JUMP && (pmove->velocity[0] == 0 || !theIsAlien || pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) ) - //return; // don't pogo stick - - if ( pmove->oldbuttons & IN_JUMP ) - return; // don't pogo stick - - // In the air now. - pmove->onground = -1; - - PM_PreventMegaBunnyJumping(false); - - if ( tfc ) - { - pmove->PM_PlaySound( CHAN_BODY, "player/plyrjmp8.wav", 0.5, ATTN_NORM, 0, PITCH_NORM ); - } - else - { - PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), 1.0 ); - } - - // See if user can super long jump? - cansuperjump = atoi( pmove->PM_Info_ValueForKey( pmove->physinfo, "slj" ) ) == 1 ? true : false; - - // Acclerate upward - // If we are ducking... - if ( ( pmove->bInDuck ) || ( pmove->flags & FL_DUCKING ) ) - { - // Adjust for super long jump module - // UNDONE -- note this should be based on forward angles, not current velocity. - if ( cansuperjump && - ( pmove->cmd.buttons & IN_DUCK ) && - ( pmove->flDuckTime > 0 ) && - Length( pmove->velocity ) > 50 ) - { - pmove->punchangle[0] = -5; - - for (i =0; i < 2; i++) - { - pmove->velocity[i] = pmove->forward[i] * PLAYER_LONGJUMP_SPEED * 1.6; - } - - pmove->velocity[2] = sqrt(2 * 800 * 56.0); - } - else - { - pmove->velocity[2] = sqrt(2 * 800 * 45.0); - } - } - else - { - pmove->velocity[2] = sqrt(2 * 800 * 45.0); - } - - // Decay it for simulation - PM_FixupGravityVelocity(); - - // Flag that we jumped. - pmove->oldbuttons |= IN_JUMP; // don't jump again until released -} - -/* -============= -PM_CheckWaterJump -============= -*/ -#define WJ_HEIGHT 8 -void PM_CheckWaterJump (void) -{ - vec3_t vecStart, vecEnd; - vec3_t flatforward; - vec3_t flatvelocity; - float curspeed; - pmtrace_t tr; - int savehull; - - // Already water jumping. - if ( pmove->waterjumptime ) - return; - - // Don't hop out if we just jumped in - if ( pmove->velocity[2] < -180 ) - return; // only hop out if we are moving up - - // See if we are backing up - flatvelocity[0] = pmove->velocity[0]; - flatvelocity[1] = pmove->velocity[1]; - flatvelocity[2] = 0; - - // Must be moving - curspeed = VectorNormalize( flatvelocity ); - - // see if near an edge - flatforward[0] = pmove->forward[0]; - flatforward[1] = pmove->forward[1]; - flatforward[2] = 0; - VectorNormalize (flatforward); - - // Are we backing into water from steps or something? If so, don't pop forward - if ( curspeed != 0.0 && ( DotProduct( flatvelocity, flatforward ) < 0.0 ) ) - return; - - VectorCopy( pmove->origin, vecStart ); - vecStart[2] += WJ_HEIGHT; - - VectorMA ( vecStart, 24, flatforward, vecEnd ); - - // Trace, this trace should use the point sized collision hull - savehull = pmove->usehull; - pmove->usehull = 2; - - tr = NS_PlayerTrace(pmove, vecStart, vecEnd, PM_NORMAL, -1 ); - - if ( tr.fraction < 1.0 && fabs( tr.plane.normal[2] ) < 0.1f ) // Facing a near vertical wall? - { - vecStart[2] += pmove->player_maxs[ savehull ][2] - WJ_HEIGHT; - VectorMA( vecStart, 24, flatforward, vecEnd ); - VectorMA( vec3_origin, -50, tr.plane.normal, pmove->movedir ); - - tr = NS_PlayerTrace(pmove, vecStart, vecEnd, PM_NORMAL, -1 ); - - if ( tr.fraction == 1.0 ) - { - pmove->waterjumptime = 2000; - pmove->velocity[2] = 225; - pmove->oldbuttons |= IN_JUMP; - pmove->flags |= FL_WATERJUMP; - } - } - - // Reset the collision hull - pmove->usehull = savehull; -} - -void PM_CheckFalling( void ) -{ - if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - { - if ((pmove->onground != -1) && !pmove->dead) - { - // Slow marines down on landing after a jump - const int theFallPenaltyThreshold = pmove->maxspeed;//BALANCE_VAR(kUnencumberedPlayerSpeed); - if(pmove->flFallVelocity > theFallPenaltyThreshold) - { - if(pmove->iuser3 == AVH_USER3_MARINE_PLAYER) - { - VectorScale(pmove->velocity, .3f, pmove->velocity); - - if(pmove->runfuncs) - { - //#ifdef AVH_CLIENT - //pmove->Con_Printf("Player landed.\n"); - //#endif - } - } - } - - // Play landing sound if we landed hard enough - if(pmove->flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD) - { - float fvol = 0.5; - char theFallPainSound[64]; - - int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); - float theVolumeScalar = 1.0f - theSilenceUpgradeLevel/3.0f; - - float theFallPainVolume = 1.0f*theVolumeScalar; - - string theAlienExtension; - bool theIsAlien = NS_GetIsPlayerAlien(theAlienExtension); - - if(theIsAlien || pmove->iuser3 == AVH_USER3_ALIEN_EMBRYO ) - { - sprintf(theFallPainSound, kFallPainSoundFormat, pmove->iuser3); - } - else - { - if ( GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_13) ) - { - sprintf(theFallPainSound, kFallPainSoundFormat, 2); - } - else - { - sprintf(theFallPainSound, kFallPainSoundFormat, 1); - } - } - - if ( pmove->waterlevel > 0 ) - { - } - else if ( pmove->flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED ) - { - // NOTE: In the original game dll , there were no breaks after these cases, causing the first one to - // cascade into the second - //switch ( RandomLong(0,1) ) - //{ - //case 0: - //pmove->PM_PlaySound( CHAN_VOICE, "player/pl_fallpain2.wav", 1, ATTN_NORM, 0, PITCH_NORM ); - //break; - //case 1: - pmove->PM_PlaySound( CHAN_VOICE, theFallPainSound, theFallPainVolume, ATTN_NORM, 0, PITCH_NORM ); - // break; - //} - fvol = 1.0; - } - else if ( pmove->flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED / 2 ) - { - qboolean tfc = false; - tfc = atoi( pmove->PM_Info_ValueForKey( pmove->physinfo, "tfc" ) ) == 1 ? true : false; - - if ( tfc ) - { - pmove->PM_PlaySound( CHAN_VOICE, theFallPainSound, theFallPainVolume, ATTN_NORM, 0, PITCH_NORM ); - } - - fvol = 0.85; - } - else if ( pmove->flFallVelocity < PLAYER_MIN_BOUNCE_SPEED ) - { - fvol = 0; - } - - if ( fvol > 0.0 ) - { - // Play landing step right away - pmove->flTimeStepSound = 0; - - PM_UpdateStepSound(); - - // play step sound for current texture - PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), fvol ); - - // Knock the screen around a little bit, temporary effect - pmove->punchangle[ 2 ] = pmove->flFallVelocity * 0.013; // punch z axis - - if ( pmove->punchangle[ 0 ] > 8 ) - { - pmove->punchangle[ 0 ] = 8; - } - } - } - } - - if ( pmove->onground != -1 ) - { - pmove->flFallVelocity = 0; - } - } -} - -/* -================= -PM_PlayWaterSounds - - ================= -*/ -void PM_PlayWaterSounds( void ) -{ - // Did we enter or leave water? - if ( ( pmove->oldwaterlevel == 0 && pmove->waterlevel != 0 ) || - ( pmove->oldwaterlevel != 0 && pmove->waterlevel == 0 ) ) - { - int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); - float theVolume = 1.0f - theSilenceUpgradeLevel/3.0f; - - switch ( pmove->RandomLong(0,3) ) - { - case 0: - pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade1.wav", theVolume, ATTN_NORM, 0, PITCH_NORM ); - break; - case 1: - pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade2.wav", theVolume, ATTN_NORM, 0, PITCH_NORM ); - break; - case 2: - pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade3.wav", theVolume, ATTN_NORM, 0, PITCH_NORM ); - break; - case 3: - pmove->PM_PlaySound( CHAN_BODY, "player/pl_wade4.wav", theVolume, ATTN_NORM, 0, PITCH_NORM ); - break; - } - } -} - -/* -=============== -PM_CalcRoll - -=============== -*/ -float PM_CalcRoll (vec3_t angles, vec3_t velocity, float rollangle, float rollspeed ) -{ - float sign; - float side; - float value; - vec3_t forward, right, up; - - AngleVectors (angles, forward, right, up); - - side = DotProduct (velocity, right); - - sign = side < 0 ? -1 : 1; - - side = fabs(side); - - value = rollangle; - - if (side < rollspeed) - { - side = side * value / rollspeed; - } - else - { - side = value; - } - - return side * sign; -} - -/* -============= -PM_DropPunchAngle - -============= -*/ -void PM_DropPunchAngle ( vec3_t punchangle ) -{ - float len; - - len = VectorNormalize ( punchangle ); - len -= (10.0 + len * 0.5) * pmove->frametime; - len = max( len, 0.0 ); - VectorScale ( punchangle, len, punchangle); -} - -/* -============== -PM_CheckParamters - -============== -*/ -void PM_CheckParamters( void ) -{ - float spd; - float maxspeed; - vec3_t v_angle; - - spd = ( pmove->cmd.forwardmove * pmove->cmd.forwardmove ) + - ( pmove->cmd.sidemove * pmove->cmd.sidemove ) + - ( pmove->cmd.upmove * pmove->cmd.upmove ); - spd = sqrt( spd ); - - maxspeed = pmove->clientmaxspeed; //atof( pmove->PM_Info_ValueForKey( pmove->physinfo, "maxspd" ) ); - if ( maxspeed != 0.0 ) - { - pmove->maxspeed = min( maxspeed, pmove->maxspeed ); - } - - if ( ( spd != 0.0 ) && - ( spd > pmove->maxspeed ) ) - { - float fRatio = pmove->maxspeed / spd; - pmove->cmd.forwardmove *= fRatio; - pmove->cmd.sidemove *= fRatio; - pmove->cmd.upmove *= fRatio; - } - - bool theIsImmobilized = GetHasUpgrade(pmove->iuser4, MASK_PLAYER_STUNNED) || GetHasUpgrade(pmove->iuser4, MASK_ALIEN_EMBRYO); - - if ( pmove->flags & FL_FROZEN || - pmove->flags & FL_ONTRAIN || - theIsImmobilized || - pmove->dead ) - { - pmove->cmd.forwardmove = 0; - pmove->cmd.sidemove = 0; - pmove->cmd.upmove = 0; - pmove->cmd.viewangles[0] = 0; - pmove->cmd.viewangles[1] = 0; - pmove->cmd.viewangles[2] = 0; - pmove->cmd.buttons = 0; - } - - - PM_DropPunchAngle( pmove->punchangle ); - - // Take angles from command. - if(!theIsImmobilized) - { - if ( !pmove->dead) - { - VectorCopy ( pmove->cmd.viewangles, v_angle ); - VectorAdd( v_angle, pmove->punchangle, v_angle ); - - // Set up view angles. - pmove->angles[ROLL] = PM_CalcRoll ( v_angle, pmove->velocity, pmove->movevars->rollangle, pmove->movevars->rollspeed )*4; - pmove->angles[PITCH] = v_angle[PITCH]; - pmove->angles[YAW] = v_angle[YAW]; - } - else - { - VectorCopy( pmove->oldangles, pmove->angles ); - } - } - else - { - VectorCopy( pmove->oldangles, pmove->angles ); - } - - // Set dead player view_offset - if ( pmove->dead ) - { - pmove->view_ofs[2] = PM_DEAD_VIEWHEIGHT; - } - - // Adjust client view angles to match values used on server. - if (pmove->angles[YAW] > 180.0f) - { - pmove->angles[YAW] -= 360.0f; - } - - // Set default stepsize - pmove->movevars->stepsize = NS_GetStepsize(pmove->iuser3); -} - -void PM_ReduceTimers( void ) -{ - if ( pmove->flTimeStepSound > 0 ) - { - pmove->flTimeStepSound -= pmove->cmd.msec; - if ( pmove->flTimeStepSound < 0 ) - { - pmove->flTimeStepSound = 0; - } - } - if ( pmove->flDuckTime > 0 ) - { - pmove->flDuckTime -= pmove->cmd.msec; - if ( pmove->flDuckTime < 0 ) - { - pmove->flDuckTime = 0; - } - } - if ( pmove->flSwimTime > 0 ) - { - pmove->flSwimTime -= pmove->cmd.msec; - if ( pmove->flSwimTime < 0 ) - { - pmove->flSwimTime = 0; - } - } -} - -qboolean PM_CanFlap() -{ - qboolean theCanFlap = false; - - if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) - { - if((pmove->onground == -1) && !(pmove->oldbuttons & IN_JUMP)) - { - if(AvHMUHasEnoughAlienEnergy(pmove->fuser3, kAlienEnergyFlap)) - { - // Can't hold the button down - theCanFlap = true; - } - } - } - return theCanFlap; -} - -qboolean PM_CanGlide() -{ - - if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) - { - if((pmove->onground == -1)) - { - return true; - } - } - - return false; - -} - - -qboolean PM_CanWalljump() -{ - vec3_t front; - pmtrace_t trace; - qboolean theCanWalljump = false; - - if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4) - { - front[0] = pmove->origin[0] + pmove->player_maxs[pmove->usehull][0] + 2; - front[1] = pmove->origin[1] + pmove->player_maxs[pmove->usehull][1] + 2; - front[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2] + 1; - - trace = NS_PlayerTrace (pmove, pmove->origin, front, PM_WORLD_ONLY, -1 ); - - if ( (trace.fraction < 1 )) - { - theCanWalljump = true; - } - } - return theCanWalljump; -} - - -void PM_Overwatch() -{ - #ifdef AVH_CLIENT - gOverwatchTargetRange = -1; - #endif - - // If overwatch is on and we have a target -// if(GetHasUpgrade(pmove->iuser4, MASK_MARINE_OVERWATCH)) -// { -// // Get target -// int thePhysIndex = (int)pmove->fuser1; -// if(thePhysIndex != -1) -// { -// physent_t* theTarget = NULL;//AvHSUGetEntity(thePhysIndex); -// -// //= &(pmove->physents[ thePhysIndex ]); -// //physent_t* theTarget = gEngfuncs.pEventAPI->EV_GetPhysent(thePhysIndex); -// -// if(theTarget) -// { -// // TODO: Take gun offset into account with vecMid? -// vec3_t vecMid; -// vec3_t vecMidEnemy; -// vec3_t vecDirToEnemy; -// vec3_t vec; -// -// VectorAdd(pmove->origin, pmove->view_ofs, vecMid); -// -// VectorCopy(theTarget->origin, vecMidEnemy);//theTarget->BodyTarget( vecMid ); -// -// // Right now just point at enemy -// //UTIL_MakeVectors() -// VectorSubtract(vecMidEnemy, vecMid, vecDirToEnemy); -// // = UTIL_VecToAngles(vecDirToEnemy); -// VectorAngles(vecDirToEnemy, vec); -// -// //vec.x = -vec.x; -// vec[0] = -vec[0]; -// -// // if (vec.y > 360) -// // vec.y -= 360; -// // -// // if (vec.y < 0) -// // vec.y += 360; -// -// -// // Set new view angles, set iHasNewViewAngles so it isn't reset on us -// VectorCopy(vec, pmove->angles); -// -// #ifdef AVH_CLIENT -// VectorCopy(pmove->angles, gTopDownViewAngles); -// iHasNewViewAngles = true; -// #endif -// -// AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); -// //VectorCopy(vec, this->pev->v_angle); -// -// // Save target range -// #ifdef AVH_CLIENT -// gOverwatchTargetRange = Length(vecDirToEnemy); -// #endif -// pmove->fuser2 = Length(vecDirToEnemy)/100.0f; -// } -// } -// } -} - -bool PM_TopDown() -{ - bool theInTopDownMode = false; - - if(GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - { - // Reset this, it seems to be getting set by the engine - //pmove->movetype = MOVETYPE_FLY; - - pmove->forward[0] = pmove->forward[1] = 0; - pmove->forward[2] = -1; - - pmove->right[0] = 1; - pmove->right[1] = pmove->right[2] = 0; - - pmove->up[0] = pmove->up[2] = 0; - pmove->up[1] = 1; - - theInTopDownMode = true; - - const AvHMapExtents& theMapExtents = GetMapExtents(); - - float theMinX = theMapExtents.GetMinMapX(); - float theMinY = theMapExtents.GetMinMapY(); - float theMaxX = theMapExtents.GetMaxMapX(); - float theMaxY = theMapExtents.GetMaxMapY(); - float theMaxZ = theMapExtents.GetMaxViewHeight(); - float theMinZ = theMapExtents.GetMinViewHeight(); - - // Modify the max to make sure commander stays inside the world - vec3_t theStartPos; - VectorCopy(pmove->origin, theStartPos); - theStartPos[2] = theMaxZ; - - vec3_t theNewStartPos; - vec3_t theEndPos; - VectorCopy(pmove->origin, theEndPos); - theEndPos[2] = theMinZ; - - float theMaxCommanderHeight = theMaxZ; - - AvHSHUGetFirstNonSolidPoint((float*)theStartPos, (float*)theEndPos, (float*)theNewStartPos); - - //theMaxZ = min(theMaxZ, theNewStartPos[2]); - -// #ifdef AVH_CLIENT -// extern DebugPointListType gSquareDebugLocations; -// DebugPoint theDebugPoint(theNewStartPos[0], theNewStartPos[1], theMaxZ - theNewStartPos[2]); -// gSquareDebugLocations.push_back(theDebugPoint); -// #endif - - float speed, drop, friction, control, newspeed; - float currentspeed, addspeed, accelspeed; - int i; - vec3_t wishvel; - vec3_t theAngles; - float fmove = 0.0f; - float smove = 0.0f; - float umove = 0.0f; - vec3_t wishdir; - float wishspeed; - - // Don't scroll when COMMANDER_MOUSECOORD set, it's indicating a world position, not a move - //if(pmove->cmd.impulse != COMMANDER_MOUSECOORD) - //{ -// #ifdef AVH_CLIENT -// if ( pmove->runfuncs ) -// { -// // Set spectator flag -// iIsSpectator = SPEC_IS_SPECTATOR; -// } -// #endif - - // Move around in normal spectator method - // friction - speed = Length (pmove->velocity); - if (speed < 1) - { - VectorCopy (vec3_origin, pmove->velocity) - } - else - { - drop = 0; - - friction = pmove->movevars->friction*5.0; // extra friction - //friction = pmove->movevars->friction*.9f; - control = speed < pmove->movevars->stopspeed ? pmove->movevars->stopspeed : speed; - drop += control*friction*pmove->frametime; - - // scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - - VectorScale (pmove->velocity, newspeed, pmove->velocity); - } - - // If player moved mouse wheel, move camera up and down if we didn't just move over a view entity - qboolean theFoundEntity = false; - //float theDesiredHeight = PM_GetDesiredTopDownCameraHeight(theFoundEntity); - -// gHeightLevel += (pmove->cmd.forwardmove/300.0f); -// float theDesiredHeight = theMaxViewHeight + gHeightLevel; -// -// // Note: To have a nice smooth zoom-up effect, comment out this next line, but you'll have to -// // fix the bouncing from the drop/control/friction code above. That's where the problem is. -// // You'll also have to add an initial upwards velocity in AvHPlayer::StartTopDown() -// pmove->origin[2] = theDesiredHeight; -// -// const float kSwoopingTolerance = 1.0f; -// float theDiff = theDesiredHeight - pmove->origin[2]; -// float theFabsDiff = fabs(theDiff); -// -// if(theFabsDiff > kSwoopingTolerance) -// { -// // When we get near, don't use sudden moves, move slowly -// float theAmount = min(theFabsDiff*10, 1000); -// -// //fmove = theDiff;//pmove->cmd.forwardmove; -// if(theDiff > 0) -// fmove = -theAmount; -// else -// fmove = theAmount; -// } -// else -// { -// fmove = 0.0f; -// } - - // accelerate - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - umove = pmove->cmd.upmove; - - float theMoveTotal = fabs(fmove) + fabs(smove) + fabs(umove); - if(theMoveTotal > 5.0f) - { -// VectorNormalize (pmove->forward); -// VectorNormalize (pmove->right); -// VectorNormalize (pmove->up); -// -// for (i=0 ; i<3 ; i++) -// { -// wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove + pmove->up[i]*umove; -// } -// //wishvel[2] += pmove->cmd.upmove; -// - PM_GetWishVelocity(wishvel); - VectorCopy (wishvel, wishdir); - - wishspeed = VectorNormalize(wishdir); - - // - // clamp to server defined max speed - // - if (wishspeed > pmove->movevars->maxspeed) - { - VectorScale (wishvel, pmove->movevars->maxspeed/wishspeed, wishvel); - wishspeed = pmove->movevars->maxspeed; - } - - currentspeed = DotProduct(pmove->velocity, wishdir); - addspeed = wishspeed - currentspeed; - if (addspeed > 0) - { - accelspeed = pmove->movevars->accelerate*pmove->frametime*wishspeed; - if (accelspeed > addspeed) - accelspeed = addspeed; - - for (i=0 ; i<3 ; i++) - pmove->velocity[i] += accelspeed*wishdir[i]; - - // move - VectorMA (pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); - } - } - else - { - wishvel[0] = wishvel[1] = wishvel[2] = 0.0f; - wishdir[0] = wishdir[1] = wishdir[2] = 0.0f; - pmove->velocity[2] = 0.0f; - } - //} - - // Set view down - theAngles[0] = kTopDownYaw; - theAngles[1] = kTopDownPitch; - theAngles[2] = kTopDownRoll; - - // Set angles facing down so observer knows which way to point - VectorCopy(theAngles, pmove->angles); - - // Set origin - //pmove->origin[2] = 1080; - //pmove->origin[2] = PM_GetDesiredTopDownCameraHeight(); - - #ifdef AVH_CLIENT - if ( pmove->runfuncs ) - { - VectorCopy(theAngles, gTopDownViewAngles); - //iHasNewViewAngles = true; - - // Set view origin to our real origin but at our highest map extents. This is needed to the commander's origin is actually inside the world so - // he receives nearby events, but so he looks like he's outside the world. Changing this? Make sure AvHPlayer::GetVisualOrigin() is updated also. - VectorCopy(pmove->origin, gTopDownViewOrigin); - gTopDownViewOrigin[2] = theMaxCommanderHeight; - - //iHasNewViewOrigin = true; - } - #endif - - AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); - - // Are we zooming to an area? - if(pmove->cmd.impulse == COMMANDER_MOVETO) - { - pmove->origin[0] = pmove->cmd.upmove*kWorldPosNetworkConstant; - pmove->origin[1] = pmove->cmd.sidemove*kWorldPosNetworkConstant; - VectorCopy(vec3_origin, pmove->velocity) - } - - // Clip position to map extents - float theCurrentX = pmove->origin[0]; - float theCurrentY = pmove->origin[1]; - //float theCurrentZ = pmove->origin[2]; - - pmove->origin[0] = min(max(theMinX, theCurrentX), theMaxX); - pmove->origin[1] = min(max(theMinY, theCurrentY), theMaxY); - pmove->origin[2] = min(max(theMinZ, theNewStartPos[2]), theMaxZ); - - if(pmove->runfuncs) - { - // Update view offset - #ifdef AVH_CLIENT - // Save our top down height so we can offset camera in view.cpp - // Changing this? Make sure AvHPlayer::GetVisualOrigin() is updated also. - //gTopDownHeight = theMaxCommanderHeight; - #endif - - pmove->view_ofs[2] = theMaxCommanderHeight - pmove->origin[2]; - - //pmove->Con_Printf("PMTopDown(): up: %f, side: %f, forward: %f, impulse: %d, velocity: %f\n", pmove->cmd.upmove, pmove->cmd.sidemove, pmove->cmd.forwardmove, pmove->cmd.impulse, Length(pmove->velocity)); - } - } - - // Reset view - // else if(pmove->iuser3 == AVH_USER3_VIEW_SPECIAL_LEAVE_COMMANDER) - // { - // vec3_t theAngles; - // - // // Set view down - // theAngles[0] = 0; - // theAngles[1] = 0; - // theAngles[2] = 0; - // - // // Set angles facing down so observer knows which way to point - // VectorCopy(theAngles, pmove->angles); - // - // #ifdef AVH_CLIENT - // if ( pmove->runfuncs ) - // { - // VectorCopy(theAngles, gTopDownViewAngles); - // iHasNewViewAngles = true; - // } - // #endif - // - // AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); - // - // #ifdef AVH_CLIENT - // if ( pmove->runfuncs ) - // { - // iHasNewViewAngles = true; - // pmove->iuser3 = 0; - // } - // #endif - // - // #ifdef AVH_SERVER - // pmove->iuser3 = 0; - // #endif - // } - return theInTopDownMode; -} - -void PM_Jetpack() -{ - bool theHasJetpackUpgrade = GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_7) && (pmove->iuser3 == AVH_USER3_MARINE_PLAYER); - - // Turn off jetpack by default - gIsJetpacking[pmove->player_index] = false; - - if(!pmove->dead && theHasJetpackUpgrade) - { - bool theJumpHeldDown = (pmove->cmd.buttons & IN_JUMP); - bool theHasEnoughEnergy = (pmove->fuser3 > (kJetpackMinimumEnergyToJetpack*kNormalizationNetworkFactor)); - float theTimePassed = pmove->frametime; - - // If jump is held down and we have a jetpack, apply upwards force! - added check for digestion - elven - if(theJumpHeldDown && theHasEnoughEnergy && !GetHasUpgrade(pmove->iuser4, MASK_ENSNARED) && !GetHasUpgrade(pmove->iuser4, MASK_PLAYER_STUNNED) && !GetHasUpgrade(pmove->iuser4, MASK_DIGESTING)) - { - gIsJetpacking[pmove->player_index] = true; - - // Apply upwards force to player - //pmove->velocity[2] += kJetpackForce; - - vec3_t theWishVelocity; - PM_GetWishVelocity(theWishVelocity); - - const float kBaseScalar = .6f; - - int theMinMarineSpeed = BALANCE_VAR(kBasePlayerSpeed); - if(theMinMarineSpeed == 0) - { - theMinMarineSpeed = 150; - } - - int theMaxMarineSpeed = BALANCE_VAR(kUnencumberedPlayerSpeed); - if(theMaxMarineSpeed == 0) - { - theMaxMarineSpeed = 220; - } - - float theWeightScalar = kBaseScalar + (1.0f - kBaseScalar)*((pmove->clientmaxspeed - theMinMarineSpeed)/(theMaxMarineSpeed - theMinMarineSpeed)); - - pmove->velocity[0] += (theWishVelocity[0]/pmove->clientmaxspeed)*kJetpackLateralScalar; - pmove->velocity[1] += (theWishVelocity[1]/pmove->clientmaxspeed)*kJetpackLateralScalar; - pmove->velocity[2] += theTimePassed*theWeightScalar*kJetpackForce; - - // Play an event every so often - if(pmove->runfuncs /*&& (pmove->RandomLong(0, 2) == 0)*/) - { - pmove->PM_PlaybackEventFull(0, pmove->player_index, gJetpackEventID, 0, (float *)pmove->origin, (float *)pmove->origin, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); - } - } - - float theJetpackEnergy = pmove->fuser3/kNormalizationNetworkFactor; - - AvHMUUpdateJetpackEnergy(gIsJetpacking[pmove->player_index], theTimePassed, theJetpackEnergy); - - pmove->fuser3 = theJetpackEnergy*kNormalizationNetworkFactor; - } - - //pmove->Con_Printf("Jetpacking: %d\n", gIsJetpacking); -} - -/* -============= -PlayerMove - -Returns with origin, angles, and velocity modified in place. - -Numtouch and touchindex[] will be set if any of the physents -were contacted during the move. -============= -*/ -void PM_PlayerMove ( qboolean server ) -{ - physent_t *pLadder = NULL; - - // Are we running server code? - pmove->server = server; - - if (pmove->cmd.buttons & IN_JUMP) - { - int a =0; - } - - int theMaxSpeed = 0; - - // Adjust speeds etc. - PM_CheckParamters(); - - // Assume we don't touch anything - pmove->numtouch = 0; - - // # of msec to apply movement - pmove->frametime = pmove->cmd.msec * 0.001; - -// if(pmove->runfuncs) -// { -// pmove->Con_Printf("pmove->frametime: %f\n", pmove->frametime); -// } - - PM_ReduceTimers(); - - bool theIsGestating = GetHasUpgrade(pmove->iuser4, MASK_ALIEN_EMBRYO); - bool theIsParalyzed = GetHasUpgrade(pmove->iuser4, MASK_PLAYER_STUNNED); - - if(!theIsParalyzed && !theIsGestating) - { - // Convert view angles to vectors - AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); - } - - //if(pmove->cmd.impulse == COMMANDER_MOUSECOORD) -// if(pmove->iuser3 == AVH_USER3_VIS_SPECIAL_TOPDOWN) -// { -// if((pmove->cmd.impulse != COMMANDER_MOVETO) && (pmove->cmd.impulse != MESSAGE_NULL)) -// { -// return; -// } -// } - - bool theIsDucking = AvHMUGetCanDuck(pmove->iuser3) && (pmove->flags & FL_DUCKING); - pmove->usehull = AvHMUGetHull(theIsDucking, pmove->iuser3); - - //pmove->Con_Printf("Using hull: %d\n", pmove->usehull); - - //PM_ShowClipBox(); - - // Clear movement when building, giving orders, or anything other than scrolling - if(GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) - { - if((pmove->cmd.impulse != COMMANDER_SCROLL) && (pmove->cmd.impulse != COMMANDER_MOVETO)) - { - pmove->cmd.sidemove = pmove->cmd.upmove = pmove->cmd.forwardmove = 0; - pmove->cmd.buttons = 0; - } - } - - if ( pmove->flags & FL_FROZEN ) - return; - - // Special handling for spectator and observers. (iuser1 is set if the player's in observer mode) - if ( pmove->spectator || pmove->iuser1 > 0 ) - { - PM_SpectatorMove(); - PM_CategorizePosition(); - return; - } - -// if(GetHasUpgrade(pmove->iuser4, MASK_MARINE_OVERWATCH)) -// { -// PM_Overwatch(); -// } - - PM_TopDown(); - - // Paralyzed players and embryos can't move - if(theIsParalyzed || theIsGestating) - { - PM_Physics_Toss(); - return; - } - - // Always try and unstick us unless we are in NOCLIP mode - if ( pmove->movetype != MOVETYPE_NOCLIP && pmove->movetype != MOVETYPE_NONE ) - { - if ( PM_CheckStuck() ) - { - return; // Can't move, we're stuck - } - } - - // Now that we are "unstuck", see where we are ( waterlevel and type, pmove->onground ). - PM_CategorizePosition(); - - // Store off the starting water level - pmove->oldwaterlevel = pmove->waterlevel; - - // If we are not on ground, store off how fast we are moving down - if ( pmove->onground == -1 ) - { - pmove->flFallVelocity = -pmove->velocity[2]; - } - - g_onladder[pmove->player_index] = 0; - - PM_Jetpack(); - - PM_AlienAbilities(); - - NS_UpdateWallsticking(); - - // Don't run ladder code if dead or on a train, or a lerk or a skulk - bool theIsFlyingAlien = (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) && (pmove->onground == -1); - bool theIsSkulk = (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1); - - if ( !pmove->dead && !(pmove->flags & FL_ONTRAIN) && !gIsJetpacking[pmove->player_index] && !GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && !theIsFlyingAlien && !theIsSkulk) - { - pLadder = PM_Ladder(); - if ( pLadder ) - { - g_onladder[pmove->player_index] = 1; - } - } - - //pmove->Con_DPrintf("g_onladder: %d\n", g_onladder); - - PM_UpdateStepSound(); - - PM_Duck(); - - // Don't run ladder code if dead or on a train - if ( !pmove->dead && !(pmove->flags & FL_ONTRAIN) && !gIsJetpacking[pmove->player_index] && !GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - { - if ( pLadder ) - { - PM_LadderMove( pLadder ); - } - else if ( pmove->movetype != MOVETYPE_WALK && - pmove->movetype != MOVETYPE_NOCLIP ) - { - // Clear ladder stuff unless player is noclipping - // it will be set immediately again next frame if necessary - pmove->movetype = MOVETYPE_WALK; - } - } - - // Slow down, I'm pulling it! (a box maybe) but only when I'm standing on ground - if ( ( pmove->onground != -1 ) && ( pmove->cmd.buttons & IN_USE) ) - { - VectorScale( pmove->velocity, 0.3, pmove->velocity ); - } - - // Reset gravity to 1.0, in case we're not gliding anymore. This will get changed - // in PM_Jump if player is still holding down the key. - // This assumes only players in here! Sounds problematic to me. -// if(pmove->iuser3 == AVH_USER3_GLIDE) -// { -// pmove->gravity = 1.0f; -// } - - // Handle movement - switch ( pmove->movetype ) - { - default: - pmove->Con_Printf("Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", pmove->movetype, pmove->server); - break; - - case MOVETYPE_NONE: - break; - - case MOVETYPE_NOCLIP: - PM_NoClip(); - break; - - case MOVETYPE_TOSS: - case MOVETYPE_BOUNCE: - PM_Physics_Toss(); - break; - - case MOVETYPE_FLY: - - PM_CheckWater(); - - // Was jump button pressed? - // If so, set velocity to 270 away from ladder. This is currently wrong. - // Also, set MOVE_TYPE to walk, too. - if ( pmove->cmd.buttons & IN_JUMP) - { - if ( !pLadder ) - { - PM_Jump (); - } - } - else - { - pmove->oldbuttons &= ~IN_JUMP; - } - - // Perform the move accounting for any base velocity. - VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity); - PM_FlyMove (); - VectorSubtract (pmove->velocity, pmove->basevelocity, pmove->velocity); - break; - - case MOVETYPE_WALK: - if ( !PM_InWater() ) - { - PM_AddCorrectGravity(); - } - - // If we are leaping out of the water, just update the counters. - // tankefugl: 0000972 - if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) - // :tankefugl -// if ( pmove->waterjumptime ) - { - PM_WaterJump(); - PM_FlyMove(); - - // Make sure waterlevel is set correctly - PM_CheckWater(); - return; - } - - // If we are swimming in the water, see if we are nudging against a place we can jump up out - // of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0 - if ( pmove->waterlevel >= 2 ) - { - if ( pmove->waterlevel == 2 ) - { - PM_CheckWaterJump(); - } - - // If we are falling again, then we must not trying to jump out of water any more. - if ( pmove->velocity[2] < 0 && pmove->waterjumptime ) - { - pmove->waterjumptime = 0; - } - - // Was jump button pressed? - if (pmove->cmd.buttons & IN_JUMP) - { - PM_Jump (); - } - else - { - pmove->oldbuttons &= ~IN_JUMP; - } - - // Perform regular water movement - PM_WaterMove(); - - VectorSubtract (pmove->velocity, pmove->basevelocity, pmove->velocity); - - // Get a final position - PM_CategorizePosition(); - } - else - - // Not underwater - { - - // Was jump button pressed? - if ( pmove->cmd.buttons & IN_JUMP ) - { - if ( !pLadder ) - { - PM_Jump (); - } - } - else - { - pmove->oldbuttons &= ~IN_JUMP; - } - - // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor, - // we don't slow when standing still, relative to the conveyor. - if ((pmove->onground != -1) || GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - { - if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - pmove->velocity[2] = 0.0; - PM_Friction(); - } - - // Make sure velocity is valid. - PM_CheckVelocity(); - - // Are we on ground now - if ( (pmove->onground != -1) || GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - { - PM_WalkMove(); - } - else - { - PM_AirMove(); // Take into account movement when in air. - } - - // Set final flags. - PM_CategorizePosition(); - - //float theVelocityLength = Length(pmove->velocity); - //pmove->Con_Printf("Max speed: %f, velocity: %f\n", pmove->maxspeed, theVelocityLength); - - // Now pull the base velocity back out. - // Base velocity is set if you are on a moving object, like - // a conveyor (or maybe another monster?) - VectorSubtract (pmove->velocity, pmove->basevelocity, pmove->velocity ); - - // Make sure velocity is valid. - PM_CheckVelocity(); - - // Add any remaining gravitational component. - if ( !PM_InWater() ) - { - PM_FixupGravityVelocity(); - } - - // If we are on ground, no downward velocity. - if((pmove->onground != -1) && !GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) - { - pmove->velocity[2] = 0; - } - - // See if we landed on the ground with enough force to play - // a landing sound. - PM_CheckFalling(); - } - - - // Did we enter or leave the water? - PM_PlayWaterSounds(); - break; - } -} - -void PM_CreateStuckTable( void ) -{ - float x, y, z; - int idx; - int i; - float zi[3]; - - memset(rgv3tStuckTable, 0, 54 * sizeof(vec3_t)); - - idx = 0; - // Little Moves. - x = y = 0; - // Z moves - for (z = -0.125 ; z <= 0.125 ; z += 0.125) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - idx++; - } - x = z = 0; - // Y moves - for (y = -0.125 ; y <= 0.125 ; y += 0.125) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - idx++; - } - y = z = 0; - // X moves - for (x = -0.125 ; x <= 0.125 ; x += 0.125) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - idx++; - } - - // Remaining multi axis nudges. - for ( x = - 0.125; x <= 0.125; x += 0.250 ) - { - for ( y = - 0.125; y <= 0.125; y += 0.250 ) - { - for ( z = - 0.125; z <= 0.125; z += 0.250 ) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - idx++; - } - } - } - - // Big Moves. - x = y = 0; - zi[0] = 0.0f; - zi[1] = 1.0f; - zi[2] = 6.0f; - - for (i = 0; i < 3; i++) - { - // Z moves - z = zi[i]; - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - idx++; - } - - x = z = 0; - - // Y moves - for (y = -2.0f ; y <= 2.0f ; y += 2.0) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - idx++; - } - y = z = 0; - // X moves - for (x = -2.0f ; x <= 2.0f ; x += 2.0f) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - idx++; - } - - // Remaining multi axis nudges. - for (i = 0 ; i < 3; i++) - { - z = zi[i]; - - for (x = -2.0f ; x <= 2.0f ; x += 2.0f) - { - for (y = -2.0f ; y <= 2.0f ; y += 2.0) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - idx++; - } - } - } -} - - - - -/* -This modume implements the shared player physics code between any particular game and -the engine. The same PM_Move routine is built into the game .dll and the client .dll and is -invoked by each side as appropriate. There should be no distinction, internally, between server -and client. This will ensure that prediction behaves appropriately. -*/ - -void PM_Move ( struct playermove_s *ppmove, int server ) -{ - assert( pm_shared_initialized ); - - pmove = ppmove; - - PM_SetHulls(); - - PM_PlayerMove( ( server != 0 ) ? true : false ); - - if ( pmove->onground != -1 ) - { - pmove->flags |= FL_ONGROUND; - } - else - { - pmove->flags &= ~FL_ONGROUND; - } - - // In single player, reset friction after each movement to FrictionModifier Triggers work still. - if ( !pmove->multiplayer && ( pmove->movetype == MOVETYPE_WALK ) ) - { - pmove->friction = 1.0f; - } - - //int theRandomNumber = pmove->RandomLong(0, 100); - // - //#ifdef AVH_CLIENT - //if(pmove->runfuncs) - //{ - // PM_DebugLocations(theRandomNumber); - //} - //#endif - - // Remember most predicted player origin (only if runfuncs?) - if(pmove->runfuncs) - { - VectorCopy(pmove->origin, gPredictedPlayerOrigin); - VectorCopy(pmove->view_ofs, gPredictedPlayerVOfs); - } - - /////////////////////////////// - // Begin Max's Code - /////////////////////////////// - - // If the current orientation is different than the desired orientation, - // interpolate between them. - - vec3_t currentAngles; - vec3_t targetAngles; - - #ifdef AVH_SERVER - - NS_FixWallstickingAngles(pmove->vuser1); - NS_FixWallstickingAngles(pmove->vuser2); - - VectorCopy(pmove->vuser1, currentAngles); - VectorCopy(pmove->vuser2, targetAngles); - - #endif - - #ifdef AVH_CLIENT - - NS_FixWallstickingAngles(gPlayerAngles); - NS_FixWallstickingAngles(gTargetPlayerAngles); - - VectorCopy(gPlayerAngles, currentAngles); - VectorCopy(gTargetPlayerAngles, targetAngles); - - #endif - - if (VectorCompare(currentAngles, targetAngles) == 0) - { - - Quat src = Quat(currentAngles).Unit(); - Quat dst = Quat(targetAngles).Unit(); - - Quat rot = (dst * src.Conjugate()).Unit(); - - // Compute the axis and angle we need to rotate about to go from src - // to dst. - - float angle = acosf(rot.w) * 2; - float sinAngle = sqrtf(1.0f - rot.w * rot.w); - - if (fabs(sinAngle) < 0.0005f) - { - sinAngle = 1; - } - - vec3_t axis; - - axis[0] = rot.x / sinAngle; - axis[1] = rot.y / sinAngle; - axis[2] = rot.z / sinAngle; - - // Wrap the angle to the range -PI to PI - angle = WrapFloat(angle, -M_PI, M_PI); - - // Amount to rotate this frame. - float frameAngle = kSkulkRotationRate * pmove->frametime; - - if (fabs(angle) <= frameAngle) - { - // If we are very close, just jump to the goal orientation. - VectorCopy(targetAngles, currentAngles); - } - else - { - - Quat final; - - if (angle < 0) - { - final = Quat(-frameAngle, axis) * src; - } - else - { - final = Quat(frameAngle, axis) * src; - } - - vec3_t xAxis; - vec3_t yAxis; - vec3_t zAxis; - - final.GetVectors(xAxis, yAxis, zAxis); - - VectorsToAngles(yAxis, xAxis, zAxis, currentAngles); - - } - - } - - #ifdef AVH_SERVER - VectorCopy(currentAngles, pmove->vuser1); - #endif - - #ifdef AVH_CLIENT - VectorCopy(currentAngles, gPlayerAngles); - #endif - - /////////////////////////////// - // End Max's Code - /////////////////////////////// - -} - -//void PM_GetEntityList(PhysEntListType& outList) -//{ -// physent_t* theTarget = NULL; -// -// for (i = 0; i < MAX_PHYSENTS; i++) -// { -// theTarget = pmove->physents[i]; -// outList.push_back(theTarget); -// } -// -//} - - -int PM_GetVisEntInfo( int ent ) -{ - if ( ent >= 0 && ent <= pmove->numvisent ) - { - return pmove->visents[ ent ].info; - } - return -1; -} - -int PM_GetPhysEntInfo( int ent ) -{ - if ( ent >= 0 && ent <= pmove->numphysent) - { - return pmove->physents[ ent ].info; - } - return -1; -} - -void PM_Init( struct playermove_s *ppmove ) -{ - assert( !pm_shared_initialized ); - - pmove = ppmove; - - PM_CreateStuckTable(); - PM_InitTextureTypes(); - PM_InitBoxHull(); - - pm_shared_initialized = 1; -} - - -bool PM_ViewTraceEntity(float inOriginX, float inOriginY, float inOriginZ, float inDirX, float inDirY, float inDirZ, int& outIndex) -{ - bool theSuccess = false; - - vec3_t theTraceEnd; - vec3_t theNormal; - theNormal[0] = inDirX; - theNormal[1] = inDirY; - theNormal[2] = inDirZ; - VectorScale(theNormal, 8012, theTraceEnd); - - vec3_t theTraceStart; - theTraceStart[0] = inOriginX; - theTraceStart[1]= inOriginY; - theTraceStart[2] = inOriginZ; - - pmtrace_t* theTrace = NULL; - bool theDone = false; - - do - { - theTrace = pmove->PM_TraceLine(theTraceStart, theTraceEnd, PM_TRACELINE_ANYVISIBLE, 2, -1); - - if(!theTrace || theTrace->fraction < 0.0001f || theTrace->ent > 0) - { - theDone = true; - } - else - { - // Trace again from here - VectorCopy(theTrace->endpos, theTraceStart); - } - - } while(!theDone); - - if(theTrace && (theTrace->ent > 0)) - { - outIndex = theTrace->ent; - theSuccess = true; - } - return theSuccess; -} - -bool GetIsEntityAPlayer(int inPhysIndex) -{ - bool theEntIsPlayer = false; - - physent_t* theEntity = NULL; - if((inPhysIndex > 0) && (inPhysIndex <= pmove->numphysent)) - { - theEntity = pmove->physents + inPhysIndex; - } - - if(theEntity) - { - const char* theName = theEntity->name; - if(!strcmp(theName, kAvHPlayerClassName)) - { - theEntIsPlayer = true; - } - } - - return theEntIsPlayer; -} - - -bool PM_ViewTracePlayer(float inOriginX, float inOriginY, float inOriginZ, float inEndX, float inEndY, float inEndZ, int& outIndex) -{ - bool theSuccess = false; - - #ifdef AVH_CLIENT - //gSquareDebugLocations.clear(); - #endif - - vec3_t theTraceEnd; - //vec3_t theNormal; - //theNormal[0] = inDirX; - //theNormal[1] = inDirY; - //theNormal[2] = inDirZ; - //VectorScale(theNormal, 8012, theTraceEnd); - theTraceEnd[0] = inEndX; - theTraceEnd[1] = inEndY; - theTraceEnd[2] = inEndZ; - - vec3_t theTraceStart; - theTraceStart[0] = inOriginX; - theTraceStart[1]= inOriginY; - theTraceStart[2] = inOriginZ; - - #ifdef AVH_CLIENT - DebugPoint thePoint; - thePoint.x = theTraceStart[0]; - thePoint.y = theTraceStart[1]; - thePoint.z = theTraceStart[2]; - //gSquareDebugLocations.push_back(thePoint); - - thePoint.x = theTraceEnd[0]; - thePoint.y = theTraceEnd[1]; - thePoint.z = theTraceEnd[2]; - //gSquareDebugLocations.push_back(thePoint); - #endif - -// pmtrace_t* theTrace = NULL; -// bool theDone = false; - -// do -// { - pmtrace_t theTraceStruct; - - theTraceStruct = NS_PlayerTrace(pmove, theTraceStart, theTraceEnd, PM_NORMAL, -1); - int theEntIndex = theTraceStruct.ent; - -// //pmtrace_t (*PM_PlayerTrace) (vec3_t start, vec3_t end, int traceFlags, int ignore_pe ); -// //theTrace = pmove->PM_TraceLine(theTraceStart, theTraceEnd, PM_TRACELINE_ANYVISIBLE, 2, -1); -// bool theEntityIsAPlayer = false; -// -// if(!theTrace || theTrace->fraction < 0.0001f || (theEntityIsAPlayer = GetIsEntityAPlayer(theTrace->ent))) -// { -// theDone = true; -// } -// else -// { -// // Trace again from here -// VectorCopy(theTrace->endpos, theTraceStart); -// } - - //if(!theDone || theEntityIsAPlayer) - if(theEntIndex > 0) - { - #ifdef AVH_CLIENT - DebugPoint thePoint; - thePoint.x = theTraceStruct.endpos[0]; - thePoint.y = theTraceStruct.endpos[1]; - thePoint.z = theTraceStruct.endpos[2]; - //gSquareDebugLocations.push_back(thePoint); - #endif - } - -// } while(!theDone); - - if(theEntIndex > 0) - //if(theTrace && (theTrace->ent > 0)) - { - outIndex = theEntIndex; - //outIndex = theTrace->ent; - theSuccess = true; - } - return theSuccess; -} - - - +// Copyright (c) 1999, Valve LLC. All rights reserved. +// +// This product contains software technology licensed from Id +// Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +// All Rights Reserved. +// +// Use, distribution, and modification of this source code and/or resulting +// object code is restricted to non-commercial enhancements to products from +// Valve LLC. All other use, distribution, or modification is prohibited +// without written permission from Valve LLC. +// +// +// $Workfile: pm_shared.cpp $ +// $Date: 2002/10/24 21:20:28 $ +// +//------------------------------------------------------------------------------- +// $Log: pm_shared.cpp,v $ +// Revision 1.39 2002/10/24 21:20:28 Flayra +// - Reworked jetpack effect +// - Extended spectating distance +// - Only play blink effect once +// - Update alien energy in shared code +// +// Revision 1.38 2002/10/18 22:16:51 Flayra +// - Level5 crouching fix +// - Skulks can't use ladders +// +// Revision 1.37 2002/10/16 20:50:28 Flayra +// - Updated onos footsteps with new sound +// +// Revision 1.36 2002/10/16 00:42:55 Flayra +// - Removed blink fail event +// +// Revision 1.35 2002/10/03 18:30:31 Flayra +// - Moved alien energy to fuser3 +// - Flying/ladder fix (lerks can't use them) +// - When players are frozen, don't allow them to press buttons either +// +// Revision 1.34 2002/09/25 20:41:45 Flayra +// - Removed marine strafing slowdown +// +// Revision 1.33 2002/09/23 22:05:58 Flayra +// - Added code for rotation skulk model (removed it though) +// - Removed inverse lighting for skulks +// - Started to add speed abuse code, but realized the physics are inprecise and going over maxspeed is normal +// +// Revision 1.32 2002/09/09 19:45:30 Flayra +// - Blink fixes, removed level 5 custom step size +// +// Revision 1.31 2002/08/16 02:28:03 Flayra +// - Removed overwatch code +// - Webs now prevent jumping and jetpacking +// +// Revision 1.30 2002/08/09 01:12:16 Flayra +// - Turned on wall-climbing by default +// - Tweaked jetpack physics +// - Only update predicted player origin when runfuncs is true (fixes bug with flickering ghost unit in CM) +// +// Revision 1.29 2002/08/02 21:45:46 Flayra +// - Fixed jetpack energy problem, moved wall-running back to duck +// +// Revision 1.28 2002/07/25 17:28:37 Flayra +// - More linux changes +// +// Revision 1.27 2002/07/23 16:51:49 Flayra +// - MrBlonde's fix for player shaking (not sure how well it works) +// +// Revision 1.26 2002/07/10 14:37:18 Flayra +// - Removed large differing step-sizes (bug #255) +// +// Revision 1.25 2002/07/08 16:23:28 Flayra +// - Added debug code to fix solidity problems, fixed commander/hera-landing-pad bug, added document header +// +//=============================================================================== +#include +#include "common/mathlib.h" +#include "common/const.h" +#include "common/usercmd.h" +#include "pm_defs.h" +#include "pm_shared.h" +#include "pm_movevars.h" +#include "pm_debug.h" +#include // NULL +#include // sqrt +#include // strcpy +#include // atoi +#include // isspace +#include "mod/AvHSpecials.h" +#include "mod/AvHMarineEquipmentConstants.h" +#include "mod/AvHMessage.h" +#include "util/MathUtil.h" +#include "util/Quat.h" +#include "util/Mat3.h" +#include "common/event_flags.h" +#include "mod/AvHSoundConstants.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHHulls.h" +#include "mod/AvHAlienAbilityConstants.h" +#include "mod/AvHAlienWeaponConstants.h" +#include "mod/AvHMovementUtil.h" +#include "mod/AvHMapExtents.h" +#include "mod/AvHSharedMovementInfo.h" +#include "util/Balance.h" + + + +#include "common/const.h" +#include "common/com_model.h" +#include "common/mathlib.h" + +#include "mod/CollisionUtil.h" +#include "engine/studio.h" + + +//#ifdef AVH_SERVER +#include "engine/edict.h" +#include "engine/eiface.h" +#include "dlls/enginecallback.h" +//#endif + +#ifdef AVH_SERVER +void AvHSUAddDebugPoint(float inX, float inY, float inZ); +#endif + +void AvHSHUGetFirstNonSolidPoint(float* inStartPos, float* inEndPos, float* outNonSolidPoint); +bool AvHSHUGetCenterPositionForGroup(int inGroupNumber, float* inPlayerOrigin, float* outCenterPosition); + +#ifdef AVH_CLIENT +////#include "cl_dll/cl_dll.h" +////#include "cl_dll/hud.h" +//#include "cl_dll/cl_util.h" +//#include "cl_dll/cl_dll.h" +#include "cl_dll/eventscripts.h" +#endif + +float PM_GetDesiredTopDownCameraHeight(qboolean& outFoundEntity); +qboolean PM_CanWalljump(); +qboolean PM_CanFlap(); +void PM_Overwatch(); +bool PM_TopDown(); +void PM_Jump(void); +void PM_PreventMegaBunnyJumping(bool inIgnoreZ); +bool GetIsEntityAPlayer(int inPhysIndex); + +#ifdef AVH_CLIENT +#include "pm_shared/pm_debug.h" +#include "..\common\hltv.h" +void PM_DebugLocations(); +typedef vector< DebugPoint > PositionListType; +DebugPointListType gTriDebugLocations; +DebugPointListType gSquareDebugLocations; +DebugEntityListType gCubeDebugEntities; +#endif + +//extern int gWallJumpEventID; +//extern int gFlightEventID; + +extern int gJetpackEventID; +extern int gBlinkEffectSuccessEventID; + +#define Vector vec3_t +#include "mod/AvHSelectionHelper.h" + +int gHeightLevel = 0; + +const float kMarineBackpedalSpeedScalar = .4f; +const float kMarineSidestepSpeedScalar = 1.0f; + +//physent_t* AvHSUGetEntity(int inPhysIndex); + +const AvHMapExtents& GetMapExtents(); + +#ifdef AVH_CLIENT +// Spectator Mode +int iJumpSpectator; +float vJumpOrigin[3]; +float vJumpAngles[3]; +//float gTopDownHeight = 0; + +float gTopDownViewOrigin[3]; +float gTopDownViewAngles[3]; +//int iHasNewViewOrigin = 0; +//int iHasNewViewAngles = 0; +#endif + +static int pm_shared_initialized = 0; + +//void InterpolateAngles( float *start, float *end, float *output, float frac ); + +#pragma warning( disable : 4305 ) + + +playermove_t *pmove = NULL; + +extern "C" +{ + Vector gPredictedPlayerOrigin; + Vector gPredictedPlayerVOfs; +} + +#ifdef AVH_CLIENT +float gOverwatchTargetRange; +#endif + + +/////////////////////////////// +// Begin Max's Code +/////////////////////////////// + +const float kSkulkRotationRate = (float)(2.5 * M_PI); // Radians per second. +const float kSkulkRotationLookAhead = 75; + +// Distance around the skulk to look each in direction for wallsticking. +const vec3_t kWallstickingDistanceCheck = { 5, 5, 5 }; +// : 0000972 +vec3_t gSurfaceNormal = { 0, 0, 0 }; +bool canWallJump = false; +// : + +#ifdef AVH_SERVER +bool gCanMove[MAX_CLIENTS]; +#else +bool gCanMove; +#endif + +#ifdef AVH_CLIENT +extern vec3_t gPlayerAngles; +extern vec3_t gTargetPlayerAngles; +extern vec3_t gWorldViewAngles; +#endif + +/////////////////////////////// +// End Max's Code +/////////////////////////////// + +// Ducking time +#define TIME_TO_DUCK (0.4 * 1000) +//#define VEC_DUCK_HULL_MIN -18 +//#define VEC_DUCK_HULL_MAX 18 +//#define VEC_DUCK_VIEW 12 +#define PM_DEAD_VIEWHEIGHT -8 +#define MAX_CLIMB_SPEED 120 +#define STUCK_MOVEUP 1 +#define STUCK_MOVEDOWN -1 +//#define VEC_HULL_MIN -36 +//#define VEC_HULL_MAX 36 +//#define VEC_VIEW 28 +#define STOP_EPSILON 0.1 + +#define CTEXTURESMAX 512 // max number of textures loaded +#define CBTEXTURENAMEMAX 13 // only load first n chars of name + +#define CHAR_TEX_CONCRETE 'C' // texture types +#define CHAR_TEX_METAL 'M' +#define CHAR_TEX_DIRT 'D' +#define CHAR_TEX_VENT 'V' +#define CHAR_TEX_GRATE 'G' +#define CHAR_TEX_TILE 'T' +#define CHAR_TEX_SLOSH 'S' +#define CHAR_TEX_WOOD 'W' +#define CHAR_TEX_COMPUTER 'P' +#define CHAR_TEX_GLASS 'Y' +#define CHAR_TEX_FLESH 'F' + +#define STEP_CONCRETE 0 // default step sound +#define STEP_METAL 1 // metal floor +#define STEP_DIRT 2 // dirt, sand, rock +#define STEP_VENT 3 // ventillation duct +#define STEP_GRATE 4 // metal grating +#define STEP_TILE 5 // floor tiles +#define STEP_SLOSH 6 // shallow liquid puddle +#define STEP_WADE 7 // wading in liquid +#define STEP_LADDER 8 // climbing ladder + +#define PLAYER_FATAL_FALL_SPEED 1024// approx 60 feet +#define PLAYER_MAX_SAFE_FALL_SPEED 580// approx 20 feet +#define DAMAGE_FOR_FALL_SPEED (float) 100 / ( PLAYER_FATAL_FALL_SPEED - PLAYER_MAX_SAFE_FALL_SPEED )// damage per unit per second. +#define PLAYER_MIN_BOUNCE_SPEED 200 +#define PLAYER_FALL_PENALTY_THRESHHOLD (float)200 // If player lands with this much speed or more, slow them down as penalty for jumping +#define PLAYER_FALL_PUNCH_THRESHHOLD (float)350 // won't punch player's screen/make scrape noise unless player falling at least this fast. + +#define PLAYER_LONGJUMP_SPEED 350 // how fast we longjump + +const float kAlienEnergyFlap = .025f; + +// double to float warning +#pragma warning(disable : 4244) +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#define min(a, b) (((a) < (b)) ? (a) : (b)) +// up / down +#define PITCH 0 +// left / right +#define YAW 1 +// fall over +#define ROLL 2 + +#define MAX_CLIENTS 32 + +#define CONTENTS_CURRENT_0 -9 +#define CONTENTS_CURRENT_90 -10 +#define CONTENTS_CURRENT_180 -11 +#define CONTENTS_CURRENT_270 -12 +#define CONTENTS_CURRENT_UP -13 +#define CONTENTS_CURRENT_DOWN -14 + +#define CONTENTS_TRANSLUCENT -15 + +static vec3_t rgv3tStuckTable[54]; +static int rgStuckLast[MAX_CLIENTS][2]; + +// Texture names +static int gcTextures = 0; +static char grgszTextureName[CTEXTURESMAX][CBTEXTURENAMEMAX]; +static char grgchTextureType[CTEXTURESMAX]; + +int g_onladder[MAX_CLIENTS]; +bool gIsJetpacking[MAX_CLIENTS]; + + +// Borrowed from Quake1. + +static hull_t box_hull; +static dclipnode_t box_clipnodes[6]; +static mplane_t box_planes[6]; + + +// : 243 +// Players should never play sounds when digesting +void PM_NSPlaySound( int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch ) { + if ( AvHGetIsAlien(pmove->iuser3) || !GetHasUpgrade(pmove->iuser4, MASK_DIGESTING) ) + {pmove->PM_PlaySound(channel,sample,volume,attenuation,fFlags,pitch);} +} + +bool PM_GetIsBlinking() +{ + if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4 && GetHasUpgrade(pmove->iuser4, MASK_ALIEN_MOVEMENT)) + return true; + + if (pmove->cmd.weaponselect == 255 || pmove->flags & FL_FAKECLIENT) + { + return (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4) && (pmove->cmd.impulse == ALIEN_ABILITY_BLINK); + } + return false; +} + +bool PM_GetIsLeaping() +{ + if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1 && GetHasUpgrade(pmove->iuser4, MASK_ALIEN_MOVEMENT)) + return true; + + if (pmove->cmd.weaponselect == 255 || pmove->flags & FL_FAKECLIENT) + { + return (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1) && (pmove->cmd.impulse == ALIEN_ABILITY_LEAP); + } + return false; +} + +bool PM_GetIsCharging() +{ + if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5 && GetHasUpgrade(pmove->iuser4, MASK_ALIEN_MOVEMENT)) + return true; + + if (pmove->cmd.weaponselect == 255 || pmove->flags & FL_FAKECLIENT) + { + return (pmove->cmd.impulse == ALIEN_ABILITY_CHARGE); + } + return false; +} + +/* +=================== +PM_InitBoxHull + +Set up the planes and clipnodes so that the six floats of a bounding box +can just be stored out and get a proper hull_t structure. +=================== +*/ +void PM_InitBoxHull (void) +{ + int i; + int side; + + box_hull.clipnodes = box_clipnodes; + box_hull.planes = box_planes; + box_hull.firstclipnode = 0; + box_hull.lastclipnode = 5; + + for (i=0 ; i<6 ; i++) + { + box_clipnodes[i].planenum = i; + + side = i&1; + + box_clipnodes[i].children[side] = CONTENTS_EMPTY; + if (i != 5) + box_clipnodes[i].children[side^1] = i + 1; + else + box_clipnodes[i].children[side^1] = CONTENTS_SOLID; + + } + +} + + +/* +=================== +PM_HullForBox + +To keep everything totally uniform, bounding boxes are turned into small +BSP trees instead of being compared directly. +=================== +*/ +hull_t* PM_HullForBox (vec3_t mins, vec3_t maxs) +{ + + for (int i=0 ; i<6 ; i++) + { + + box_planes[i].type = i>>1; + + box_planes[i].normal[0] = 0; + box_planes[i].normal[1] = 0; + box_planes[i].normal[2] = 0; + box_planes[i].normal[i>>1] = 1; + + } + + box_planes[0].dist = maxs[0]; + box_planes[1].dist = mins[0]; + box_planes[2].dist = maxs[1]; + box_planes[3].dist = mins[1]; + box_planes[4].dist = maxs[2]; + box_planes[5].dist = mins[2]; + + return &box_hull; +} + + +void NS_DrawBoundingBox(const vec3_t mins, + const vec3_t maxs, + const vec3_t inEntityOrigin, + const vec3_t inEntityAngles) +{ + +#ifdef AVH_SERVER + + void PM_DrawRectangle(vec3_t tl, vec3_t bl, vec3_t tr, vec3_t br, int pcolor, float life); + extern int PM_boxpnt[6][4]; + + int pcolor = 132; + float plife = 0.1; + + vec3_t tmp; + vec3_t p[8]; + float gap = 0; + int j; + + for (j = 0; j < 8; j++) + { + tmp[0] = (j & 1) ? mins[0] - gap : maxs[0] + gap; + tmp[1] = (j & 2) ? mins[1] - gap : maxs[1] + gap; + tmp[2] = (j & 4) ? mins[2] - gap : maxs[2] + gap; + + VectorCopy(tmp, p[j]); + } + + // If the bbox should be rotated, do that + vec3_t forward, right, up; + + AngleVectorsTranspose(inEntityAngles, forward, right, up); + for (j = 0; j < 8; j++) + { + VectorCopy(p[j], tmp); + p[j][0] = DotProduct ( tmp, forward ); + p[j][1] = DotProduct ( tmp, right ); + p[j][2] = DotProduct ( tmp, up ); + } + + // Offset by entity origin, if any. + for (j = 0; j < 8; j++) + VectorAdd(p[j], inEntityOrigin, p[j]); + + for (j = 0; j < 6; j++) + { + PM_DrawRectangle( + p[PM_boxpnt[j][1]], + p[PM_boxpnt[j][0]], + p[PM_boxpnt[j][2]], + p[PM_boxpnt[j][3]], + pcolor, plife); + } + +#endif + +} + + +void NS_DrawBoundingBox(const OBBox& inBox) +{ + + vec3_t theAngles; + VectorsToAngles(inBox.mAxis[1], inBox.mAxis[0], inBox.mAxis[2], theAngles); + + vec3_t theMin; + VectorScale(inBox.mExtents, -1, theMin); + + vec3_t theMax; + VectorCopy(inBox.mExtents, theMax); + + NS_DrawBoundingBox(theMin, theMax, inBox.mOrigin, theAngles); + +} + + + +/** + * Computes the hull for an oriented bounding box. + */ +hull_t* NS_HullForBox(vec3_t inEntityMins, vec3_t inEntityMaxs, + vec3_t inEntityAngles, vec3_t inPlayerExtents) +{ + + float thePlayerRadius = max(inPlayerExtents[0], inPlayerExtents[1]); + + // Players "feel" too large so artificially shrink them relative to the + // structures. + thePlayerRadius *= 0.8; + + vec3_t axis[3]; + AngleVectors(inEntityAngles, axis[0], axis[1], axis[2]); + + // For some reason the y-axis is negated so flip it. + + axis[1][0] = -axis[1][0]; + axis[1][1] = -axis[1][1]; + axis[1][2] = -axis[1][2]; + + for (int i = 0; i < 3; ++i) + { + + int p1 = i * 2; + int p2 = p1 + 1; + + vec3_t p; + + // Compute the offset the planes to account for the size of the player. + + /* + float offset = fabs(inPlayerExtents[0] * axis[i][0]) + + fabs(inPlayerExtents[1] * axis[i][1]) + + fabs(inPlayerExtents[2] * axis[i][2]); + */ + + // Use a cylindrical player instead of a square one. + float offset = thePlayerRadius; + + // Positive plane. + + VectorCopy(axis[i], box_planes[p1].normal); + VectorScale(box_planes[p1].normal, inEntityMaxs[i], p); + + box_planes[p1].dist = DotProduct(p, box_planes[p1].normal) + offset; + box_planes[p1].type = 3; // Not axis aligned. + + // Negative plane. + + VectorCopy(axis[i], box_planes[p2].normal); + VectorScale(box_planes[p2].normal, inEntityMins[i], p); + + box_planes[p2].dist = DotProduct(p, box_planes[p2].normal) - offset; + box_planes[p2].type = 3; // Not axis aligned. + + } + + return &box_hull; + +} + + +bool NS_GetCollisionSizeForUser3(AvHUser3 inUser3, vec3_t outMinSize, vec3_t outMaxSize) +{ + + switch (inUser3) + { + + case AVH_USER3_COMMANDER_STATION: + outMinSize[0] = -45; + outMinSize[1] = -16; + outMinSize[2] = 0; + outMaxSize[0] = 45; + outMaxSize[1] = 16; + outMaxSize[2] = 70.34; + return true; + + case AVH_USER3_INFANTRYPORTAL: + outMinSize[0] = -25; + outMinSize[1] = -25; + outMinSize[2] = 0; + outMaxSize[0] = 25; + outMaxSize[1] = 25; + outMaxSize[2] = 5; + return true; + + case AVH_USER3_PHASEGATE: + outMinSize[0] = -55; + outMinSize[1] = -40; + outMinSize[2] = 0; + outMaxSize[0] = 40; + outMaxSize[1] = 50; + outMaxSize[2] = 14.49; + return true; + + case AVH_USER3_ARMORY: + case AVH_USER3_ADVANCED_ARMORY: + outMinSize[0] = -20; + outMinSize[1] = -20; + outMinSize[2] = 0; + outMaxSize[0] = 20; + outMaxSize[1] = 20; + outMaxSize[2] = 62.1931; + return true; + + case AVH_USER3_ARMSLAB: + outMinSize[0] = -30; + outMinSize[1] = -20; + outMinSize[2] = 0; + outMaxSize[0] = 30; + outMaxSize[1] = 20; + outMaxSize[2] = 60; + return true; + + case AVH_USER3_PROTOTYPE_LAB: + outMinSize[0] = -16; + outMinSize[1] = -16; + outMinSize[2] = 0; + outMaxSize[0] = 16; + outMaxSize[1] = 16; + outMaxSize[2] = 67.7443; + return true; + + case AVH_USER3_OBSERVATORY: + outMinSize[0] = -16; + outMinSize[1] = -16; + outMinSize[2] = 0; + outMaxSize[0] = 16; + outMaxSize[1] = 16; + outMaxSize[2] = 80.7443; + return true; + + /* + case AVH_USER3_RESTOWER: + outMinSize[0] = -35; + outMinSize[1] = -35; + outMinSize[2] = 0; + outMaxSize[0] = 35; + outMaxSize[1] = 35; + outMaxSize[2] = 60; + return true; + */ + + case AVH_USER3_TURRET_FACTORY: + case AVH_USER3_ADVANCED_TURRET_FACTORY: + outMinSize[0] = -40; + outMinSize[1] = -25; + outMinSize[2] = 0; + outMaxSize[0] = 40; + outMaxSize[1] = 25; + outMaxSize[2] = 50; + return true; + + case AVH_USER3_TURRET: + outMinSize[0] = -16; + outMinSize[1] = -16; + outMinSize[2] = 0; + outMaxSize[0] = 16; + outMaxSize[1] = 16; + outMaxSize[2] = 42; + return true; + + case AVH_USER3_SIEGETURRET: + outMinSize[0] = -16; + outMinSize[1] = -16; + outMinSize[2] = 0; + outMaxSize[0] = 16; + outMaxSize[1] = 16; + outMaxSize[2] = 62.1931; + return true; + + /* + case AVH_USER3_ALIENRESTOWER: + outMinSize[0] = -30; + outMinSize[1] = -35; + outMinSize[2] = 0; + outMaxSize[0] = 30; + outMaxSize[1] = 75; + outMaxSize[2] = 100; + return true; + + case AVH_USER3_OFFENSE_CHAMBER: + case AVH_USER3_DEFENSE_CHAMBER: + case AVH_USER3_SENSORY_CHAMBER: + case AVH_USER3_MOVEMENT_CHAMBER: + outMinSize[0] = -16; + outMinSize[1] = -16; + outMinSize[2] = 0; + outMaxSize[0] = 16; + outMaxSize[1] = 16; + outMaxSize[2] = 44; + return true; + + case AVH_USER3_HIVE: + outMinSize[0] = -50; + outMinSize[1] = -80; + outMinSize[2] = -145; + outMaxSize[0] = 50; + outMaxSize[1] = 80; + outMaxSize[2] = 50; + return true; + */ + + } + + return false; + +} + +void NS_GetClosestPlaneNormal(vec3_t inDirection, mplane_t inPlanes[], int inNumPlanes, vec3_t outNormal) +{ + + VectorCopy(inDirection, outNormal); + float best = 0; + + for (int i = 0; i < inNumPlanes; ++i) + { + + float d = fabs(DotProduct(inDirection, inPlanes[i].normal)); + + if (d >= best) + { + VectorCopy(inPlanes[i].normal, outNormal); + best = d; + } + + } + +} + +void NS_TraceLine(float* start, float* end, int hull, int traceFlags, int ignore_pe, bool ignorePlayers, trace_t& result) +{ + + memset(&result, 0, sizeof(trace_t)); + + result.fraction = 1; + result.ent = NULL; + + VectorCopy(end, result.endpos); + + int hullNumber = NS_GetValveHull(hull); + + for (int i = 0; i < pmove->numphysent; ++i) //Elven - why is this ++i? could this be an off by 1 error? + { + if (i != ignore_pe && pmove->physents[i].solid != SOLID_NOT && (!ignorePlayers || !pmove->physents[i].player)) + { + + hull_t* hull = NULL; + bool ignoreRotation = false; + + if (pmove->physents[i].model != NULL) + { + hull = &pmove->physents[i].model->hulls[hullNumber]; + } + else if (!(traceFlags & PM_WORLD_ONLY)) + { + + vec3_t min; + vec3_t max; + + VectorAdd(pmove->physents[i].mins, pmove->player_mins[pmove->usehull], min); + VectorAdd(pmove->physents[i].maxs, pmove->player_maxs[pmove->usehull], max); + + vec3_t offset; + VectorAdd(pmove->physents[i].maxs, pmove->physents[i].mins, offset); + + vec3_t center; + VectorMA(pmove->physents[i].origin, 0.5, offset, center); + + vec3_t theEntityMins; + vec3_t theEntityMaxs; + + if (NS_GetCollisionSizeForUser3((AvHUser3)pmove->physents[i].iuser3, + theEntityMins, theEntityMaxs)) + { + + vec3_t thePlayerExtents; + VectorSubtract(pmove->player_maxs[hullNumber], pmove->player_mins[hullNumber], thePlayerExtents); + VectorScale(thePlayerExtents, 0.5, thePlayerExtents); + + hull = NS_HullForBox(theEntityMins, + theEntityMaxs, + pmove->physents[i].angles, + thePlayerExtents); + + // We've already taken the rotation of the entity into account. + ignoreRotation = true; + + } + else + { + + vec3_t min; + vec3_t max; + + VectorAdd(pmove->physents[i].mins, pmove->player_mins[hullNumber], min); + VectorAdd(pmove->physents[i].maxs, pmove->player_maxs[hullNumber], max); + + hull = PM_HullForBox(min, max); + + } + + } + + if (hull != NULL) + { + + vec3_t localStart; + vec3_t localEnd; + + // Translate the start and end into the model's frame of reference. + + VectorSubtract(start, pmove->physents[i].origin, localStart); + VectorSubtract(end, pmove->physents[i].origin, localEnd); + + // Rotate the start and end into the model's frame of reference. + + bool rotated; + + if (pmove->physents[i].angles[0] || + pmove->physents[i].angles[1] || + pmove->physents[i].angles[2]) + { + rotated = !ignoreRotation; + } + else + { + rotated = false; + } + + vec3_t forward; + vec3_t right; + vec3_t up; + + if (rotated) + { + + AngleVectors(pmove->physents[i].angles, forward, right, up); + + vec3_t temp; + + VectorCopy(localStart, temp); + localStart[0] = DotProduct(temp, forward); + localStart[1] = -DotProduct(temp, right); + localStart[2] = DotProduct(temp, up); + + VectorCopy(localEnd, temp); + localEnd[0] = DotProduct(temp, forward); + localEnd[1] = -DotProduct(temp, right); + localEnd[2] = DotProduct(temp, up); + + } + + trace_t trace; + NS_TraceLine(hull, localStart, localEnd, &trace); + + if (trace.fraction < result.fraction) + { + memcpy(&result, &trace, sizeof(trace_t)); + + if (rotated) + { + + vec3_t invAngles; + + VectorScale(pmove->physents[i].angles, -1, invAngles); + AngleVectors(invAngles, forward, right, up); + + vec3_t temp; + VectorCopy(result.plane.normal, temp); + + result.plane.normal[0] = DotProduct(temp, forward); + result.plane.normal[1] = -DotProduct(temp, right); + result.plane.normal[2] = DotProduct(temp, up); + + // TODO result.plane.dst needs to be updated. + + } + + } + + } + + } + } + + // Compute the end position of the trace. + + result.endpos[0] = start[0] + result.fraction * (end[0] - start[0]); + result.endpos[1] = start[1] + result.fraction * (end[1] - start[1]); + result.endpos[2] = start[2] + result.fraction * (end[2] - start[2]); + +} + +pmtrace_t NS_PlayerTrace(playermove_t* pmove, float* start, float* end, int traceFlags, int ignore_pe) +{ + + //return pmove->PM_PlayerTrace(start, end, traceFlags, ignore_pe); + + pmtrace_t result = { 0 }; + + result.fraction = 1; + result.ent = -1; + + VectorCopy(end, result.endpos); + + int hullNumber = NS_GetValveHull(pmove->usehull); + + + for (int i = 0; i < pmove->numphysent; ++i) //wtf is this again? elven. + { + if (i != ignore_pe && pmove->physents[i].solid != SOLID_NOT) + { + + hull_t* hull = NULL; + bool ignoreRotation = false; + + vec3_t awayDirection; + + if (pmove->physents[i].model != NULL) + { + hull = &pmove->physents[i].model->hulls[hullNumber]; + } + else if (!(traceFlags & PM_WORLD_ONLY)) + { + + + /* + OBBox theBox[50]; + int theNumBoxes; + NS_GetHitBoxesForEntity(pmove->physents[i].info, 50, theBox, theNumBoxes, pmove->time); + + for (int z = 0; z < theNumBoxes; ++z) + { + NS_DrawBoundingBox(theBox[z]); + } + */ + + vec3_t min; + vec3_t max; + + VectorAdd(pmove->physents[i].mins, pmove->player_mins[pmove->usehull], min); + VectorAdd(pmove->physents[i].maxs, pmove->player_maxs[pmove->usehull], max); + + vec3_t offset; + VectorAdd(pmove->physents[i].maxs, pmove->physents[i].mins, offset); + + vec3_t center; + VectorMA(pmove->physents[i].origin, 0.5, offset, center); + + // Check if the player is moving away from the object. If they + // are, don't check for collision. This makes it so that objects + // can still move apart even if they are intersecting. + + vec3_t direction; + VectorSubtract(end, start, direction); + + VectorSubtract(start, center, awayDirection); + VectorNormalize(awayDirection); + + vec3_t theEntityMins; + vec3_t theEntityMaxs; + + if (NS_GetCollisionSizeForUser3((AvHUser3)pmove->physents[i].iuser3, + theEntityMins, theEntityMaxs)) + { + + vec3_t thePlayerExtents; + VectorSubtract(pmove->player_maxs[pmove->usehull], pmove->player_mins[pmove->usehull], thePlayerExtents); + VectorScale(thePlayerExtents, 0.5, thePlayerExtents); + + hull = NS_HullForBox(theEntityMins, + theEntityMaxs, + pmove->physents[i].angles, + thePlayerExtents); + + /* + NS_DrawBoundingBox(theEntityMins, + theEntityMaxs, + pmove->physents[i].origin, + pmove->physents[i].angles); + */ + + + // We've already taken the rotation of the entity into account. + ignoreRotation = true; + + } + else + { + + vec3_t min; + vec3_t max; + + VectorAdd(pmove->physents[i].mins, pmove->player_mins[pmove->usehull], min); + VectorAdd(pmove->physents[i].maxs, pmove->player_maxs[pmove->usehull], max); + + hull = PM_HullForBox(min, max); + + } + + vec3_t localStart; + VectorSubtract(start, pmove->physents[i].origin, localStart); + + if (NS_PointContents(hull, 0, localStart) == CONTENTS_SOLID) + { + // If the player is stuck but trying to move away, let them. + if (DotProduct(direction, awayDirection) >= 0) + { + hull = NULL; + } + } + + } + + if (hull != NULL) + { + + vec3_t localStart; + vec3_t localEnd; + + // Translate the start and end into the model's frame of reference. + + VectorSubtract(start, pmove->physents[i].origin, localStart); + VectorSubtract(end, pmove->physents[i].origin, localEnd); + + // Rotate the start and end into the model's frame of reference. + + bool rotated; + + if (pmove->physents[i].model == NULL) + { + rotated = false; + } + else + { + if (pmove->physents[i].angles[0] || + pmove->physents[i].angles[1] || + pmove->physents[i].angles[2]) + { + rotated = !ignoreRotation; + } + else + { + rotated = false; + } + } + + vec3_t forward; + vec3_t right; + vec3_t up; + + if (rotated) + { + + AngleVectors(pmove->physents[i].angles, forward, right, up); + + vec3_t temp; + + VectorCopy(localStart, temp); + localStart[0] = DotProduct(temp, forward); + localStart[1] = -DotProduct(temp, right); + localStart[2] = DotProduct(temp, up); + + VectorCopy(localEnd, temp); + localEnd[0] = DotProduct(temp, forward); + localEnd[1] = -DotProduct(temp, right); + localEnd[2] = DotProduct(temp, up); + + } + + trace_t trace; + NS_TraceLine(hull, localStart, localEnd, &trace); + + if (trace.fraction < result.fraction) + { + + result.allsolid = trace.allsolid; + result.startsolid = trace.startsolid; + result.inopen = trace.inopen; + result.inwater = trace.inwater; + result.fraction = trace.fraction; + result.ent = i; + result.hitgroup = trace.hitgroup; + + if (pmove->physents[i].model == NULL && result.allsolid) + { + + // We don't have a normal, so use the plane normal + // closest to the away direction. + + NS_GetClosestPlaneNormal(awayDirection, box_planes, 6, result.plane.normal); + result.plane.dist = 0; + + //VectorCopy(awayDirection, result.plane.normal); + + result.allsolid = false; + + } + else + { + VectorCopy(trace.plane.normal, result.plane.normal); + result.plane.dist = trace.plane.dist; + } + + if (rotated) + { + + vec3_t invAngles; + + VectorScale(pmove->physents[i].angles, -1, invAngles); + AngleVectors(invAngles, forward, right, up); + + vec3_t temp; + VectorCopy(result.plane.normal, temp); + + result.plane.normal[0] = DotProduct(temp, forward); + result.plane.normal[1] = -DotProduct(temp, right); + result.plane.normal[2] = DotProduct(temp, up); + + // TODO result.plane.dst needs to be updated. + + } + + } + + } + + } + } + + // Compute the end position of the trace. + + result.endpos[0] = start[0] + result.fraction * (end[0] - start[0]); + result.endpos[1] = start[1] + result.fraction * (end[1] - start[1]); + result.endpos[2] = start[2] + result.fraction * (end[2] - start[2]); + + // Compute the delta velocity + + if (result.fraction < 1) + { + + float s = DotProduct(result.plane.normal, pmove->velocity); + + vec3_t antiVelocity; + VectorScale(result.plane.normal, s, antiVelocity); + + VectorSubtract(pmove->velocity, antiVelocity, result.deltavelocity); + + } + + return result; + +} + +int NS_TestPlayerPosition(playermove_t* pmove, float* origin, pmtrace_t* trace) +{ + + //return pmove->PM_TestPlayerPosition(origin, trace); + + pmtrace_t tempTrace = NS_PlayerTrace(pmove, origin, origin, PM_WORLD_ONLY, -1); + + if (trace != NULL) + { + memcpy(trace, &tempTrace, sizeof(pmtrace_t)); + } + + return tempTrace.ent; + +} + + + +//#ifdef AVH_CLIENT +void PM_DebugLocations(int theRandomNumber) +{ +#ifdef AVH_CLIENT + gTriDebugLocations.clear(); +#endif + + //int theNumEnts = pmove->numphysent; + int theNumEnts = pmove->numvisent; + + //if(pmove->server) + //{ + // pmove->Con_Printf("DEBUGLOCATIONS (server, %d ents):\n", theNumEnts); + //} + //else + //{ + // pmove->Con_Printf("DEBUGLOCATIONS (client, %d ents):\n", theNumEnts); + //} + + physent_t* theEntity = NULL; + for (int i = 0; i < theNumEnts; i++) + { + //theEntity = pmove->physents + i;; + theEntity = pmove->visents + i;; + if(theEntity)// && (theEntity->info >= 1) && (theEntity->info <= 16)) + { + vec3_t theEntityOrigin; + VectorCopy(theEntity->origin, theEntityOrigin); + + //if(pmove->iuser3 == AVH_USER3_FUNC_RESOURCE) + //{ + // pmove->Con_Printf(" - entnumber(%d) iuser3 (%d) %d origin(%f, %f, %f) mins(%f, %f, %f) maxs(%f %f %f)\n", theEntity->info, pmove->iuser3, theEntityOrigin[0], theEntityOrigin[1], theEntityOrigin[2], theEntity->mins[0], theEntity->mins[1], theEntity->mins[2], theEntity->maxs[0], theEntity->maxs[1], theEntity->maxs[2]); + //} + //pmove->Con_Printf("mins(%f, %f, %f) maxs(%f %f %f)\n", i, theEntityOrigin[0], theEntityOrigin[1], theEntityOrigin[2]); + +#ifdef AVH_CLIENT + DebugPoint thePoint; + thePoint.x = theEntityOrigin[0]; + thePoint.y = theEntityOrigin[1]; + thePoint.z = theEntityOrigin[2]; + + gTriDebugLocations.push_back(thePoint); +#endif + +#ifdef AVH_SERVER + AvHSUAddDebugPoint(theEntityOrigin[0], theEntityOrigin[1], theEntityOrigin[2]); +#endif + // float theOriginX = (theEntity->maxs.x + theEntity->mins.x)/2.0f; + // float theOriginY = (theEntity->maxs.y + theEntity->mins.y)/2.0f; + // float theOriginZ = (theEntity->maxs.z + theEntity->mins.z)/2.0f; + //DrawCircleOnGroundAtPoint(theEntityOrigin, 4, 0, 12, 1, 1, 0, .5f); + } + } + pmove->Con_Printf("\n"); + + // if(theRandomNumber < 10) + // { + // pmove->Con_Printf("Using hull: %d\n", pmove->usehull); + // } +} +//#endif + +void PM_GetWishVelocity(vec3_t& outWishVelocity) +{ + VectorNormalize(pmove->forward); + VectorNormalize(pmove->right); + VectorNormalize(pmove->up); + + float fmove = pmove->cmd.forwardmove; + float smove = pmove->cmd.sidemove; + float umove = pmove->cmd.upmove; + + for(int i=0 ; i<3 ; i++) + { + outWishVelocity[i] = pmove->forward[i]*fmove + pmove->right[i]*smove + pmove->up[i]*umove; + } +} + +bool NS_CheckOffsetFromOrigin(float inX, float inY, float inZ, vec3_t& outNormal) +{ + + /////////////////////////////// + // Begin Max's Code + /////////////////////////////// + + bool thePointIsSolid = false; + + const float kScalar = 1.3f; + + vec3_t thePoint; + + thePoint[0] = pmove->origin[0] + inX*kScalar; + thePoint[1] = pmove->origin[1] + inY*kScalar; + thePoint[2] = pmove->origin[2] + inZ*kScalar; + + // Trace against the world geometry. + + trace_t trace; + NS_TraceLine(pmove->origin, thePoint, 1, PM_WORLD_ONLY, -1, true, trace); + + if (trace.fraction < 1) + { + // : Don't weight it if surface normal is too plane + if (trace.plane.normal[2] > 0.7) + return true; + + // Weight the normal based on how close the intersection is. + + vec3_t normal; + VectorScale(trace.plane.normal, 1.0f / (trace.fraction + 0.1f), normal); + VectorAdd(normal, outNormal, outNormal); + + thePointIsSolid = true; + + #ifdef AVH_SERVER + //PM_ParticleLine(pmove->origin, thePoint, 63, 0.1, 5.0); + #endif + + } + else + { + #ifdef AVH_SERVER + //PM_ParticleLine(pmove->origin, thePoint, 0, 0.1, 5.0); + #endif + } + + // If there was no intersection with the world, trace the entities. + + // We prioritize world tracing over entity tracing so that small entities (like + // IPs, resource nozzles and switches) don't cause changes in the skulk's orientation. + + if (!thePointIsSolid) + { + + const int pointSizeHull = 2; + + trace_t trace; + NS_TraceLine(pmove->origin, thePoint, pointSizeHull, 0, -1, true, trace); + + /* + struct pmtrace_s* theTrace = pmove->PM_TraceLine(pmove->origin, thePoint, + PM_TRACELINE_ANYVISIBLE, pointSizeHull, pmove->player_index); + */ + + //if (theTrace && (theTrace->fraction < 1) && !GetIsEntityAPlayer(theTrace->ent)) + if (trace.fraction < 1) + { + + // Add the normal so that we compute an average normal over all tests. + // Weight the normal based on how close the intersection is. + + vec3_t normal; + + //VectorScale(theTrace->plane.normal, 1.0f / (theTrace->fraction + 0.1f), normal); + VectorScale(trace.plane.normal, 1.0f / (trace.fraction + 0.1f), normal); + VectorAdd(normal, outNormal, outNormal); + + thePointIsSolid = true; + + #ifdef AVH_SERVER + //PM_ParticleLine(pmove->origin, thePoint, 79, 0.1, 5.0); + #endif + + } + + } + + return thePointIsSolid; + + /////////////////////////////// + // End Max's Code + /////////////////////////////// + +} + +void NS_GetWallstickingAngles(const vec3_t surfaceNormal, const vec3_t currentAngles, vec3_t angles) +{ + + vec3_t forward; + vec3_t right; + vec3_t up; + + VectorCopy(surfaceNormal, up); + AngleVectors(currentAngles, forward, NULL, NULL); + + CrossProduct(forward, up, right); + CrossProduct(up, right, forward); + + VectorNormalize(forward); + VectorNormalize(right); + VectorNormalize(up); + + VectorsToAngles(forward, right, up, angles); + +} + +void NS_FixWallstickingAngles(vec3_t angles) +{ + + const float minValue = -1000; + const float maxValue = 1000; + + if (!(angles[0] > minValue && angles[0] < maxValue) || + !(angles[1] > minValue && angles[1] < maxValue) || + !(angles[2] > minValue && angles[2] < maxValue)) + { + + // The angles have become invalid, so reset them. + + angles[0] = 0; + angles[1] = 0; + angles[2] = 0; + + } + +} + +void PM_ParticleAxes(vec3_t origin, vec3_t angles) +{ + + + vec3_t forward; + vec3_t right; + vec3_t up; + + AngleVectors(angles, forward, right, up); + + VectorScale(forward, 100, forward); + VectorScale(right, 100, right); + VectorScale(up, 100, up); + + VectorAdd(origin, forward, forward); + VectorAdd(origin, right, right); + VectorAdd(origin, up, up); + + PM_ParticleLine(origin, forward, 63, 0.1, 2.0); + PM_ParticleLine(origin, right, 79, 0.1, 2.0); + PM_ParticleLine(origin, up, 15, 0.1, 2.0); + +} + + +void NS_UpdateWallsticking() +{ + canWallJump = true; + + SetUpgradeMask(&pmove->iuser4, MASK_WALLSTICKING, false); + + //pmove->effects &= ~EF_INVLIGHT; + + if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1) + { + // : 0000972 + pmove->waterjumptime -= pmove->cmd.msec; + if (pmove->waterjumptime < 0) + pmove->waterjumptime = 0; + + if(!(pmove->cmd.buttons & IN_DUCK) && !(pmove->oldbuttons & IN_JUMP) && (pmove->waterlevel < 2)) +// if(!(pmove->cmd.buttons & IN_DUCK)) //&& ((pmove->onground != -1) || (pmove->numtouch > 0))) + // : + { + + vec3_t theMinPoint; + vec3_t theMaxPoint; + + // TODO: SCALE BY FPS DEPENDANCY + VectorScale(kWallstickingDistanceCheck, -1, theMinPoint); + VectorCopy(kWallstickingDistanceCheck, theMaxPoint); + + // Trace corners of hull to see if any are touching a solid and + // compute an average surface normal. + + vec3_t theSurfaceNormal = { 0, 0, 0 }; + + bool wallsticking = false; + + // : fix to allow skulks to climb walls oriented 45 degrees in the x-y plane + // it also allows for easier climbing around courners + //wallsticking |= NS_CheckOffsetFromOrigin(pmove->forward[0], pmove->forward[1], pmove->forward[2], theSurfaceNormal); + //if (wallsticking) + // VectorScale(theSurfaceNormal, 5, theSurfaceNormal); + // : + + //wallsticking |= NS_CheckOffsetFromOrigin(theMinPoint[0], theMinPoint[1], theMinPoint[2], theSurfaceNormal); + //wallsticking |= NS_CheckOffsetFromOrigin(theMinPoint[0], theMaxPoint[1], theMinPoint[2], theSurfaceNormal); + //wallsticking |= NS_CheckOffsetFromOrigin(theMaxPoint[0], theMinPoint[1], theMinPoint[2], theSurfaceNormal); + //wallsticking |= NS_CheckOffsetFromOrigin(theMaxPoint[0], theMaxPoint[1], theMinPoint[2], theSurfaceNormal); + + wallsticking |= NS_CheckOffsetFromOrigin(theMinPoint[0], theMinPoint[1], theMaxPoint[2], theSurfaceNormal); + wallsticking |= NS_CheckOffsetFromOrigin(theMinPoint[0], theMaxPoint[1], theMaxPoint[2], theSurfaceNormal); + wallsticking |= NS_CheckOffsetFromOrigin(theMaxPoint[0], theMinPoint[1], theMaxPoint[2], theSurfaceNormal); + wallsticking |= NS_CheckOffsetFromOrigin(theMaxPoint[0], theMaxPoint[1], theMaxPoint[2], theSurfaceNormal); + + VectorNormalize(theSurfaceNormal); + if (wallsticking) + { + // : 0000972 + if (pmove->waterjumptime == 0) + { + //float dotNormalView = DotProduct(pmove->forward, theSurfaceNormal); + vec3_t tempNormal = {0, 0, 0}; + bool checkedDownOffset = NS_CheckOffsetFromOrigin(0, 0, theMinPoint[2], tempNormal); + VectorNormalize(tempNormal); + if ((checkedDownOffset == false) || (tempNormal[2] < 0.7f && tempNormal[2] > 0.05f)) + { + VectorCopy(theSurfaceNormal, gSurfaceNormal); + + if (pmove->cmd.buttons & IN_WALK) + { + vec3_t theDispVect; + VectorScale(theSurfaceNormal, theMinPoint[0], theDispVect); + VectorAdd(pmove->origin, theDispVect, theDispVect); + + pmtrace_t wallclimbTrace = NS_PlayerTrace(pmove, pmove->origin, theDispVect, PM_NORMAL, -1); + + if (wallclimbTrace.fraction < 1) + { + VectorCopy(wallclimbTrace.endpos, pmove->origin); + } + } + } + else + { + canWallJump = false; + } + + // Set wall sticking to true. + SetUpgradeMask(&pmove->iuser4, MASK_WALLSTICKING); + } + // : + + vec3_t angles; + + #ifdef AVH_SERVER + + vec3_t worldViewAngles; + + Mat3 objectMatrix(pmove->vuser1); + Mat3 viewMatrix(pmove->angles); + + (objectMatrix * viewMatrix).GetEulerAngles(worldViewAngles); + + NS_GetWallstickingAngles(theSurfaceNormal, pmove->angles, angles); + VectorCopy(angles, pmove->vuser2); + + #endif + + #ifdef AVH_CLIENT + NS_GetWallstickingAngles(theSurfaceNormal, gWorldViewAngles, angles); + VectorCopy(angles, gTargetPlayerAngles); + #endif + + } + else + { + + // This seems like a good idea, but it doesn't work too well in + // practice (maybe the constant just need to be tweaked). + + /* + // If the Skulk is not wallsticking, then rotate to align with the + // surface he's moving towards (if there's one nearby). + + vec3_t traceEnd; + vec3_t traceDir; + + VectorCopy(pmove->velocity, traceDir); + VectorNormalize(traceDir); + + VectorScale(traceDir, kSkulkRotationLookAhead, traceDir); + VectorAdd(traceDir, pmove->origin, traceEnd); + + pmtrace_t trace = NS_PlayerTrace(pmove, pmove->origin, traceEnd, PM_NORMAL, -1 ); + + if (trace.fraction < 1) + { + + vec3_t angles; + + #ifdef AVH_SERVER + NS_GetWallstickingAngles(trace.plane.normal, pmove->vuser1, angles); + VectorCopy(angles, pmove->vuser2); + #endif + + #ifdef AVH_CLIENT + NS_GetWallstickingAngles(trace.plane.normal, gWorldViewAngles, angles); + VectorCopy(angles, gTargetPlayerAngles); + #endif + } + */ + + // When the player isn't wall sticking, just revert to the normal + // angles. + + #ifdef AVH_SERVER + VectorCopy(pmove->angles, pmove->vuser2); + #endif + + #ifdef AVH_CLIENT + VectorCopy(pmove->angles, gTargetPlayerAngles); + #endif + + } + + // These buttons have changed this frame + int theButtonsChanged = (pmove->oldbuttons ^ pmove->cmd.buttons); + + // The changed ones still down are "pressed" + //int theButtonPressed = theButtonsChanged & pmove->cmd.buttons; + + // If we're now wallsticking and we JUST pressed our duck button, slow our velocity + if(GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && (theButtonsChanged & IN_DUCK)) + { + //pmove->Con_Printf("Setting vel to 0\n"); + VectorScale(pmove->velocity, .999f, pmove->velocity); + //pmove->velocity[0] = pmove->velocity[1] = pmove->velocity[2] = 0.0f; + + // Trigger footstep immediately + pmove->flTimeStepSound = 0.0f; + } + } + else + { + #ifdef AVH_SERVER + //VectorCopy(pmove->angles, pmove->vuser2); + + vec3_t up = { 0, 0, 1 }; + NS_GetWallstickingAngles(up, pmove->angles, pmove->vuser2); + #endif + } + + } + +} + + +int NS_GetStepsize(int iuser3) +{ + // TODO: Move this into a server variable? + const int kDefaultStepsize = 18; + int theStepSize = kDefaultStepsize; + + //if((pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1) || (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER2)) + //{ + // theStepSize = kDefaultStepsize;//.66f*kDefaultStepsize; + //} + //else if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4) + //{ + // theStepSize = 1.3f*kDefaultStepsize; + //} + //else if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5) + //{ + // theStepSize = 1.6*kDefaultStepsize; + //} + //if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5) + //{ + // theStepSize = 1.3f*kDefaultStepsize; + //} + + return theStepSize; +} + + +void PM_SwapTextures( int i, int j ) +{ + char chTemp; + char szTemp[ CBTEXTURENAMEMAX ]; + + strcpy( szTemp, grgszTextureName[ i ] ); + chTemp = grgchTextureType[ i ]; + + strcpy( grgszTextureName[ i ], grgszTextureName[ j ] ); + grgchTextureType[ i ] = grgchTextureType[ j ]; + + strcpy( grgszTextureName[ j ], szTemp ); + grgchTextureType[ j ] = chTemp; +} + +void PM_SortTextures( void ) +{ + // Bubble sort, yuck, but this only occurs at startup and it's only 512 elements... + // + int i, j; + + for ( i = 0 ; i < gcTextures; i++ ) + { + for ( j = i + 1; j < gcTextures; j++ ) + { + if ( stricmp( grgszTextureName[ i ], grgszTextureName[ j ] ) > 0 ) + { + // Swap + // + PM_SwapTextures( i, j ); + } + } + } +} + +void PM_InitTextureTypes() +{ + char buffer[512]; + int i, j; + byte *pMemFile; + int fileSize, filePos; + static qboolean bTextureTypeInit = false; + + if ( bTextureTypeInit ) + return; + + memset(&(grgszTextureName[0][0]), 0, CTEXTURESMAX * CBTEXTURENAMEMAX); + memset(grgchTextureType, 0, CTEXTURESMAX); + + gcTextures = 0; + memset(buffer, 0, 512); + + fileSize = pmove->COM_FileSize( "sound/materials.txt" ); + pMemFile = pmove->COM_LoadFile( "sound/materials.txt", 5, NULL ); + if ( !pMemFile ) + return; + + filePos = 0; + // for each line in the file... + while ( pmove->memfgets( pMemFile, fileSize, &filePos, buffer, 511 ) != NULL && (gcTextures < CTEXTURESMAX) ) + { + // skip whitespace + i = 0; + while(buffer[i] && isspace(buffer[i])) + i++; + + if (!buffer[i]) + continue; + + // skip comment lines + if (buffer[i] == '/' || !isalpha(buffer[i])) + continue; + + // get texture type + grgchTextureType[gcTextures] = toupper(buffer[i++]); + + // skip whitespace + while(buffer[i] && isspace(buffer[i])) + i++; + + if (!buffer[i]) + continue; + + // get sentence name + j = i; + while (buffer[j] && !isspace(buffer[j])) + j++; + + if (!buffer[j]) + continue; + + // null-terminate name and save in sentences array + j = min (j, CBTEXTURENAMEMAX-1+i); + buffer[j] = 0; + strcpy(&(grgszTextureName[gcTextures++][0]), &(buffer[i])); + } + + // Must use engine to free since we are in a .dll + pmove->COM_FreeFile ( pMemFile ); + + PM_SortTextures(); + + bTextureTypeInit = true; +} + +char PM_FindTextureType( char *name ) +{ + int left, right, pivot; + int val; + + assert( pm_shared_initialized ); + + left = 0; + right = gcTextures - 1; + + while ( left <= right ) + { + pivot = ( left + right ) / 2; + + val = strnicmp( name, grgszTextureName[ pivot ], CBTEXTURENAMEMAX-1 ); + if ( val == 0 ) + { + return grgchTextureType[ pivot ]; + } + else if ( val > 0 ) + { + left = pivot + 1; + } + else if ( val < 0 ) + { + right = pivot - 1; + } + } + + return CHAR_TEX_CONCRETE; +} + +float PM_GetDesiredTopDownCameraHeight(qboolean& outFoundEntity) +{ +// vec3_t theTraceStart; +// vec3_t theTraceEnd; +// vec3_t theForward; +// pmtrace_t* theTrace = NULL; +// physent_t* theTarget = NULL; +// float theHeight = 320;//180; +// qboolean theDone = false; +// int theEntityHit = -1; +// +// // Start tracing at player, but at the highest point in the map +// VectorCopy(pmove->origin, theTraceStart); +// theTraceStart[2] = 750; +// +// VectorCopy(pmove->origin, theTraceEnd); +// theTraceEnd[2] = -500; +// +// //AngleVectors(pmove->angles, theForward, NULL, NULL); +// //VectorNormalize(theForward); +// theForward[0] = theForward[1] = 0; +// theForward[2] = -1; +// +// //VectorMA(pmove->origin, theHeight, theForward, theTraceEnd); +// +// do +// { +// //struct pmtrace_s *(*PM_TraceLine)( float *start, float *end, int flags, int usehull, int ignore_pe ); +// theTrace = pmove->PM_TraceLine(theTraceStart, theTraceEnd, PM_TRACELINE_ANYVISIBLE, 2, theEntityHit); +// +// // Find out if there are any view height entities at our x,y +// theEntityHit = theTrace->ent; +// if(theEntityHit > 0) +// { +// //theTarget = AvHSUGetEntity(theEntityHit); +// for (int i = 0; i < pmove->numphysent; i++) +// { +// if ( pmove->physents[i].info == theEntityHit) +// { +// theTarget = pmove->physents + i; +// } +// } +// } +// +// // If so, pick the nearest one and set our height to it +// if(theTarget && (theTarget->iuser3 == AVH_USER3_VIEWHEIGHT)) +// { +// // Use it's center for the height +// theHeight = (theTarget->maxs[2] + theTarget->mins[2])/2.0f; +// +// outFoundEntity = true; +// +// // We're done +// theDone = true; +// } +// //if(!theTarget) +// //{ +// // theDone = true; +// //} +// +// // Do the trace again but starting at this point +// VectorCopy(theTrace->endpos, theTraceStart); +//// if(theTrace->startsolid) +//// { +//// VectorMA(theTrace->endpos, 10, theForward, theTraceStart); +//// } +// } +// // While the end point is not a view entity and not below +// //while(theTrace->fraction > kFloatTolerance && !theDone); +// while((theTrace->fraction != 1.0f) && !theDone /*&& (!theTrace->startsolid || theTrace->fraction != 0)*/); +// +// // Return the result +// return theHeight; + return 640; +} + +bool NS_GetIsPlayerAlien(string& outExtension, float* outVolume = NULL) +{ + int theUser3 = pmove->iuser3; + + // Default alien extension + bool theIsAlien1 = (theUser3 == AVH_USER3_ALIEN_PLAYER1); + bool theIsAlien2 = (theUser3 == AVH_USER3_ALIEN_PLAYER2); + bool theIsAlien3 = (theUser3 == AVH_USER3_ALIEN_PLAYER3); + bool theIsAlien4 = (theUser3 == AVH_USER3_ALIEN_PLAYER4); + bool theIsAlien5 = (theUser3 == AVH_USER3_ALIEN_PLAYER5); + + if(theIsAlien1) + { + outExtension = kAlien1FootstepExtension; + } + else if(theIsAlien5) + { + outExtension = kAlien5FootstepExtension; + if(outVolume) + { + *outVolume = .3f; + } + } + else if(theIsAlien2 || theIsAlien3 || theIsAlien4) + { + outExtension = kAlienFootstepExtension; + } + + return theIsAlien1 || theIsAlien2 || theIsAlien3 || theIsAlien4 || theIsAlien5; +} + +void NS_PlayStepSound(int inMaterialType, int inSoundNumber, float inVolume) +{ + // Is this an alien step sound? + string theExtension; + bool theIsAlien = NS_GetIsPlayerAlien(theExtension, &inVolume); + + // If we have heavy armor + if((pmove->iuser3 == AVH_USER3_MARINE_PLAYER) && GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_13)) + { + theExtension = kHeavyFootstepExtension; + + // Can't be too quiet with heavy armor + inVolume = max(inVolume, .5f); + } + + // Build base name + string theBaseName; + + // Switch material type + switch(inMaterialType) + { + case STEP_CONCRETE: + theBaseName = kConcreteBaseName; + break; + + case STEP_METAL: + theBaseName = kMetalBaseName; + break; + + case STEP_DIRT: + theBaseName = kDirtBaseName; + break; + + case STEP_VENT: + theBaseName = kDuctBaseName; + break; + + case STEP_GRATE: + theBaseName = kGrateBaseName; + break; + + case STEP_TILE: + theBaseName = kTileBaseName; + break; + + case STEP_SLOSH: + theBaseName = kSloshBaseName; + break; + + case STEP_WADE: + theBaseName = kWadeBaseName; + break; + + case STEP_LADDER: + theBaseName = kLadderBaseName; + break; + } + + // Map random number to indices Valve uses + int theFinalNumber; + switch(inSoundNumber) + { + case 0: + theFinalNumber = 1; + break; + case 1: + theFinalNumber = 3; + break; + case 2: + theFinalNumber = 2; + break; + case 3: + theFinalNumber = 4; + break; + case 4: + theFinalNumber = 5; + break; + } + + // If player is walking, don't play sound + float theWalkFactor = .5f;//AvHMUGetWalkSpeedFactor((AvHUser3)pmove->iuser3); + + int theCurrentSpeed = Length(pmove->velocity);//sqrt( pmove->cmd.forwardmove * pmove->cmd.forwardmove ) + ( pmove->cmd.sidemove * pmove->cmd.sidemove ) + ( pmove->cmd.upmove * pmove->cmd.upmove ); + int theMaxSpeed = pmove->clientmaxspeed; + theMaxSpeed = pmove->maxspeed; + float theSpeedFraction = theCurrentSpeed/(float)theMaxSpeed; + + bool thePlayingFootstep = true; + if(theSpeedFraction > theWalkFactor) + { + // Construct full name using base name and sound number + char theFinalName[128]; + sprintf(theFinalName, "%s%s%d", kFootstepDirectory, theBaseName.c_str(), theFinalNumber); + + //if(theIsAlien && (inMaterialType != STEP_WADE)) + float theNorm = ATTN_NORM; + if((theExtension != "") && (inMaterialType == STEP_CONCRETE)) + { + //theNorm = ATTN_IDLE; + strcat(theFinalName, theExtension.c_str()); + } + strcat(theFinalName, ".wav"); + + // If alien has silencio upgrade, mute footstep volume + float theSilenceUpgradeFactor = 0.0f; + + if(theIsAlien) + { + int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); + theSilenceUpgradeFactor = theSilenceUpgradeLevel/3.0f; + } + + inVolume = inVolume - inVolume*theSilenceUpgradeFactor; + + // Play it at the specified volume + PM_NSPlaySound(CHAN_BODY, theFinalName, inVolume, theNorm, 0, PITCH_NORM); + } + else + { +// pmove->Con_DPrintf("Skipping footstep sound\n"); + thePlayingFootstep = false; + } + +// pmove->Con_DPrintf("current speed: %d, max speed: %d, fraction %f, played footstep: %d\n", theCurrentSpeed, theMaxSpeed, theSpeedFraction, thePlayingFootstep); +} + +void PM_PlayStepSound( int step, float fvol ) +{ + static int iSkipStep = 0; + int irand; + vec3_t hvel; + + pmove->iStepLeft = !pmove->iStepLeft; + + if ( !pmove->runfuncs ) + { + return; + } + + irand = pmove->RandomLong(0,1) + ( pmove->iStepLeft * 2 ); + + // Don't play footsteps when ducked + // FIXME mp_footsteps needs to be a movevar + if((pmove->multiplayer && !pmove->movevars->footsteps) || (pmove->flags & FL_DUCKING)) + { + return; + } + + VectorCopy( pmove->velocity, hvel ); + hvel[2] = 0.0; + + //if ( pmove->multiplayer && ( !g_onladder && Length( hvel ) <= 220 ) ) (use CBasePlayer::GetMaxWalkSpeed somehow if ever uncommenting this) + // return; + + // irand - 0,1 for right foot, 2,3 for left foot + // used to alternate left and right foot + // FIXME, move to player state + + switch (step) + { + default: + case STEP_CONCRETE: + case STEP_METAL: + case STEP_DIRT: + case STEP_VENT: + case STEP_SLOSH: + case STEP_GRATE: + case STEP_WADE: + case STEP_LADDER: + NS_PlayStepSound(step, irand, fvol); + break; + + case STEP_TILE: + if ( !pmove->RandomLong(0,4) ) + irand = 4; + NS_PlayStepSound(step, irand, fvol); + break; + } + +// switch (step) +// { +// default: +// case STEP_CONCRETE: +// switch (irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_step1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_step3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_step2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_step4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// case STEP_METAL: +// switch(irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_metal1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_metal3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_metal2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_metal4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// case STEP_DIRT: +// switch(irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_dirt1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_dirt3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_dirt2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_dirt4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// case STEP_VENT: +// switch(irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_duct1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_duct3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_duct2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_duct4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// case STEP_GRATE: +// switch(irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_grate1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_grate3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_grate2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_grate4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// case STEP_TILE: +// if ( !pmove->RandomLong(0,4) ) +// irand = 4; +// switch(irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_tile1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_tile3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_tile2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_tile4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 4: PM_NSPlaySound( CHAN_BODY, "player/pl_tile5.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// case STEP_SLOSH: +// switch(irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_slosh1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_slosh3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_slosh2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_slosh4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// case STEP_WADE: +// if ( iSkipStep == 0 ) +// { +// iSkipStep++; +// break; +// } +// +// if ( iSkipStep++ == 3 ) +// { +// iSkipStep = 0; +// } +// +// switch (irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_wade1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_wade2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_wade3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_wade4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// case STEP_LADDER: +// switch(irand) +// { +// // right foot +// case 0: PM_NSPlaySound( CHAN_BODY, "player/pl_ladder1.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 1: PM_NSPlaySound( CHAN_BODY, "player/pl_ladder3.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// // left foot +// case 2: PM_NSPlaySound( CHAN_BODY, "player/pl_ladder2.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// case 3: PM_NSPlaySound( CHAN_BODY, "player/pl_ladder4.wav", fvol, ATTN_NORM, 0, PITCH_NORM ); break; +// } +// break; +// } +} + +int PM_MapTextureTypeStepType(char chTextureType) +{ + int theTextureType = 0; + + switch (chTextureType) + { + default: + case CHAR_TEX_CONCRETE: + theTextureType = STEP_CONCRETE; + break; + + case CHAR_TEX_METAL: + theTextureType = STEP_METAL; + break; + + case CHAR_TEX_DIRT: + theTextureType = STEP_DIRT; + break; + + case CHAR_TEX_VENT: + theTextureType = STEP_VENT; + break; + + case CHAR_TEX_GRATE: + theTextureType = STEP_GRATE; + break; + + case CHAR_TEX_TILE: + theTextureType = STEP_TILE; + break; + + case CHAR_TEX_SLOSH: + theTextureType = STEP_SLOSH; + break; + } + + return theTextureType; +} + +/* +==================== +PM_CatagorizeTextureType + +Determine texture info for the texture we are standing on. +==================== +*/ +void PM_CatagorizeTextureType( void ) +{ + vec3_t start, end; + const char *pTextureName; + + VectorCopy( pmove->origin, start ); + VectorCopy( pmove->origin, end ); + + // Straight down + end[2] -= 64; + + // Fill in default values, just in case. + pmove->sztexturename[0] = '\0'; + pmove->chtexturetype = CHAR_TEX_CONCRETE; + + pTextureName = pmove->PM_TraceTexture( pmove->onground, start, end ); + if ( !pTextureName ) + return; + + // strip leading '-0' or '+0~' or '{' or '!' + if (*pTextureName == '-' || *pTextureName == '+') + pTextureName += 2; + + if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') + pTextureName++; + // '}}' + + strcpy( pmove->sztexturename, pTextureName); + pmove->sztexturename[ CBTEXTURENAMEMAX - 1 ] = 0; + + // get texture type + pmove->chtexturetype = PM_FindTextureType( pmove->sztexturename ); +} + +void PM_GetSpeeds(float& outVelWalk, float& outVelRun) +{ +} + +float PM_SetStepInterval() +{ + //int theCurrentSpeed = Length(pmove->velocity); + int theCurrentSpeed = ( pmove->cmd.forwardmove * pmove->cmd.forwardmove ) + + ( pmove->cmd.sidemove * pmove->cmd.sidemove ) + + ( pmove->cmd.upmove * pmove->cmd.upmove ); + theCurrentSpeed = sqrt( (double)theCurrentSpeed ); + + int theMaxSpeed = kMaxGroundPlayerSpeed;//pmove->clientmaxspeed; + +// int kFastestFootstepInterval = BALANCE_VAR(kFootstepFastInterval); +// int kSlowestFootstepInterval = BALANCE_VAR(kFootstepSlowInterval); +// +// if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5) +// { +// kFastestFootstepInterval = 200; +// kSlowestFootstepInterval = 800; +// } + + // Play louder as we get closer to max speed + float theFraction = (float)theCurrentSpeed/theMaxSpeed; + +// int theCurrentInterval = kSlowestFootstepInterval - (kSlowestFootstepInterval - kFastestFootstepInterval)*theFraction; + + int theScalar = 800; + + switch(pmove->iuser3) + { + case AVH_USER3_MARINE_PLAYER: + if(GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_13)) + { + theScalar = BALANCE_VAR(kFootstepHeavyScalar); + } + else + { + theScalar = BALANCE_VAR(kFootstepMarineScalar); + } + break; + case AVH_USER3_ALIEN_PLAYER1: + theScalar = BALANCE_VAR(kFootstepSkulkScalar); + break; + case AVH_USER3_ALIEN_PLAYER2: + theScalar = BALANCE_VAR(kFootstepGorgeScalar); + break; + case AVH_USER3_ALIEN_PLAYER3: + theScalar = BALANCE_VAR(kFootstepLerkScalar); + break; + case AVH_USER3_ALIEN_PLAYER4: + theScalar = BALANCE_VAR(kFootstepFadeScalar); + break; + case AVH_USER3_ALIEN_PLAYER5: + theScalar = BALANCE_VAR(kFootstepOnosScalar); + break; + } + + int theCurrentInterval = max(((float)theScalar/theCurrentSpeed)*100, 200); + + pmove->flTimeStepSound = theCurrentInterval; + + return theFraction; +} + +void PM_SetHulls( void ) +{ + pmove->player_mins[0][0] = HULL0_MINX; + pmove->player_mins[0][1] = HULL0_MINY; + pmove->player_mins[0][2] = HULL0_MINZ; + + pmove->player_maxs[0][0] = HULL0_MAXX; + pmove->player_maxs[0][1] = HULL0_MAXY; + pmove->player_maxs[0][2] = HULL0_MAXZ; + + pmove->player_mins[1][0] = HULL1_MINX; + pmove->player_mins[1][1] = HULL1_MINY; + pmove->player_mins[1][2] = HULL1_MINZ; + + pmove->player_maxs[1][0] = HULL1_MAXX; + pmove->player_maxs[1][1] = HULL1_MAXY; + pmove->player_maxs[1][2] = HULL1_MAXZ; + + pmove->player_mins[2][0] = HULL2_MINX; + pmove->player_mins[2][1] = HULL2_MINY; + pmove->player_mins[2][2] = HULL2_MINZ; + + pmove->player_maxs[2][0] = HULL2_MAXX; + pmove->player_maxs[2][1] = HULL2_MAXY; + pmove->player_maxs[2][2] = HULL2_MAXZ; + + pmove->player_mins[3][0] = HULL3_MINX; + pmove->player_mins[3][1] = HULL3_MINY; + pmove->player_mins[3][2] = HULL3_MINZ; + + pmove->player_maxs[3][0] = HULL3_MAXX; + pmove->player_maxs[3][1] = HULL3_MAXY; + pmove->player_maxs[3][2] = HULL3_MAXZ; + + + // Work around for a problem in the engine where it always uses the default HL + // hull sizes for the clip_mins and clip_maxs. + + for (int i = 0; i < pmove->numphysent; ++i) + { + + // HACK: for some reason the player field isn't correct on the clients. + // This is either a problem with HL or with NS's propagation of entities. + + if (pmove->physents[i].info >= 1 && pmove->physents[i].info <= 32) + { + pmove->physents[i].player = 1; + } + else + { + pmove->physents[i].player = 0; + } + + model_s* model = pmove->physents[i].model; + + if (model) + { + + model->hulls[0].clip_mins[0] = HULL2_MINX; + model->hulls[0].clip_mins[1] = HULL2_MINY; + model->hulls[0].clip_mins[2] = HULL2_MINZ; + + model->hulls[0].clip_maxs[0] = HULL2_MAXX; + model->hulls[0].clip_maxs[1] = HULL2_MAXY; + model->hulls[0].clip_maxs[2] = HULL2_MAXZ; + + model->hulls[1].clip_mins[0] = HULL0_MINX; + model->hulls[1].clip_mins[1] = HULL0_MINY; + model->hulls[1].clip_mins[2] = HULL0_MINZ; + + model->hulls[1].clip_maxs[0] = HULL0_MAXX; + model->hulls[1].clip_maxs[1] = HULL0_MAXY; + model->hulls[1].clip_maxs[2] = HULL0_MAXZ; + + model->hulls[2].clip_mins[0] = HULL3_MINX; + model->hulls[2].clip_mins[1] = HULL3_MINY; + model->hulls[2].clip_mins[2] = HULL3_MINZ; + + model->hulls[2].clip_maxs[0] = HULL3_MAXX; + model->hulls[2].clip_maxs[1] = HULL3_MAXY; + model->hulls[2].clip_maxs[2] = HULL3_MAXZ; + + model->hulls[3].clip_mins[0] = HULL1_MINX; + model->hulls[3].clip_mins[1] = HULL1_MINY; + model->hulls[3].clip_mins[2] = HULL1_MINZ; + + model->hulls[3].clip_maxs[0] = HULL1_MAXX; + model->hulls[3].clip_maxs[1] = HULL1_MAXY; + model->hulls[3].clip_maxs[2] = HULL1_MAXZ; + + } + + } + +} + +void PM_UpdateStepSound( void ) +{ + if(!GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + { + int fWalking; + float fvol; + vec3_t knee; + vec3_t feet; + vec3_t center; + float height; + float speed; + float velrun; + float velwalk; +// float flduck; + int fLadder; + int step; + + if ( pmove->flTimeStepSound > 0 ) + return; + + if ( pmove->flags & FL_FROZEN ) + return; + + PM_CatagorizeTextureType(); + + speed = Length( pmove->velocity ); + + // determine if we are on a ladder + fLadder = ( pmove->movetype == MOVETYPE_FLY );// IsOnLadder(); + + // UNDONE: need defined numbers for run, walk, crouch, crouch run velocities!!!! + if ( !GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && (( pmove->flags & FL_DUCKING) || fLadder) ) + { + velwalk = 60; // These constants should be based on cl_movespeedkey * cl_forwardspeed somehow + velrun = 80; // UNDONE: Move walking to server +// flduck = 100; + } + else + { + velwalk = 120; + velrun = 210; +// flduck = 0; + } + + // If we're on a ladder or on the ground, and we're moving fast enough, + // play step sound. Also, if pmove->flTimeStepSound is zero, get the new + // sound right away - we just started moving in new level. + if ( (fLadder || GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) || ( pmove->onground != -1 ) ) && ( Length( pmove->velocity ) > 0.0 )) + { + fWalking = speed < velrun; + + VectorCopy( pmove->origin, center ); + VectorCopy( pmove->origin, knee ); + VectorCopy( pmove->origin, feet ); + + height = pmove->player_maxs[ pmove->usehull ][ 2 ] - pmove->player_mins[ pmove->usehull ][ 2 ]; + + knee[2] = pmove->origin[2] - 0.3 * height; + feet[2] = pmove->origin[2] - 0.5 * height; + + // find out what we're stepping in or on... + if (fLadder) + { + step = STEP_LADDER; + fvol = 0.35; + pmove->flTimeStepSound = 350; + } + else if ( pmove->PM_PointContents ( knee, NULL ) == CONTENTS_WATER ) + { + step = STEP_WADE; + fvol = 0.65; + pmove->flTimeStepSound = 600; + } + else if ( pmove->PM_PointContents ( feet, NULL ) == CONTENTS_WATER ) + { + step = STEP_SLOSH; + fvol = fWalking ? 0.2 : 0.5; + pmove->flTimeStepSound = fWalking ? 400 : 300; + } + else + { + // find texture under player, if different from current texture, + // get material type + step = PM_MapTextureTypeStepType( pmove->chtexturetype ); + float theFraction = PM_SetStepInterval(); + + switch ( pmove->chtexturetype ) + { + default: + case CHAR_TEX_CONCRETE: + case CHAR_TEX_METAL: + case CHAR_TEX_GRATE: + case CHAR_TEX_TILE: + case CHAR_TEX_SLOSH: + //fvol = .2f + .3f*theFraction; + fvol = .7f*theFraction; + break; + + case CHAR_TEX_DIRT: + fvol = .75f*theFraction; + //fvol = .25f + .3f*theFraction; + //fvol = fWalking ? 0.25 : 0.55; + //pmove->flTimeStepSound = fWalking ? 400 : 300; + break; + + case CHAR_TEX_VENT: + fvol = .3f + .5f*theFraction; + //fvol = .5f + .3f*theFraction; + //fvol = fWalking ? 0.4 : 0.7; + //pmove->flTimeStepSound = fWalking ? 400 : 300; + break; + } + } + +// pmove->flTimeStepSound += flduck; // slower step time if ducking + + // play the sound + // 35% volume if ducking + if (!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && (pmove->flags & FL_DUCKING)) + { + fvol *= 0.35; + } + + PM_PlayStepSound( step, fvol ); + } + } +} + +/* +================ +PM_AddToTouched + +Add's the trace result to touch list, if contact is not already in list. +================ +*/ +qboolean PM_AddToTouched(pmtrace_t tr, vec3_t impactvelocity) +{ + int i; + + for (i = 0; i < pmove->numtouch; i++) + { + if (pmove->touchindex[i].ent == tr.ent) + break; + } + if (i != pmove->numtouch) // Already in list. + return false; + + VectorCopy( impactvelocity, tr.deltavelocity ); + + if (pmove->numtouch >= MAX_PHYSENTS) + pmove->Con_DPrintf("Too many entities were touched!\n"); + + pmove->touchindex[pmove->numtouch++] = tr; + return true; +} + +/* +================ +PM_CheckVelocity + +See if the player has a bogus velocity value. +================ +*/ +void PM_CheckVelocity () +{ + int i; + +// +// bound velocity +// + for (i=0 ; i<3 ; i++) + { + // See if it's bogus. + if (IS_NAN(pmove->velocity[i])) + { + pmove->Con_Printf ("PM Got a NaN velocity %i\n", i); + pmove->velocity[i] = 0; + } + if (IS_NAN(pmove->origin[i])) + { + pmove->Con_Printf ("PM Got a NaN origin on %i\n", i); + pmove->origin[i] = 0; + } + + // Bound it. + if (pmove->velocity[i] > pmove->movevars->maxvelocity) + { + pmove->Con_DPrintf ("PM Got a velocity too high on %i\n", i); + pmove->velocity[i] = pmove->movevars->maxvelocity; + } + else if (pmove->velocity[i] < -pmove->movevars->maxvelocity) + { + pmove->Con_DPrintf ("PM Got a velocity too low on %i\n", i); + pmove->velocity[i] = -pmove->movevars->maxvelocity; + } + + } +} + +/* +================== +PM_ClipVelocity + +Slide off of the impacting object +returns the blocked flags: +0x01 == floor +0x02 == step / wall +================== +*/ +int PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) +{ + float backoff; + float change; + float angle; + int i, blocked; + + angle = normal[ 2 ]; + + blocked = 0x00; // Assume unblocked. + if (angle > 0) // If the plane that is blocking us has a positive z component, then assume it's a floor. + blocked |= 0x01; // + if (!angle) // If the plane has no Z, it is vertical (wall/step) + blocked |= 0x02; // + + // Determine how far along plane to slide based on incoming direction. + // Scale by overbounce factor. + backoff = DotProduct (in, normal) * overbounce; + + for (i=0 ; i<3 ; i++) + { + change = normal[i]*backoff; + out[i] = in[i] - change; + // If out velocity is too small, zero it out. + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0; + } + + // Return blocking flags. + return blocked; +} + +float PM_GetHorizontalSpeed() +{ + return sqrtf(pmove->velocity[0] * pmove->velocity[0] + + pmove->velocity[1] * pmove->velocity[1]); +} + +void PM_AddCorrectGravity() +{ + float ent_gravity; + + // TODO: Put back in alien glide when alien upgrades worked out +// if(GetHasUpgrade(pmove->iuser4, ALIEN_ABILITY_1)) +// { +// pmove->gravity = .2f; +// } +/* else */ + if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) + { + /* + // Glide down + if((pmove->cmd.buttons & IN_JUMP) && (pmove->velocity[2] <= 0.0f)) + { + pmove->gravity = .10f; + } + else + { + pmove->gravity = .55f; + } + */ + + float theMinGravity = 0.10f; + float theMaxGravity = 0.75f; + float theGravity = theMaxGravity; + + if (pmove->cmd.buttons & IN_JUMP) + { + + float theSpeed = PM_GetHorizontalSpeed(); + float theLift = (theSpeed / 300) * (pmove->forward[2] + 0.5) / 1.5; + + if (theLift < 0) + { + theLift = 0; + } + + theGravity = theMinGravity + (1 - theLift) * (theMaxGravity - theMinGravity); + + } + + pmove->gravity = max(min(theGravity, theMaxGravity), theMinGravity); + } + else + { + pmove->gravity = 1.0f; + } + + // If they're in the air and they have jump held down, and they have the gliding type, + // apply less gravity to them +// if ( pmove->cmd.buttons & IN_JUMP ) +// { +// if(pmove->iuser3 == AVH_USER3_GLIDE) +// { +// // Only glide on the way down +// if(pmove->velocity[2] < 0) +// { +// if(pmove->onground == -1) +// { +// // Glide and drag baybee +// pmove->gravity = .15f; +// pmove->velocity[0] /= 1.01f; +// pmove->velocity[1] /= 1.01f; +// } +// } +// } +// } + + // : 0000972 + if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) + // : + return; + + if (pmove->gravity) + ent_gravity = pmove->gravity; + else + ent_gravity = 1.0; + + if(GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) || GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + { + pmove->gravity = ent_gravity = 0.0f; + } + + // Add gravity so they'll be in the correct position during movement + // yes, this 0.5 looks wrong, but it's not. + pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * 0.5 * pmove->frametime ); + pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; + pmove->basevelocity[2] = 0; + + PM_CheckVelocity(); +} + + +void PM_FixupGravityVelocity () +{ + float ent_gravity; + + // : 0000972 + if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) + // : + return; + + if (pmove->gravity) + ent_gravity = pmove->gravity; + else + ent_gravity = 1.0; + + if(GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) || GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + { + pmove->gravity = ent_gravity = 0.0f; + } + + // Get the correct velocity for the end of the dt + pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * pmove->frametime * 0.5 ); + + PM_CheckVelocity(); +} + +/* +============ +PM_FlyMove + +The basic solid body movement clip that slides along multiple planes +============ +*/ +int PM_FlyMove (void) +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity; + vec3_t new_velocity; + int i, j; + pmtrace_t trace; + vec3_t end; + float time_left, allFraction; + int blocked; + + numbumps = 4; // Bump up to four times + + blocked = 0; // Assume not blocked + numplanes = 0; // and not sliding along any planes + VectorCopy (pmove->velocity, original_velocity); // Store original velocity + VectorCopy (pmove->velocity, primal_velocity); + + allFraction = 0; + time_left = pmove->frametime; // Total time for this movement operation. + + // CGC - Removed zeroing of velocity so commander can float around easily outside of world 1/11/02 + bool theIsInTopDown = (pmove->iuser4 & MASK_TOPDOWN); + + for (bumpcount=0 ; bumpcountvelocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) + break; + + // Assume we can move all the way from the current origin to the + // end point. + for (i=0 ; i<3 ; i++) + end[i] = pmove->origin[i] + time_left * pmove->velocity[i]; + + // See if we can make it from origin to end point. + + trace = NS_PlayerTrace (pmove, pmove->origin, end, PM_NORMAL, -1 ); + + if(theIsInTopDown) + { + // Don't collide with anything when we're commander + VectorCopy(end, trace.endpos); + } + + allFraction += trace.fraction; + // If we started in a solid object, or we were in solid space + // the whole way, zero out our velocity and return that we + // are blocked by floor and wall. + if (trace.allsolid && !theIsInTopDown) + { + // entity is trapped in another solid + VectorCopy (vec3_origin, pmove->velocity); + //Con_DPrintf("Trapped 4\n"); + return 4; + } + + // If we moved some portion of the total distance, then + // copy the end position into the pmove->origin and + // zero the plane counter. + if (trace.fraction > 0 || theIsInTopDown) + { // actually covered some distance + VectorCopy (trace.endpos, pmove->origin); + VectorCopy (pmove->velocity, original_velocity); + numplanes = 0; + } + + // If we covered the entire distance, we are done + // and can return. + if (trace.fraction == 1 || theIsInTopDown) + break; // moved the entire distance + + //if (!trace.ent) + // Sys_Error ("PM_PlayerTrace: !trace.ent"); + + // Save entity that blocked us (since fraction was < 1.0) + // for contact + // Add it if it's not already in the list!!! + PM_AddToTouched(trace, pmove->velocity); + + // If the plane we hit has a high z component in the normal, then + // it's probably a floor + if (trace.plane.normal[2] > 0.7) + { + blocked |= 1; // floor + } + // If the plane has a zero z component in the normal, then it's a + // step or wall + if (!trace.plane.normal[2]) + { + blocked |= 2; // step / wall + //Con_DPrintf("Blocked by %i\n", trace.ent); + } + + // Reduce amount of pmove->frametime left by total time left * fraction + // that we covered. + time_left -= time_left * trace.fraction; + + // Did we run out of planes to clip against? + if (numplanes >= MAX_CLIP_PLANES) + { // this shouldn't really happen + // Stop our movement if so. + VectorCopy (vec3_origin, pmove->velocity); + //Con_DPrintf("Too many planes 4\n"); + + break; + } + + // Set up next clipping plane + VectorCopy (trace.plane.normal, planes[numplanes]); + numplanes++; +// + +// modify original_velocity so it parallels all of the clip planes +// + if ( pmove->movetype == MOVETYPE_WALK && + ((pmove->onground == -1) || (pmove->friction != 1)) ) // relfect player velocity + { + for ( i = 0; i < numplanes; i++ ) + { + if ( planes[i][2] > 0.0 ) + {// floor or slope + PM_ClipVelocity( original_velocity, planes[i], new_velocity, 1 ); + VectorCopy( new_velocity, original_velocity ); + } + else + PM_ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + pmove->movevars->bounce * (1-pmove->friction) ); + } + + VectorCopy( new_velocity, pmove->velocity ); + VectorCopy( new_velocity, original_velocity ); + } + else + { + for (i=0 ; ivelocity, + 1); + for (j=0 ; jvelocity, planes[j]) < 0) + break; // not ok + } + if (j == numplanes) // Didn't have to clip, so we're ok + break; + } + + // Did we go all the way through plane set + if (i != numplanes) + { // go along this plane + // pmove->velocity is set in clipping call, no need to set again. + ; + } + else + { // go along the crease + if (numplanes != 2) + { + //Con_Printf ("clip velocity, numplanes == %i\n",numplanes); + VectorCopy (vec3_origin, pmove->velocity); + //Con_DPrintf("Trapped 4\n"); + + break; + } + CrossProduct (planes[0], planes[1], dir); + d = DotProduct (dir, pmove->velocity); + VectorScale (dir, d, pmove->velocity ); + } + + // + // if original velocity is against the original velocity, stop dead + // to avoid tiny occilations in sloping corners + // + if (DotProduct (pmove->velocity, primal_velocity) <= 0) + { + //Con_DPrintf("Back\n"); + VectorCopy (vec3_origin, pmove->velocity); + break; + } + } + } + + if ( allFraction == 0 ) + { + if(!GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + { + VectorCopy (vec3_origin, pmove->velocity); + } + //Con_DPrintf( "Don't stick\n" ); + } + + return blocked; +} + +/* +============== +PM_Accelerate +============== +*/ +void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed; + + // Dead player's don't accelerate + if (pmove->dead) + return; + + // If waterjumping, don't accelerate + // : 0000972 + if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) + // : + return; + + // See if we are changing direction a bit + currentspeed = DotProduct (pmove->velocity, wishdir); + + // Reduce wishspeed by the amount of veer. + addspeed = wishspeed - currentspeed; + + // If not going to add any speed, done. + if (addspeed <= 0) + return; + + // Determine amount of accleration. + accelspeed = accel * pmove->frametime * wishspeed * pmove->friction; + + // Cap at addspeed + if (accelspeed > addspeed) + accelspeed = addspeed; + + // Adjust velocity. + for (i=0 ; i<3 ; i++) + { + pmove->velocity[i] += accelspeed * wishdir[i]; + } +} + +/* +===================== +PM_WalkMove + +Only used by players. Moves along the ground when player is a MOVETYPE_WALK. +====================== +*/ +void PM_WalkMove () +{ + int clip; + int oldonground; + int i; + + vec3_t wishvel; + float spd; + float fmove, smove, umove; + vec3_t wishdir; + float wishspeed; + + vec3_t dest, start; + vec3_t original, originalvel; + vec3_t down, downvel; + float downdist, updist; + + pmtrace_t trace; + + // Copy movement amounts + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + // Added by mmcguire. + umove = pmove->cmd.upmove; + + float theVelocityLength = Length(pmove->velocity); + //pmove->Con_Printf("Max speed: %f, velocity: %f\n", pmove->maxspeed, theVelocityLength); + + // Marines move slower when moving backwards or sideways + if(pmove->iuser3 == AVH_USER3_MARINE_PLAYER) + { + if(fmove < 0) + { + fmove *= kMarineBackpedalSpeedScalar; + } + + smove *= kMarineSidestepSpeedScalar; + } + + if(GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + { + + vec3_t theForwardView, theRightView, theUpView; + + VectorCopy(pmove->forward, theForwardView); + VectorCopy(pmove->up, theUpView); + + VectorNormalize(theForwardView); + VectorNormalize(theUpView); + + CrossProduct(theForwardView, theUpView, theRightView); + + // Changed by mmcguire. Added up movement. + for (i=0 ; i<3 ; i++) + { + wishvel[i] = theForwardView[i]*fmove + theRightView[i]*smove + theUpView[i]*umove; + } + + } + else + { + // Zero out z components of movement vectors + pmove->forward[2] = 0; + pmove->right[2] = 0; + + VectorNormalize (pmove->forward); // Normalize remainder of vectors. + VectorNormalize (pmove->right); // + + //pmove->Con_Printf("PM_WalkMove()->fmove: %f\n", fmove); + + if (PM_GetIsCharging()) + { + SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, true); + + // Modify fmove? + fmove = 500; + } + + for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity + wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; + + wishvel[2] = 0; // Zero out z part of velocity + } + + VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move + wishspeed = VectorNormalize(wishdir); + +// +// Clamp to server defined max speed +// + if (wishspeed > pmove->maxspeed) + { + VectorScale (wishvel, pmove->maxspeed/wishspeed, wishvel); + wishspeed = pmove->maxspeed; + } + + // Set pmove velocity + if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + pmove->velocity[2] = 0; + + PM_Accelerate (wishdir, wishspeed, pmove->movevars->accelerate); + + if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + pmove->velocity[2] = 0; + + // Add in any base velocity to the current velocity. + VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity ); + + spd = Length( pmove->velocity ); + + if (spd < 1.0f) + { + VectorClear( pmove->velocity ); + return; + } + + // If we are not moving, do nothing + //if (!pmove->velocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) + // return; + + oldonground = pmove->onground; + +// first try just moving to the destination + dest[0] = pmove->origin[0] + pmove->velocity[0]*pmove->frametime; + dest[1] = pmove->origin[1] + pmove->velocity[1]*pmove->frametime; + + // Wall-sticking change + //dest[2] = pmove->origin[2]; + dest[2] = pmove->origin[2] + pmove->velocity[2]*pmove->frametime; + + // first try moving directly to the next spot + VectorCopy (dest, start); + + trace = NS_PlayerTrace (pmove, pmove->origin, dest, PM_NORMAL, -1 ); + + + // If we made it all the way, then copy trace end + // as new player position. + if (trace.fraction == 1) + { + VectorCopy (trace.endpos, pmove->origin); + return; + } + + if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + { + if (oldonground == -1 && // Don't walk up stairs if not on ground. + pmove->waterlevel == 0) + return; + } + + // : 0000972 + if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) + // : + // If we are jumping out of water, don't do anything more. + return; + + // Try sliding forward both on ground and up 16 pixels + // take the move that goes farthest + VectorCopy (pmove->origin, original); // Save out original pos & + VectorCopy (pmove->velocity, originalvel); // velocity. + + // Slide move + clip = PM_FlyMove (); + + // Copy the results out + VectorCopy (pmove->origin , down); + VectorCopy (pmove->velocity, downvel); + + // Reset original values. + VectorCopy (original, pmove->origin); + + VectorCopy (originalvel, pmove->velocity); + + // Start out up one stair height + VectorCopy (pmove->origin, dest); + //dest[2] += pmove->movevars->stepsize; + dest[2] += NS_GetStepsize(pmove->iuser3); + + trace = NS_PlayerTrace (pmove, pmove->origin, dest, PM_NORMAL, -1 ); + + // If we started okay and made it part of the way at least, + // copy the results to the movement start position and then + // run another move try. + if (!trace.startsolid && !trace.allsolid) + { + VectorCopy (trace.endpos, pmove->origin); + } + +// slide move the rest of the way. + clip = PM_FlyMove (); + +// Now try going back down from the end point +// press down the stepheight + VectorCopy (pmove->origin, dest); + //dest[2] -= pmove->movevars->stepsize; + dest[2] -= NS_GetStepsize(pmove->iuser3); + + trace = NS_PlayerTrace (pmove, pmove->origin, dest, PM_NORMAL, -1 ); + + // If we are not on the ground any more then + // use the original movement attempt + if ( trace.plane.normal[2] < 0.7) + goto usedown; + // If the trace ended up in empty space, copy the end + // over to the origin. + if (!trace.startsolid && !trace.allsolid) + { + VectorCopy (trace.endpos, pmove->origin); + } + // Copy this origion to up. + VectorCopy (pmove->origin, pmove->up); + + // decide which one went farther + downdist = (down[0] - original[0])*(down[0] - original[0]) + + (down[1] - original[1])*(down[1] - original[1]); + updist = (pmove->up[0] - original[0])*(pmove->up[0] - original[0]) + + (pmove->up[1] - original[1])*(pmove->up[1] - original[1]); + + if (downdist > updist) + { +usedown: + VectorCopy (down , pmove->origin); + VectorCopy (downvel, pmove->velocity); + } else // copy z value from slide move + pmove->velocity[2] = downvel[2]; +} + +/* +================== +PM_Friction + +Handles both ground friction and water friction +================== +*/ +void PM_Friction (void) +{ + float *vel; + float speed, newspeed, control; + float friction; + float drop; + vec3_t newvel; + + // If we are in water jump cycle, don't apply friction + // : 0000972 + if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) + // : + return; + + if (PM_GetIsBlinking()) + return; + + // Get velocity + vel = pmove->velocity; + + // Calculate speed + speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]); + + // If too slow, return + if (speed < 0.1f) + { + return; + } + + drop = 0; + +// apply ground friction + if ((pmove->onground != -1) || (GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING))) // On an entity that is the ground + { + vec3_t start, stop; + pmtrace_t trace; + + start[0] = stop[0] = pmove->origin[0] + vel[0]/speed*16; + start[1] = stop[1] = pmove->origin[1] + vel[1]/speed*16; + start[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2]; + stop[2] = start[2] - 34; + + trace = NS_PlayerTrace (pmove, start, stop, PM_NORMAL, -1 ); + + if (trace.fraction == 1.0) + friction = pmove->movevars->friction*pmove->movevars->edgefriction; + else + friction = pmove->movevars->friction; + + // Grab friction value. + //friction = pmove->movevars->friction; + + friction *= pmove->friction; // player friction? + + // Bleed off some speed, but if we have less than the bleed + // threshhold, bleed the theshold amount. + control = (speed < pmove->movevars->stopspeed) ? + pmove->movevars->stopspeed : speed; + // Add the amount to t'he drop amount. + drop += control*friction*pmove->frametime; + } + +// apply water friction +// if (pmove->waterlevel) +// drop += speed * pmove->movevars->waterfriction * waterlevel * pmove->frametime; + +// scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + + // Determine proportion of old speed we are using. + newspeed /= speed; + + // Adjust velocity according to proportion. + newvel[0] = vel[0] * newspeed; + newvel[1] = vel[1] * newspeed; + newvel[2] = vel[2] * newspeed; + + VectorCopy( newvel, pmove->velocity ); +} + +void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed, wishspd = wishspeed; + + if (pmove->dead) + return; + // : 0000972 + if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) + // : + return; + if(GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + return; + + // Cap speed + //wishspd = VectorNormalize (pmove->wishveloc); + + if (wishspd > 30) + wishspd = 30; + // Determine veer amount + currentspeed = DotProduct (pmove->velocity, wishdir); + // See how much to add + addspeed = wishspd - currentspeed; + // If not adding any, done. + if (addspeed <= 0) + return; + // Determine acceleration speed after acceleration + + accelspeed = accel * wishspeed * pmove->frametime * pmove->friction; + // Cap it + if (accelspeed > addspeed) + accelspeed = addspeed; + + // Adjust pmove vel. + for (i=0 ; i<3 ; i++) + { + pmove->velocity[i] += accelspeed*wishdir[i]; + } +} + +/* +=================== +PM_WaterMove + +=================== +*/ +void PM_WaterMove (void) +{ + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + vec3_t start, dest; + vec3_t temp; + pmtrace_t trace; + + float speed, newspeed, addspeed, accelspeed; + +// +// user intentions +// + for (i=0 ; i<3 ; i++) + wishvel[i] = pmove->forward[i]*pmove->cmd.forwardmove + pmove->right[i]*pmove->cmd.sidemove; + + // Sinking after no other movement occurs + if (!pmove->cmd.forwardmove && !pmove->cmd.sidemove && !pmove->cmd.upmove) + wishvel[2] -= 60; // drift towards bottom + else // Go straight up by upmove amount. + wishvel[2] += pmove->cmd.upmove; + + // Copy it over and determine speed + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // Cap speed. + if (wishspeed > pmove->maxspeed) + { + VectorScale (wishvel, pmove->maxspeed/wishspeed, wishvel); + wishspeed = pmove->maxspeed; + } + // Slow us down a bit. + wishspeed *= 0.8; + + VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity); +// Water friction + VectorCopy(pmove->velocity, temp); + speed = VectorNormalize(temp); + if (speed) + { + newspeed = speed - pmove->frametime * speed * pmove->movevars->friction * pmove->friction; + + if (newspeed < 0) + newspeed = 0; + VectorScale (pmove->velocity, newspeed/speed, pmove->velocity); + } + else + newspeed = 0; + +// +// water acceleration +// + if ( wishspeed < 0.1f ) + { + return; + } + + addspeed = wishspeed - newspeed; + if (addspeed > 0) + { + + VectorNormalize(wishvel); + accelspeed = pmove->movevars->accelerate * wishspeed * pmove->frametime * pmove->friction; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i = 0; i < 3; i++) + pmove->velocity[i] += accelspeed * wishvel[i]; + } + +// Now move +// assume it is a stair or a slope, so press down from stepheight above + VectorMA (pmove->origin, pmove->frametime, pmove->velocity, dest); + VectorCopy (dest, start); + + //start[2] += pmove->movevars->stepsize + 1; + start[2] += NS_GetStepsize(pmove->iuser3) + 1; + + trace = NS_PlayerTrace (pmove, start, dest, PM_NORMAL, -1 ); + + if (!trace.startsolid && !trace.allsolid) // FIXME: check steep slope? + { // walked up the step, so just keep result and exit + VectorCopy (trace.endpos, pmove->origin); + return; + } + + // Try moving straight along out normal path. + PM_FlyMove (); +} + + +/* +=================== +PM_AirMove + +=================== +*/ +void PM_AirMove (void) +{ + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + + // Copy movement amounts + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + // Zero out z components of movement vectors + pmove->forward[2] = 0; + pmove->right[2] = 0; + // Renormalize + VectorNormalize (pmove->forward); + VectorNormalize (pmove->right); + + // Determine x and y parts of velocity + for (i=0 ; i<2 ; i++) + { + wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; + } + // Zero out z part of velocity + wishvel[2] = 0; + + // Determine maginitude of speed of move + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // Clamp to server defined max speed + if (wishspeed > pmove->maxspeed) + { + VectorScale (wishvel, pmove->maxspeed/wishspeed, wishvel); + wishspeed = pmove->maxspeed; + } + + PM_PreventMegaBunnyJumping(true); + + float theAirAccelerate = gIsJetpacking[pmove->player_index] ? pmove->movevars->airaccelerate*4 : pmove->movevars->airaccelerate; + if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) + { + theAirAccelerate = pmove->movevars->airaccelerate/22.0f; + } + + PM_AirAccelerate (wishdir, wishspeed, theAirAccelerate); + + // Add in any base velocity to the current velocity. + VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity ); + + //pmove->Con_Printf("airmove wish vel: %f %f %f\n", wishvel[0], wishvel[1], wishvel[2]); + //pmove->Con_Printf("airmove vel: %f %f %f\n", pmove->velocity[0], pmove->velocity[1], pmove->velocity[2]); + + PM_FlyMove (); +} + +qboolean PM_InWater( void ) +{ + return ( pmove->waterlevel > 1 ); +} + +/* +============= +PM_CheckWater + +Sets pmove->waterlevel and pmove->watertype values. +============= +*/ +qboolean PM_CheckWater () +{ + vec3_t point; + int cont; + int truecont; + float height; + float heightover2; + + // Pick a spot just above the players feet. + point[0] = pmove->origin[0] + (pmove->player_mins[pmove->usehull][0] + pmove->player_maxs[pmove->usehull][0]) * 0.5; + point[1] = pmove->origin[1] + (pmove->player_mins[pmove->usehull][1] + pmove->player_maxs[pmove->usehull][1]) * 0.5; + point[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2] + 1; + + // Assume that we are not in water at all. + pmove->waterlevel = 0; + pmove->watertype = CONTENTS_EMPTY; + + // Not sure why this is happening, but the commander on hera, above the droppad is returning CONTENTS_WATER + // The commander can never be under water + if(!GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + { + // Grab point contents. + cont = pmove->PM_PointContents (point, &truecont ); + // Are we under water? (not solid and not empty?) + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT ) + { + // Set water type + pmove->watertype = cont; + + // We are at least at level one + pmove->waterlevel = 1; + + height = (pmove->player_mins[pmove->usehull][2] + pmove->player_maxs[pmove->usehull][2]); + heightover2 = height * 0.5; + + // Now check a point that is at the player hull midpoint. + point[2] = pmove->origin[2] + heightover2; + cont = pmove->PM_PointContents (point, NULL ); + // If that point is also under water... + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT ) + { + // Set a higher water level. + pmove->waterlevel = 2; + + // Now check the eye position. (view_ofs is relative to the origin) + point[2] = pmove->origin[2] + pmove->view_ofs[2]; + + cont = pmove->PM_PointContents (point, NULL ); + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT ) + pmove->waterlevel = 3; // In over our eyes + } + + // Adjust velocity based on water current, if any. + if ( ( truecont <= CONTENTS_CURRENT_0 ) && + ( truecont >= CONTENTS_CURRENT_DOWN ) ) + { + // The deeper we are, the stronger the current. + static vec3_t current_table[] = + { + {1, 0, 0}, {0, 1, 0}, {-1, 0, 0}, + {0, -1, 0}, {0, 0, 1}, {0, 0, -1} + }; + + VectorMA (pmove->basevelocity, 50.0*pmove->waterlevel, current_table[CONTENTS_CURRENT_0 - truecont], pmove->basevelocity); + } + } + } + + return pmove->waterlevel > 1; +} + +/* +============= +PM_CategorizePosition +============= +*/ +void PM_CategorizePosition (void) +{ + vec3_t point; + pmtrace_t tr; + +// if the player hull point one unit down is solid, the player +// is on ground + +// see if standing on something solid + + // Doing this before we move may introduce a potential latency in water detection, but + // doing it after can get us stuck on the bottom in water if the amount we move up + // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call + // this several times per frame, so we really need to avoid sticking to the bottom of + // water on each call, and the converse case will correct itself if called twice. + PM_CheckWater(); + + point[0] = pmove->origin[0]; + point[1] = pmove->origin[1]; + point[2] = pmove->origin[2] - 2; + // : 1027 + // Correctly detect that we are climbing + if (((pmove->velocity[2] > (MAX_CLIMB_SPEED-10)) && (g_onladder[pmove->player_index] > 0)) + || (pmove->velocity[2] > 180) ) + // Shooting up really fast. Definitely not on ground. + { + pmove->onground = -1; + } + else + { + // Try and move down. + tr = NS_PlayerTrace (pmove, pmove->origin, point, PM_NORMAL, -1 ); + +// // If we hit a steep plane, we are not on ground +// if ( tr.plane.normal[2] < 0.7) +// pmove->onground = -1; // too steep +// else +// pmove->onground = tr.ent; // Otherwise, point to index of ent under us. + + // Change from Mr. Blonde, to fix weird problems when standing on buildings or resource nodes + if(tr.ent > 0 && !pmove->physents[tr.ent].model) + { + pmove->onground = tr.ent; // Standing on a point entity + } + else if ( tr.plane.normal[2] < 0.7 ) + { + pmove->onground = -1; // too steep + } + else + { + pmove->onground = tr.ent; // Otherwise, point to index of ent under us. + } + + if ( gIsJetpacking[pmove->player_index] == 0 ) { + + // If we are on something... + if (pmove->onground != -1) + { + // Then we are not in water jump sequence + pmove->waterjumptime = 0; + // If we could make the move, drop us down that 1 pixel + if (pmove->waterlevel < 2 && !tr.startsolid && !tr.allsolid) + VectorCopy (tr.endpos, pmove->origin); + } + } + // Standing on an entity other than the world + if (tr.ent > 0) // So signal that we are touching something. + { + PM_AddToTouched(tr, pmove->velocity); + } + } +} + +/* +================= +PM_GetRandomStuckOffsets + +When a player is stuck, it's costly to try and unstick them +Grab a test offset for the player based on a passed in index +================= +*/ +int PM_GetRandomStuckOffsets(int nIndex, int server, vec3_t offset) +{ + // Last time we did a full + int idx; + idx = rgStuckLast[nIndex][server]++; + + VectorCopy(rgv3tStuckTable[idx % 54], offset); + + return (idx % 54); +} + +void PM_ResetStuckOffsets(int nIndex, int server) +{ + rgStuckLast[nIndex][server] = 0; +} + +/* +================= +NudgePosition + +If pmove->origin is in a solid position, +try nudging slightly on all axis to +allow for the cut precision of the net coordinates +================= +*/ +#define PM_CHECKSTUCK_MINTIME 0.05 // Don't check again too quickly. + +int PM_CheckStuck (void) +{ + // Can't be stuck when you're a commander, allows commander to fly around outside the world. Do the same for observers/spectators? + if(GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + { + return 0; + } + + vec3_t base; + vec3_t offset; + vec3_t test; + int hitent; + int idx; + float fTime; + int i; + pmtrace_t traceresult; + + + +// int theEntNumber = 1; +// +// vec3_t theMins; +// VectorCopy(pmove->physents[theEntNumber].mins, theMins); +// +// vec3_t theMaxs; +// VectorCopy(pmove->physents[theEntNumber].maxs, theMaxs); +// +// //char* theName = pmove->physents[hitent].model +// //int theModType = pmove->PM_GetModelType( pmove->physents[hitent].model ); +// +// int theSolidity = pmove->physents[theEntNumber].solid; +// +// vec3_t theAngles; +// VectorCopy(pmove->physents[theEntNumber].angles, theAngles); +// +// vec3_t theOrigin; +// VectorCopy(pmove->physents[theEntNumber].origin, theOrigin); +// +// if(pmove->server) +// { +// pmove->Con_DPrintf("Server: ent: %d, solidity: %d mins(%f, %f, %f), maxs(%f, %f, %f), origin(%f, %f, %f), angles(%f, %f, %f)\n", theEntNumber, theSolidity, theMins[0], theMins[1], theMins[2], theMaxs[0], theMaxs[1], theMaxs[2], theOrigin[0], theOrigin[1], theOrigin[2], theAngles[0], theAngles[1], theAngles[2]); +// } +// else +// { +// pmove->Con_DPrintf("Client: ent: %d, solidity: %d mins(%f, %f, %f), maxs(%f, %f, %f), origin(%f, %f, %f), angles(%f, %f, %f)\n", theEntNumber, theSolidity, theMins[0], theMins[1], theMins[2], theMaxs[0], theMaxs[1], theMaxs[2], theOrigin[0], theOrigin[1], theOrigin[2], theAngles[0], theAngles[1], theAngles[2]); +// } + + + + + + static float rgStuckCheckTime[MAX_CLIENTS][2]; // Last time we did a full + + // If position is okay, exit + //hitent = pmove->PM_TestPlayerPosition (pmove->origin, &traceresult ); + hitent = NS_TestPlayerPosition(pmove, pmove->origin, &traceresult); + + if (hitent == -1 ) + { + PM_ResetStuckOffsets( pmove->player_index, pmove->server ); + return 0; + } + + VectorCopy (pmove->origin, base); + + // + // Deal with precision error in network. + // + if (!pmove->server) + { + // World or BSP model + if ( ( hitent == 0 ) || + ( pmove->physents[hitent].model != NULL ) ) + { + int nReps = 0; + PM_ResetStuckOffsets( pmove->player_index, pmove->server ); + do + { + i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); + + VectorAdd(base, offset, test); + //if (pmove->PM_TestPlayerPosition (test, &traceresult ) == -1) + if (NS_TestPlayerPosition(pmove, test, &traceresult ) == -1) + { + PM_ResetStuckOffsets( pmove->player_index, pmove->server ); + + VectorCopy ( test, pmove->origin ); + return 0; + } + nReps++; + } while (nReps < 54); + } + } + + // Only an issue on the client. + + if (pmove->server) + idx = 0; + else + idx = 1; + + fTime = pmove->Sys_FloatTime(); + // Too soon? + if (rgStuckCheckTime[pmove->player_index][idx] >= + ( fTime - PM_CHECKSTUCK_MINTIME ) ) + { + return 1; + } + rgStuckCheckTime[pmove->player_index][idx] = fTime; + + pmove->PM_StuckTouch( hitent, &traceresult ); + + i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); + + VectorAdd(base, offset, test); + + if ( ( hitent = NS_TestPlayerPosition (pmove, test, NULL ) ) == -1 ) + //if ( ( hitent = pmove->PM_TestPlayerPosition ( test, NULL ) ) == -1 ) + { + //pmove->Con_DPrintf("Nudged\n"); + + PM_ResetStuckOffsets( pmove->player_index, pmove->server ); + + if (i >= 27) + VectorCopy ( test, pmove->origin ); + + return 0; + } + + // If player is flailing while stuck in another player ( should never happen ), then see + // if we can't "unstick" them forceably. + if ( pmove->cmd.buttons & ( IN_JUMP | IN_DUCK | IN_ATTACK ) && ( pmove->physents[ hitent ].player != 0 ) ) + { + float x, y, z; + float xystep = 8.0; + float zstep = 18.0; + float xyminmax = xystep; + float zminmax = 4 * zstep; + + for ( z = 0; z <= zminmax; z += zstep ) + { + for ( x = -xyminmax; x <= xyminmax; x += xystep ) + { + for ( y = -xyminmax; y <= xyminmax; y += xystep ) + { + VectorCopy( base, test ); + test[0] += x; + test[1] += y; + test[2] += z; + + //if ( pmove->PM_TestPlayerPosition ( test, NULL ) == -1 ) + if (NS_TestPlayerPosition (pmove, test, NULL ) == -1 ) + { + VectorCopy( test, pmove->origin ); + return 0; + } + } + } + } + } + + //VectorCopy (base, pmove->origin); + + return 1; +} + +/* +=============== +PM_SpectatorMove +=============== +*/ +void PM_SpectatorMove (void) +{ + float speed, drop, friction, control, newspeed; + //float accel; + float currentspeed, addspeed, accelspeed; + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + // this routine keeps track of the spectators psoition + // there a two different main move types : track player or moce freely (OBS_ROAMING) + // doesn't need excate track position, only to generate PVS, so just copy + // targets position and real view position is calculated on client (saves server CPU) + + if ( pmove->iuser1 == OBS_ROAMING) + { + +#ifdef AVH_CLIENT + // jump only in roaming mode + if ( iJumpSpectator ) + { + VectorCopy( vJumpOrigin, pmove->origin ); + VectorCopy( vJumpAngles, pmove->angles ); + VectorCopy( vec3_origin, pmove->velocity ); + iJumpSpectator = 0; + return; + } + #endif + // Move around in normal spectator method + + speed = Length (pmove->velocity); + if (speed < 1) + { + VectorCopy (vec3_origin, pmove->velocity) + } + else + { + drop = 0; + + friction = pmove->movevars->friction*1.5; // extra friction + control = speed < pmove->movevars->stopspeed ? pmove->movevars->stopspeed : speed; + drop += control*friction*pmove->frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale (pmove->velocity, newspeed, pmove->velocity); + } + + // accelerate + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + VectorNormalize (pmove->forward); + VectorNormalize (pmove->right); + + for (i=0 ; i<3 ; i++) + { + wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; + } + wishvel[2] += pmove->cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + if (wishspeed > pmove->movevars->spectatormaxspeed) + { + VectorScale (wishvel, pmove->movevars->spectatormaxspeed/wishspeed, wishvel); + wishspeed = pmove->movevars->spectatormaxspeed; + } + + currentspeed = DotProduct(pmove->velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + + accelspeed = pmove->movevars->accelerate*pmove->frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove->velocity[i] += accelspeed*wishdir[i]; + + // move + VectorMA (pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); + } + else + { + // all other modes just track some kind of target, so spectator PVS = target PVS + + int target; + + // no valid target ? + if ( pmove->iuser2 <= 0) + return; + + // Find the client this player's targeting + for (target = 0; target < pmove->numphysent; target++) + { + if ( pmove->physents[target].info == pmove->iuser2 ) + break; + } + + if (target == pmove->numphysent) + return; + + // use targets position as own origin for PVS + VectorCopy( pmove->physents[target].angles, pmove->angles ); + VectorCopy( pmove->physents[target].origin, pmove->origin ); + + // no velocity + VectorCopy( vec3_origin, pmove->velocity ); + } +} + +/* +================== +PM_SplineFraction + +Use for ease-in, ease-out style interpolation (accel/decel) +Used by ducking code. +================== +*/ +float PM_SplineFraction( float value, float scale ) +{ + float valueSquared; + + value = scale * value; + valueSquared = value * value; + + // Nice little ease-in, ease-out spline-like curve + return 3 * valueSquared - 2 * valueSquared * value; +} + +void PM_FixPlayerCrouchStuck( int direction ) +{ + int hitent; + int i; + vec3_t test; + + hitent = pmove->PM_TestPlayerPosition ( pmove->origin, NULL ); + if (hitent == -1 ) + return; + + VectorCopy( pmove->origin, test ); + for ( i = 0; i < 36; i++ ) + { + pmove->origin[2] += direction; + + hitent = NS_TestPlayerPosition (pmove, pmove->origin, NULL ); + //hitent = pmove->PM_TestPlayerPosition ( pmove->origin, NULL ); + if (hitent == -1 ) + return; + } + + VectorCopy( test, pmove->origin ); // Failed +} + +void PM_UnDuck( void ) +{ + if(AvHMUGetCanDuck(pmove->iuser3)) + { + + pmtrace_t trace; + vec3_t newOrigin; + + VectorCopy( pmove->origin, newOrigin ); + + // : remove the jump when pressing and releasing duck quickly + if ( pmove->onground != -1 && pmove->flags & FL_DUCKING && pmove->bInDuck == false) + { + int theStandingHull = AvHMUGetHull(false, pmove->iuser3); + int theCrouchingHull = AvHMUGetHull(true, pmove->iuser3); + + newOrigin[2] += ( pmove->player_mins[theCrouchingHull][2] - pmove->player_mins[theStandingHull][2] ); + } + + trace = NS_PlayerTrace(pmove, newOrigin, newOrigin, PM_NORMAL, -1 ); + + if ( !trace.startsolid ) + { + //pmove->usehull = 0; + pmove->usehull = AvHMUGetHull(false, pmove->iuser3); + + // Oh, no, changing hulls stuck us into something, try unsticking downward first. + + trace = NS_PlayerTrace(pmove, newOrigin, newOrigin, PM_NORMAL, -1 ); + + if ( trace.startsolid ) + { + // Trace again for onos, but move us up first. Trying to get him unstick by moving down will never work for him. + if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER5) + { + newOrigin[2] += 30; + } + + trace = NS_PlayerTrace(pmove, newOrigin, newOrigin, PM_NORMAL, -1 ); + + if ( trace.startsolid ) + { + + // See if we are stuck? If so, stay ducked with the duck hull until we have a clear spot + //pmove->Con_Printf( "unstick got stuck\n" ); + + //pmove->usehull = 1; + pmove->usehull = AvHMUGetHull(true, pmove->iuser3); + //pmove->Con_Printf("Using hull: %d\n", pmove->usehull); + return; + } + } + //pmove->Con_Printf("Using hull: %d\n", pmove->usehull); + + pmove->flags &= ~FL_DUCKING; + pmove->bInDuck = false; + //pmove->view_ofs[2] = VEC_VIEW; + float theViewHeight = kStandingViewHeightPercentage*pmove->player_maxs[pmove->usehull][2]; + pmove->view_ofs[2] = theViewHeight; + pmove->flDuckTime = 0; + + VectorCopy( newOrigin, pmove->origin ); + + // Recatagorize position since ducking can change origin + PM_CategorizePosition(); + } + } +} + +void PM_Duck( void ) +{ + if(AvHMUGetCanDuck(pmove->iuser3)) + { + + int buttonsChanged = ( pmove->oldbuttons ^ pmove->cmd.buttons ); // These buttons have changed this frame + int nButtonPressed = buttonsChanged & pmove->cmd.buttons; // The changed ones still down are "pressed" + + int duckchange = buttonsChanged & IN_DUCK ? 1 : 0; + int duckpressed = nButtonPressed & IN_DUCK ? 1 : 0; + + if ( pmove->cmd.buttons & IN_DUCK ) + { + pmove->oldbuttons |= IN_DUCK; + } + else + { + pmove->oldbuttons &= ~IN_DUCK; + } + + // Prevent ducking if the iuser3 variable is set + //if ( pmove->iuser3 || pmove->dead ) + //{ + // // Try to unduck + // if ( pmove->flags & FL_DUCKING ) + // { + // PM_UnDuck(); + // } + // return; + //} + + if((pmove->flags & FL_DUCKING) && (!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING))) + { + pmove->cmd.forwardmove *= 0.333; + pmove->cmd.sidemove *= 0.333; + pmove->cmd.upmove *= 0.333; + } + + if ( ( pmove->cmd.buttons & IN_DUCK ) || ( pmove->bInDuck ) || ( pmove->flags & FL_DUCKING ) ) + { + if ( pmove->cmd.buttons & IN_DUCK ) + { + if ( (nButtonPressed & IN_DUCK ) && !( pmove->flags & FL_DUCKING ) ) + { + // Use 1 second so super long jump will work + pmove->flDuckTime = TIME_TO_DUCK; + pmove->bInDuck = true; + } + + float duckFraction = 1 - ((float)pmove->flDuckTime / TIME_TO_DUCK); + + if ( pmove->bInDuck ) + { + + int theStandingHull = AvHMUGetHull(false, pmove->iuser3); + int theCrouchingHull = AvHMUGetHull(true, pmove->iuser3); + + // Finish ducking immediately if duck time is over or not on ground + if ( (pmove->flDuckTime == 0) || (pmove->onground == -1 ) ) + { + + pmove->usehull = theCrouchingHull; + pmove->view_ofs[2] = kDuckingViewHeightPercentage*pmove->player_maxs[pmove->usehull][2]; + + pmove->flags |= FL_DUCKING; + pmove->bInDuck = false; + + // HACKHACK - Fudge for collision bug - no time to fix this properly + if ( pmove->onground != -1 ) + { + pmove->origin[2] -= ( pmove->player_mins[theCrouchingHull][2] - pmove->player_mins[theStandingHull][2] ); + // See if we are stuck? + PM_FixPlayerCrouchStuck( STUCK_MOVEUP ); + + // Recatagorize position since ducking can change origin + PM_CategorizePosition(); + } + + } + else + { + float theDuckingZ = kDuckingViewHeightPercentage*(pmove->player_maxs[theCrouchingHull][2]) +( pmove->player_mins[theStandingHull][2] - pmove->player_mins[theCrouchingHull][2] ); + float theStandingZ = kStandingViewHeightPercentage*(pmove->player_maxs[theStandingHull][2]); + + // Calc parametric time + float duckSplineFraction = PM_SplineFraction( duckFraction, 1); + pmove->view_ofs[2] = theStandingZ + duckSplineFraction * (theDuckingZ - theStandingZ); + + } + } + } + else + { + // Try to unduck + PM_UnDuck(); + } + } + } +} + +/* +bool NS_PositionFree(float inX, float inY, float inZ) +{ + bool thePositionFree = false; + pmtrace_t traceresult; + vec3_t thePosition; + thePosition[0] = inX; + thePosition[1] = inY; + thePosition[2] = inZ; + int hitent = pmove->PM_TestPlayerPosition(thePosition, &traceresult); + if(hitent == -1) + { + thePositionFree = true; + } + return thePositionFree; +} + +bool NS_PositionFreeForPlayer(vec3_t& inPosition) +{ + bool thePositionFree = false; + + // Test center point + if(NS_PositionFree(inPosition[0], inPosition[1], inPosition[2])) + { + // Test 8 corners of hull + vec3_t theMinHull; + VectorCopy(pmove->player_mins[pmove->usehull], theMinHull); + + vec3_t theMaxHull; + VectorCopy(pmove->player_maxs[pmove->usehull], theMaxHull); + + // Don't test hull points exactly (not exactly sure if PM_TestPlayerPosition() takes into account hull or not) + float theFudgeFactor = .5f; + float theMinX = inPosition[0] + theMinHull[0]*theFudgeFactor; + float theMinY = inPosition[1] + theMinHull[1]*theFudgeFactor; + float theMinZ = inPosition[2] + theMinHull[2]*theFudgeFactor; + + float theMaxX = inPosition[0] + theMaxHull[0]*theFudgeFactor; + float theMaxY = inPosition[1] + theMaxHull[1]*theFudgeFactor; + float theMaxZ = inPosition[2] + theMaxHull[2]*theFudgeFactor; + + if(NS_PositionFree(theMinX, theMinY, theMinZ)) + { + if(NS_PositionFree(theMinX, theMinY, theMaxZ)) + { + if(NS_PositionFree(theMinX, theMaxY, theMinZ)) + { + if(NS_PositionFree(theMinX, theMaxY, theMaxZ)) + { + if(NS_PositionFree(theMaxX, theMinY, theMinZ)) + { + if(NS_PositionFree(theMaxX, theMinY, theMaxZ)) + { + if(NS_PositionFree(theMaxX, theMaxY, theMinZ)) + { + if(NS_PositionFree(theMaxX, theMaxY, theMaxZ)) + { + thePositionFree = true; + } + } + } + } + } + } + } + } + } + + return thePositionFree; +} +*/ + +// Fade blink +bool PM_BlinkMove (void) +{ + if (pmove->fuser4 != 0.0f) + return false; + + float theScalar = 225; + float theEnergyCost = 0; + + AvHMUGetEnergyCost(AVH_WEAPON_BLINK, theEnergyCost); + + if(AvHMUHasEnoughAlienEnergy(pmove->fuser3, theEnergyCost)) + { + AvHMUDeductAlienEnergy(pmove->fuser3, theEnergyCost); + } + else + return false; + + pmove->fuser4 = (float)BALANCE_VAR(kBlinkROF); + + SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, true); + + vec3_t forward, right, up; + AngleVectors(pmove->angles, forward, right, up); + + PM_Jump(); + + vec3_t theAbilityVelocity; + VectorScale(forward, theScalar, theAbilityVelocity); + + vec3_t theFinalVelocity; + VectorAdd(pmove->velocity, theAbilityVelocity, theFinalVelocity); + + VectorCopy(theFinalVelocity, pmove->velocity); + + return true; + + // Uncomment to experience the tankeblink +/* float theEnergyCost = (float)BALANCE_VAR(kBlinkEnergyCost) * (float)pmove->frametime; + float theBlinkThresholdTime = (float)BALANCE_VAR(kBlinkThresholdTime); + if (AvHMUHasEnoughAlienEnergy(pmove->fuser3, theEnergyCost) && (pmove->fuser4 >= 0.0f)) + { + AvHMUDeductAlienEnergy(pmove->fuser3, theEnergyCost); + if (pmove->fuser4 == 0.0f) + { + int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); + float theVolumeScalar = 1.0f - theSilenceUpgradeLevel/3.0f; + PM_NSPlaySound( CHAN_WEAPON, "player/metabolize_fire.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); + } + pmove->fuser4 += (float)pmove->frametime; + pmove->fuser4 = max(0, min(pmove->fuser4, theBlinkThresholdTime)); + } + else + return false; + + if (pmove->fuser4 >= theBlinkThresholdTime) + { + SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, true); + + vec3_t wishvel, dest; + pmtrace_t trace; + float fmove, smove; + + // Copy movement amounts + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + VectorNormalize(pmove->forward); + VectorScale(pmove->forward, 800, wishvel); + + VectorMA(pmove->origin, pmove->frametime, wishvel, dest); + + // first try moving directly to the next spot + trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1 ); + // If we made it all the way, then copy trace end as new player position. + if (trace.fraction == 1) + { + VectorCopy(trace.endpos, pmove->origin); + } + else + { + // if we can't move, adjust velocity to slite along the plane we collided with + // and retry + int attempts = 0; // in case we continue to collide vs. the old planes + while (true) { + PM_ClipVelocity(wishvel, trace.plane.normal, wishvel, 1.0); + VectorMA(pmove->origin, pmove->frametime, wishvel, dest); + + trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1 ); + if (trace.fraction == 1) + { + VectorCopy(trace.endpos, pmove->origin); + break; + } + if (attempts++ > 5) { + break; + } + } + + } + VectorClear(pmove->velocity); + } + else + { + SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, false); + } +*/ +} + +// Skulk leap +bool PM_LeapMove() +{ + if (pmove->fuser4 != 0.0f) + return false; + + float theScalar = 500; + float theEnergyCost = 0; + + AvHMUGetEnergyCost(AVH_ABILITY_LEAP, theEnergyCost); + + if(AvHMUHasEnoughAlienEnergy(pmove->fuser3, theEnergyCost)) + { +#ifdef AVH_SERVER + AvHMUDeductAlienEnergy(pmove->fuser3, theEnergyCost); +#endif + } + else + return false; + + pmove->fuser4 = (float)BALANCE_VAR(kLeapROF); + + SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, true); + + vec3_t forward, right, up; + AngleVectors(pmove->angles, forward, right, up); + + //gCanJump[pmove->player_index] = true; + PM_Jump(); + //gCanJump[pmove->player_index] = false; + + vec3_t theAbilityVelocity; + VectorScale(forward, theScalar, theAbilityVelocity); + + vec3_t theFinalVelocity; + VectorAdd(pmove->velocity, theAbilityVelocity, theFinalVelocity); + + VectorCopy(theFinalVelocity, pmove->velocity); + + return true; +} + +bool PM_FlapMove() +{ + if (pmove->iuser3 != AVH_USER3_ALIEN_PLAYER3) + return false; + +// if (pmove->fuser4 != 0.0f) +// return false; + + + if(PM_CanFlap()) + { + // Set to define delay between flaps in seconds +// pmove->fuser4 = 0.1f; + + AvHMUDeductAlienEnergy(pmove->fuser3, kAlienEnergyFlap); + + // Added by mmcguire. + // Move the lerk in the direction has is facing. + vec3_t theFlapVelocity; + + float theThrust; + float theLift; + + if (pmove->cmd.forwardmove != 0) + { + + if (pmove->cmd.forwardmove > 0) + { + + theThrust = pmove->cmd.forwardmove * kWingThrustForwardScalar; + theLift = 200 * (pmove->forward[2] + 0.5) / 1.5; + + if (theLift < 0) + { + theLift = 0; + } + + } + else + { + // : 0000522 reverse lerk flight + // Uncomment to enable backwards flight + //theThrust = pmove->cmd.forwardmove * kWingThrustForwardScalar; //kWingThrustBackwardScalar; + //theLift = 200 * (pmove->forward[2] + 0.5) / 1.5; + //if (theLift < 0) + //{ + // theLift = 0; + //} + theThrust = -pmove->cmd.forwardmove * kWingThrustBackwardScalar; + theLift = 200; + // : + } + + } + else + { + theLift = 300; + theThrust = 0; + } + + VectorScale(pmove->forward, theThrust, theFlapVelocity); + theFlapVelocity[2] += theLift; + + vec3_t theNewVelocity; + VectorAdd(pmove->velocity, theFlapVelocity, theNewVelocity); + VectorCopy(theNewVelocity, pmove->velocity); + + if(pmove->runfuncs) + { + // Pick a random sound to play + int theSoundIndex = pmove->RandomLong(0, 2); + char* theSoundToPlay = NULL; + switch(theSoundIndex) + { + case 0: + theSoundToPlay = kWingFlapSound1; + break; + case 1: + theSoundToPlay = kWingFlapSound2; + break; + case 2: + theSoundToPlay = kWingFlapSound3; + break; + } + + // If alien has silencio upgrade, mute footstep volume + int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); + const float theBaseVolume = .5f; + float theVolumeScalar = theBaseVolume - (theSilenceUpgradeLevel/(float)3)*theBaseVolume; + theVolumeScalar = min(max(theVolumeScalar, 0.0f), 1.0f); + + PM_NSPlaySound(CHAN_BODY, theSoundToPlay, theVolumeScalar, ATTN_NORM, 0, PITCH_NORM); + } + + pmove->oldbuttons |= IN_JUMP; // don't jump again until released + return true; // in air, so no; effect + } + else + { + // Added by mmcguire. Lerk gliding. + if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3 && pmove->onground == -1) + { + // Compute the velocity not in the direction we're facing. + float theGlideAmount = min(0.2f, PM_GetHorizontalSpeed() / 1000); + + float speed = Length(pmove->velocity); + float projectedSpeed = DotProduct(pmove->velocity, pmove->forward); + + // : 0000522 reverse lerk flight + //if (projectedSpeed < 0) + // speed *= -1; + // : + vec3_t forwardVelocity; + VectorScale(pmove->forward, speed, forwardVelocity); + + vec3_t glideVelocity; + VectorSubtract(pmove->velocity, forwardVelocity, glideVelocity); + VectorScale(glideVelocity, theGlideAmount, glideVelocity); + + VectorSubtract(pmove->velocity, glideVelocity, pmove->velocity); + } + } + return true; +} + +// Onos charge +bool PM_ChargeMove() +{ + // TODO: Play event for charge! + + float theEnergyCost = (float)BALANCE_VAR(kChargeEnergyCost) * (float)pmove->frametime; + float theChargeThresholdTime = (float)BALANCE_VAR(kChargeThresholdTime); + float theChargeSpeed = (float)BALANCE_VAR(kChargeSpeed); + if (AvHMUHasEnoughAlienEnergy(pmove->fuser3, theEnergyCost) && (pmove->fuser4 >= 0.0f)) + { + AvHMUDeductAlienEnergy(pmove->fuser3, theEnergyCost); + if (pmove->fuser4 == 0.0f) + { + pmove->fuser4 = 0.3f; + } + pmove->fuser4 += (float)pmove->frametime; + pmove->fuser4 = max(0, min(pmove->fuser4, theChargeThresholdTime)); + } + else + return false; + + SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, true); + + if (pmove->onground != -1) + { + vec3_t forward; + vec3_t sideways; + float length = pmove->maxspeed * (1.0f + (float)BALANCE_VAR(kChargeSpeed) * pmove->fuser4 / theChargeThresholdTime); + + VectorCopy(pmove->forward, forward); + VectorScale(forward, -1 * DotProduct(forward, pmove->velocity), forward); + VectorAdd(pmove->velocity, forward, sideways); + //VectorScale(sideways, 1.5f, sideways); + + VectorCopy(pmove->forward, forward); + forward[2] = 0.0f; + VectorNormalize(forward); + VectorScale(forward, length, forward); + + VectorAdd(forward, sideways, pmove->velocity); + //VectorCopy(forward, pmove->velocity); + + // pmove->velocity[2] = 0.0f; + + float velocity = Length(pmove->velocity); + float maxvel = (pmove->maxspeed * (1.0f + theChargeSpeed)); + if (velocity > maxvel) + VectorScale(pmove->velocity, maxvel / velocity, pmove->velocity); + } + + return true; +} + +void PM_AlienAbilities() +{ + // Give some energy back if we're an alien and not evolving + string theExt; + float theTimePassed = (float)pmove->frametime; + if(NS_GetIsPlayerAlien(theExt)) + { + AvHMUUpdateAlienEnergy(theTimePassed, pmove->iuser3, pmove->iuser4, pmove->fuser3); + + // Stop charging when we're out of energy + if(GetHasUpgrade(pmove->iuser4, MASK_ALIEN_MOVEMENT)) + { + if(pmove->fuser3 <= 0.0f) + { + SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, false); + } + } + } + + // Movement abilities + + bool canmove = true; +#ifdef AVH_SERVER + canmove = gCanMove[pmove->player_index]; +#else + canmove = gCanMove; +#endif + bool success = false; + if ((pmove->cmd.buttons & IN_ATTACK2) && (AvHGetIsAlien(pmove->iuser3))) + { + switch (pmove->iuser3) + { + case AVH_USER3_ALIEN_PLAYER1: + success = canmove && PM_LeapMove(); + break; + case AVH_USER3_ALIEN_PLAYER3: + pmove->cmd.buttons |= IN_JUMP; + success = PM_FlapMove(); + break; + case AVH_USER3_ALIEN_PLAYER4: + success = canmove && PM_BlinkMove(); + break; + case AVH_USER3_ALIEN_PLAYER5: + success = canmove && PM_ChargeMove(); + break; + default: + { + break; + } + } + } + else if (PM_GetIsBlinking()) + success = PM_BlinkMove(); + else if (PM_GetIsLeaping()) + success = PM_LeapMove(); + + if (!success) + { + SetUpgradeMask(&pmove->iuser4, MASK_ALIEN_MOVEMENT, false); + + if (pmove->fuser4 >= 0.0f) + pmove->fuser4 *= -1.0f; + pmove->fuser4 += theTimePassed; + pmove->fuser4 = min(pmove->fuser4, 0.0f); + } + + return; + + //#endif + + if (PM_GetIsLeaping() || PM_GetIsBlinking()) + { + float theScalar = 500; + float theEnergyCost = 0; + + if (PM_GetIsBlinking()) + { + theScalar = 225; + AvHMUGetEnergyCost(AVH_WEAPON_BLINK, theEnergyCost); + } + else + { + AvHMUGetEnergyCost(AVH_ABILITY_LEAP, theEnergyCost); + // : 0000972 + // Add highjacked "watertime" to release leaping skulk from wall + // pmove->waterjumptime = 75; + // : + } + + if(AvHMUHasEnoughAlienEnergy(pmove->fuser3, theEnergyCost)) + { + AvHMUDeductAlienEnergy(pmove->fuser3, theEnergyCost); + } + else + { + // exploiting + return; + } + + vec3_t forward, right, up; + AngleVectors(pmove->angles, forward, right, up); + + PM_Jump(); + + vec3_t theAbilityVelocity; + //VectorScale(pmove->forward, theScalar, theAbilityVelocity); + VectorScale(forward, theScalar, theAbilityVelocity); + + vec3_t theFinalVelocity; + VectorAdd(pmove->velocity, theAbilityVelocity, theFinalVelocity); + + VectorCopy(theFinalVelocity, pmove->velocity); + + //pmove->oldbuttons |= IN_JUMP; // don't jump again until released + + + // if(pmove->runfuncs) + // { + // PM_NSPlaySound(CHAN_WEAPON, kLeapSound, 1.0f, ATTN_NORM, 0, 94 + pmove->RandomLong(0, 0xf)); + // } + //pmove->velocity[2] += 300; + //} + } +} + +void PM_LadderMove( physent_t *pLadder ) +{ + vec3_t ladderCenter; + trace_t trace; + qboolean onFloor; + vec3_t floor; + vec3_t modelmins, modelmaxs; + + if ( pmove->movetype == MOVETYPE_NOCLIP ) + return; + + pmove->PM_GetModelBounds( pLadder->model, modelmins, modelmaxs ); + + VectorAdd( modelmins, modelmaxs, ladderCenter ); + VectorScale( ladderCenter, 0.5, ladderCenter ); + + pmove->movetype = MOVETYPE_FLY; + + // On ladder, convert movement to be relative to the ladder + + VectorCopy( pmove->origin, floor ); + floor[2] += pmove->player_mins[pmove->usehull][2] - 1; + + if ( pmove->PM_PointContents( floor, NULL ) == CONTENTS_SOLID ) + onFloor = true; + else + onFloor = false; + + pmove->gravity = 0; + pmove->PM_TraceModel( pLadder, pmove->origin, ladderCenter, &trace ); + if ( trace.fraction != 1.0 ) + { + float forward = 0, right = 0; + vec3_t vpn, v_right; + + AngleVectors( pmove->angles, vpn, v_right, NULL ); + if ( pmove->cmd.buttons & IN_BACK ) + forward -= MAX_CLIMB_SPEED; + if ( pmove->cmd.buttons & IN_FORWARD ) + forward += MAX_CLIMB_SPEED; + if ( pmove->cmd.buttons & IN_MOVELEFT ) + right -= MAX_CLIMB_SPEED; + if ( pmove->cmd.buttons & IN_MOVERIGHT ) + right += MAX_CLIMB_SPEED; + +// if ( PM_GetIsBlinking() ) + //{ + //pmove->movetype = MOVETYPE_WALK; + //VectorScale( trace.plane.normal, 270, pmove->velocity ); + //} + //else + if ( pmove->cmd.buttons & IN_JUMP ) + { + pmove->movetype = MOVETYPE_WALK; + VectorScale( trace.plane.normal, 270, pmove->velocity ); + } + else + { + if ( forward != 0 || right != 0 ) + { + vec3_t velocity, perp, cross, lateral, tmp; + float normal; + + //ALERT(at_console, "pev %.2f %.2f %.2f - ", + // pev->velocity.x, pev->velocity.y, pev->velocity.z); + // Calculate player's intended velocity + //Vector velocity = (forward * gpGlobals->v_forward) + (right * gpGlobals->v_right); + VectorScale( vpn, forward, velocity ); + VectorMA( velocity, right, v_right, velocity ); + + + // Perpendicular in the ladder plane + // Vector perp = CrossProduct( Vector(0,0,1), trace.vecPlaneNormal ); + // perp = perp.Normalize(); + VectorClear( tmp ); + tmp[2] = 1; + CrossProduct( tmp, trace.plane.normal, perp ); + VectorNormalize( perp ); + + + // decompose velocity into ladder plane + normal = DotProduct( velocity, trace.plane.normal ); + // This is the velocity into the face of the ladder + VectorScale( trace.plane.normal, normal, cross ); + + + // This is the player's additional velocity + VectorSubtract( velocity, cross, lateral ); + + // This turns the velocity into the face of the ladder into velocity that + // is roughly vertically perpendicular to the face of the ladder. + // NOTE: It IS possible to face up and move down or face down and move up + // because the velocity is a sum of the directional velocity and the converted + // velocity through the face of the ladder -- by design. + CrossProduct( trace.plane.normal, perp, tmp ); + VectorMA( lateral, -normal, tmp, pmove->velocity ); + if ( onFloor && normal > 0 ) // On ground moving away from the ladder + { + VectorMA( pmove->velocity, MAX_CLIMB_SPEED, trace.plane.normal, pmove->velocity ); + } + //pev->velocity = lateral - (CrossProduct( trace.vecPlaneNormal, perp ) * normal); + } + else + { + VectorClear( pmove->velocity ); + } + } + } +} + +physent_t *PM_Ladder( void ) +{ + int i; + physent_t *pe; + hull_t *hull; + int num; + vec3_t test; + + for ( i = 0; i < pmove->nummoveent; i++ ) + { + pe = &pmove->moveents[i]; + + if ( pe->model && (modtype_t)pmove->PM_GetModelType( pe->model ) == mod_brush && pe->skin == CONTENTS_LADDER ) + { + + hull = (hull_t *)pmove->PM_HullForBsp( pe, test ); + num = hull->firstclipnode; + + // Offset the test point appropriately for this hull. + VectorSubtract ( pmove->origin, test, test); + + // Test the player's hull for intersection with this model + if ( pmove->PM_HullPointContents (hull, num, test) == CONTENTS_EMPTY) + continue; + + return pe; + } + } + + return NULL; +} + + + +void PM_WaterJump (void) +{ + // : 0000972 + if (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1) + return; + // : + + if ( pmove->waterjumptime > 10000 ) + { + pmove->waterjumptime = 10000; + } + + if ( !pmove->waterjumptime ) + return; + + pmove->waterjumptime -= pmove->cmd.msec; + if ( pmove->waterjumptime < 0 || + !pmove->waterlevel ) + { + pmove->waterjumptime = 0; + pmove->flags &= ~FL_WATERJUMP; + } + + pmove->velocity[0] = pmove->movedir[0]; + pmove->velocity[1] = pmove->movedir[1]; +} + +/* +============ +PM_AddGravity + +============ +*/ +void PM_AddGravity () +{ + float ent_gravity; + + if (pmove->gravity) + ent_gravity = pmove->gravity; + else + ent_gravity = 1.0; + + // Add gravity incorrectly + pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * pmove->frametime ); + pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; + pmove->basevelocity[2] = 0; + PM_CheckVelocity(); +} +/* +============ +PM_PushEntity + +Does not change the entities velocity at all +============ +*/ +pmtrace_t PM_PushEntity (vec3_t push) +{ + pmtrace_t trace; + vec3_t end; + + VectorAdd (pmove->origin, push, end); + + trace = NS_PlayerTrace (pmove, pmove->origin, end, PM_NORMAL, -1 ); + + VectorCopy (trace.endpos, pmove->origin); + + // So we can run impact function afterwards. + if (trace.fraction < 1.0 && + !trace.allsolid) + { + PM_AddToTouched(trace, pmove->velocity); + } + + return trace; +} + +/* +============ +PM_Physics_Toss() + +Dead player flying through air., e.g. +============ +*/ +void PM_Physics_Toss() +{ + pmtrace_t trace; + vec3_t move; + float backoff; + + PM_CheckWater(); + + if (pmove->velocity[2] > 0) + pmove->onground = -1; + + // If on ground and not moving, return. + if ( pmove->onground != -1 ) + { + if (VectorCompare(pmove->basevelocity, vec3_origin) && + VectorCompare(pmove->velocity, vec3_origin)) + return; + } + + PM_CheckVelocity (); + +// add gravity + if ( pmove->movetype != MOVETYPE_FLY && + pmove->movetype != MOVETYPE_BOUNCEMISSILE && + pmove->movetype != MOVETYPE_FLYMISSILE ) + PM_AddGravity (); + +// move origin + // Base velocity is not properly accounted for since this entity will move again after the bounce without + // taking it into account + VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity); + + PM_CheckVelocity(); + VectorScale (pmove->velocity, pmove->frametime, move); + VectorSubtract (pmove->velocity, pmove->basevelocity, pmove->velocity); + + trace = PM_PushEntity (move); // Should this clear basevelocity + + PM_CheckVelocity(); + + if (trace.allsolid) + { + // entity is trapped in another solid + pmove->onground = trace.ent; + VectorCopy (vec3_origin, pmove->velocity); + return; + } + + if (trace.fraction == 1) + { + PM_CheckWater(); + return; + } + + + if (pmove->movetype == MOVETYPE_BOUNCE) + backoff = 2.0 - pmove->friction; + else if (pmove->movetype == MOVETYPE_BOUNCEMISSILE) + backoff = 2.0; + else + backoff = 1; + + PM_ClipVelocity (pmove->velocity, trace.plane.normal, pmove->velocity, backoff); + + // stop if on ground + if (trace.plane.normal[2] > 0.7) + { + float vel; + vec3_t base; + + VectorClear( base ); + if (pmove->velocity[2] < pmove->movevars->gravity * pmove->frametime) + { + // we're rolling on the ground, add static friction. + pmove->onground = trace.ent; + pmove->velocity[2] = 0; + } + + vel = DotProduct( pmove->velocity, pmove->velocity ); + + // Con_DPrintf("%f %f: %.0f %.0f %.0f\n", vel, trace.fraction, ent->velocity[0], ent->velocity[1], ent->velocity[2] ); + + if (vel < (30 * 30) || (pmove->movetype != MOVETYPE_BOUNCE && pmove->movetype != MOVETYPE_BOUNCEMISSILE)) + { + pmove->onground = trace.ent; + VectorCopy (vec3_origin, pmove->velocity); + } + else + { + VectorScale (pmove->velocity, (1.0 - trace.fraction) * pmove->frametime * 0.9, move); + trace = PM_PushEntity (move); + } + VectorSubtract( pmove->velocity, base, pmove->velocity ) + } + +// check for in water + PM_CheckWater(); +} + +/* +==================== +PM_NoClip + +==================== +*/ +void PM_NoClip() +{ + int i; + vec3_t wishvel; + float fmove, smove; +// float currentspeed, addspeed, accelspeed; + + // Copy movement amounts + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + VectorNormalize ( pmove->forward ); + VectorNormalize ( pmove->right ); + + for (i=0 ; i<3 ; i++) // Determine x and y parts of velocity + { + wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove; + } + wishvel[2] += pmove->cmd.upmove; + + VectorMA (pmove->origin, pmove->frametime, wishvel, pmove->origin); + + // Zero out the velocity so that we don't accumulate a huge downward velocity from + // gravity, etc. + VectorClear( pmove->velocity ); + +} + +void PM_PlaybackEvent(int inEventID) +{ + int theFlags = 0; + + //#if defined( AVH_SERVER ) + theFlags = FEV_NOTHOST; + //#endif + + vec3_t theZeroVector; + theZeroVector[0] = theZeroVector[1] = theZeroVector[2] = 0.0f; + + // Lame way to get the local player index that playback event full needs...there must be another way? + //vec3_t theTraceEnd; + //VectorMA(pmove->origin, 100, pmove->forward, theTraceEnd); + //pmtrace_t theTrace = pmove->PM_PlayerTrace(pmove->origin, theTraceEnd, PM_TRACELINE_PHYSENTSONLY, -1); + //theTrace = pmove->PM_TraceLine(pmove->origin, theTraceEnd, PM_TRACELINE_PHYSENTSONLY, 2, -1); + + int thisPredictedPlayer = pmove->player_index;/*theTrace.ent;*/ + pmove->PM_PlaybackEventFull(theFlags, thisPredictedPlayer, inEventID, 0, (float *)theZeroVector, (float *)theZeroVector, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); +} + +int PM_GetCelerityLevel() { + int theSpeedUpgradeLevel =0; + if(GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_4)) + { + theSpeedUpgradeLevel = 1; + + if(GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_12)) + { + theSpeedUpgradeLevel = 2; + } + else if(GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_13)) + { + theSpeedUpgradeLevel = 3; + } + } + return theSpeedUpgradeLevel; +} + +#define LERK_DIVE_FACTOR -1.3f + +// Comment: Respect to the valve function name below +// Prevent lerks from having extreme vertical velocities + + +void PM_PreventMegaCrazyLerkPancakage() { + + // Current player speed + float spd; + // If we have to crop, apply this cropping fraction to velocity + float fraction; + float maxbasespeed=BALANCE_VAR(kLerkBaseSpeedMax) + (BALANCE_VAR(kAlienCelerityBonus)-5) * PM_GetCelerityLevel(); + + vec3_t vertical={0,0,-1.0f}; + + vec3_t normalizedVelocity; + + + spd = Length( pmove->velocity ); + + + VectorCopy(pmove->velocity, normalizedVelocity); + VectorNormalize(normalizedVelocity); + float dp=DotProduct(normalizedVelocity, vertical); + + if ( dp > 0 ) + dp /= 10.0f; + else + dp /= 5.0f; + +// if ( DotProduct(up, pmove->velocity) < 0.0f ) +// dp *= -1.0f; + + maxbasespeed *= 1.0f + dp; + + if ( spd <= maxbasespeed ) + return; + + // Returns the modifier for the velocity + fraction = maxbasespeed/spd; + + VectorScale( pmove->velocity, fraction, pmove->velocity ); //Crop it down!. + + //int theAdjustment=PM_GetCelerityLevel() * BALANCE_VAR(kAlienCelerityBonus); + //float theAscendMax=BALANCE_VAR(kLerkBaseAscendSpeedMax) + (float)theAdjustment; + //float theDescendMax=theAscendMax*LERK_DIVE_FACTOR; + //if ( pmove->velocity[2] > theAscendMax ) { + // pmove->velocity[2]=theAscendMax; + //} + // cap diving too + //else if ( pmove->velocity[2] < theDescendMax ) { + // pmove->velocity[2]=theDescendMax; + //} +} + +// Only allow bunny jumping up to 1.1x server / player maxspeed setting +#define BUNNYJUMP_MAX_SPEED_FACTOR 1.7f + +//----------------------------------------------------------------------------- +// Purpose: Corrects bunny jumping ( where player initiates a bunny jump before other +// movement logic runs, thus making onground == -1 thus making PM_Friction get skipped and +// running PM_AirMove, which doesn't crop velocity to maxspeed like the ground / other +// movement logic does. +//----------------------------------------------------------------------------- +void PM_PreventMegaBunnyJumping(bool inAir) +{ + // Current player speed + float spd; + // If we have to crop, apply this cropping fraction to velocity + float fraction; + // Speed at which bunny jumping is limited + float maxscaledspeed; + + maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * pmove->maxspeed; + if(inAir) + { + // prevent pancaking + if ( pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3 ) { + PM_PreventMegaCrazyLerkPancakage(); + return; + } + // Allow flyers, leapers, and JPers to go faster in the air, but still capped + maxscaledspeed = BALANCE_VAR(kAirspeedMultiplier)*pmove->maxspeed; + } + + // Don't divide by zero + if ( maxscaledspeed <= 0.0f ) + return; + + vec3_t theVelVector; + VectorCopy(pmove->velocity, theVelVector); + + if(inAir) + { + theVelVector[2] = 0.0f; + } + + spd = Length( theVelVector ); + + if ( spd <= maxscaledspeed ) + return; + + // Returns the modifier for the velocity + fraction = maxscaledspeed/spd; + + float theCurrentSpeed = Length(pmove->velocity); + + VectorScale( pmove->velocity, fraction, pmove->velocity ); //Crop it down!. + +//#ifdef AVH_CLIENT +// if(pmove->runfuncs) +// { +// pmove->Con_Printf("Preventing speed exploits (max speed: %f, current speed: %f, adjusted speed: %f\n", pmove->maxspeed, theCurrentSpeed, Length(pmove->velocity)); +// } +//#endif + + // Trigger footstep immediately to prevent silent bunny-hopping + //pmove->flTimeStepSound = 0.0f; +} + +/* +============= +PM_Jump +============= +*/ +void PM_Jump (void) +{ + int i; + qboolean tfc = false; + + qboolean cansuperjump = false; + + if (pmove->dead || GetHasUpgrade(pmove->iuser4, MASK_ENSNARED)) + { + pmove->oldbuttons |= IN_JUMP ; // don't jump again until released + return; + } + + tfc = atoi( pmove->PM_Info_ValueForKey( pmove->physinfo, "tfc" ) ) == 1 ? true : false; + + // Spy that's feigning death cannot jump + if ( tfc && + ( pmove->deadflag == ( DEAD_DISCARDBODY + 1 ) ) ) + { + return; + } + + // See if we are waterjumping. If so, decrement count and return. + // : 0000972 + if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) + // : +// if ( pmove->waterjumptime ) + { + pmove->waterjumptime -= pmove->cmd.msec; + if (pmove->waterjumptime < 0) + { + pmove->waterjumptime = 0; + } + return; + } + + // If we are in the water most of the way... + if (pmove->waterlevel >= 2) + { // swimming, not jumping + pmove->onground = -1; + + if (pmove->watertype == CONTENTS_WATER) // We move up a certain amount + pmove->velocity[2] = 100; + else if (pmove->watertype == CONTENTS_SLIME) + pmove->velocity[2] = 80; + else // LAVA + pmove->velocity[2] = 50; + + // play swiming sound + if ( pmove->flSwimTime <= 0 ) + { + // If alien has silencio upgrade, mute footstep volume + int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); + float theVolumeScalar = 1.0f - theSilenceUpgradeLevel/3.0f; + + // Don't play sound again for 1 second + pmove->flSwimTime = 1000; + switch ( pmove->RandomLong( 0, 3 ) ) + { + case 0: + PM_NSPlaySound( CHAN_BODY, "player/pl_wade1.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); + break; + case 1: + PM_NSPlaySound( CHAN_BODY, "player/pl_wade2.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); + break; + case 2: + PM_NSPlaySound( CHAN_BODY, "player/pl_wade3.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); + break; + case 3: + PM_NSPlaySound( CHAN_BODY, "player/pl_wade4.wav", theVolumeScalar, ATTN_NORM, 0, PITCH_NORM ); + break; + } + } + + return; + } + + // For wall jumping, remove bit above and replace with this (from coding forums) + + // Lerk flight movement + PM_FlapMove(); + + // : 0000972 walljump + if (canWallJump && (GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && (pmove->cmd.buttons & IN_JUMP) && !(pmove->oldbuttons & IN_JUMP) /*&& (gSurfaceNormal[2] < 0.7)*/)) + { + vec3_t theDirectionVec; + //VectorCopy(pmove->velocity, theDirectionVec); + //VectorAdd(pmove->basevelocity, pmove->forward, theDirectionVec); + //if (Length(theDirectionVec) == 0.0f) + VectorCopy(pmove->forward, theDirectionVec); + + VectorNormalize(theDirectionVec); + + vec3_t novar; + if (!NS_CheckOffsetFromOrigin(theDirectionVec[0] * 5, theDirectionVec[1] * 5, theDirectionVec[2] * 5, novar)) + { + VectorCopy(pmove->forward, theDirectionVec); + VectorNormalize(theDirectionVec); + + VectorScale(theDirectionVec, pmove->maxspeed + 50, pmove->velocity); + pmove->velocity[2] += 100; + + vec3_t theJumpVect; + VectorScale(gSurfaceNormal, 25, theJumpVect); + VectorAdd(theJumpVect, pmove->velocity, pmove->velocity); + + PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), 0.35 ); + + pmove->waterjumptime = 100; + } + } + // : + + // No more effect + if ( pmove->onground == -1 ) + { + // Flag that we jumped. + // HACK HACK HACK + // Remove this when the game .dll no longer does physics code!!!! + pmove->oldbuttons |= IN_JUMP; // don't jump again until released + return; // in air, so no effect + } + + +// string theAlienExtension; +// bool theIsAlien = NS_GetIsPlayerAlien(theAlienExtension); +// if ( pmove->oldbuttons & IN_JUMP && (pmove->velocity[0] == 0 || !theIsAlien || pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) ) + //return; // don't pogo stick + + if ( pmove->oldbuttons & IN_JUMP ) + return; // don't pogo stick + + // In the air now. + pmove->onground = -1; + + PM_PreventMegaBunnyJumping(false); + + if ( tfc ) + { + PM_NSPlaySound( CHAN_BODY, "player/plyrjmp8.wav", 0.5, ATTN_NORM, 0, PITCH_NORM ); + } + else + { + PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), 1.0 ); + } + + // See if user can super long jump? + cansuperjump = atoi( pmove->PM_Info_ValueForKey( pmove->physinfo, "slj" ) ) == 1 ? true : false; + + // Acclerate upward + // If we are ducking... + if ( ( pmove->bInDuck ) || ( pmove->flags & FL_DUCKING ) ) + { + // Adjust for super long jump module + // UNDONE -- note this should be based on forward angles, not current velocity. + if ( cansuperjump && + ( pmove->cmd.buttons & IN_DUCK ) && + ( pmove->flDuckTime > 0 ) && + Length( pmove->velocity ) > 50 ) + { + pmove->punchangle[0] = -5; + + for (i =0; i < 2; i++) + { + pmove->velocity[i] = pmove->forward[i] * PLAYER_LONGJUMP_SPEED * 1.6; + } + + pmove->velocity[2] = sqrt(2 * 800 * 56.0); + } + else + { + pmove->velocity[2] = sqrt(2 * 800 * 45.0); + } + } + else + { + pmove->velocity[2] = sqrt(2 * 800 * 45.0); + } + + // Decay it for simulation + PM_FixupGravityVelocity(); + + // Flag that we jumped. + pmove->oldbuttons |= IN_JUMP; // don't jump again until released +} + +/* +============= +PM_CheckWaterJump +============= +*/ +#define WJ_HEIGHT 8 +void PM_CheckWaterJump (void) +{ + vec3_t vecStart, vecEnd; + vec3_t flatforward; + vec3_t flatvelocity; + float curspeed; + pmtrace_t tr; + int savehull; + + // Already water jumping. + if ( pmove->waterjumptime ) + return; + + // Don't hop out if we just jumped in + if ( pmove->velocity[2] < -180 ) + return; // only hop out if we are moving up + + // See if we are backing up + flatvelocity[0] = pmove->velocity[0]; + flatvelocity[1] = pmove->velocity[1]; + flatvelocity[2] = 0; + + // Must be moving + curspeed = VectorNormalize( flatvelocity ); + + // see if near an edge + flatforward[0] = pmove->forward[0]; + flatforward[1] = pmove->forward[1]; + flatforward[2] = 0; + VectorNormalize (flatforward); + + // Are we backing into water from steps or something? If so, don't pop forward + if ( curspeed != 0.0 && ( DotProduct( flatvelocity, flatforward ) < 0.0 ) ) + return; + + VectorCopy( pmove->origin, vecStart ); + vecStart[2] += WJ_HEIGHT; + + VectorMA ( vecStart, 24, flatforward, vecEnd ); + + // Trace, this trace should use the point sized collision hull + savehull = pmove->usehull; + pmove->usehull = 2; + + tr = NS_PlayerTrace(pmove, vecStart, vecEnd, PM_NORMAL, -1 ); + + if ( tr.fraction < 1.0 && fabs( tr.plane.normal[2] ) < 0.1f ) // Facing a near vertical wall? + { + vecStart[2] += pmove->player_maxs[ savehull ][2] - WJ_HEIGHT; + VectorMA( vecStart, 24, flatforward, vecEnd ); + VectorMA( vec3_origin, -50, tr.plane.normal, pmove->movedir ); + + tr = NS_PlayerTrace(pmove, vecStart, vecEnd, PM_NORMAL, -1 ); + + if ( tr.fraction == 1.0 ) + { + pmove->waterjumptime = 2000; + pmove->velocity[2] = 225; + pmove->oldbuttons |= IN_JUMP; + pmove->flags |= FL_WATERJUMP; + } + } + + // Reset the collision hull + pmove->usehull = savehull; +} + +void PM_CheckFalling( void ) +{ + if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + { + if ((pmove->onground != -1) && !pmove->dead) + { + // Slow marines down on landing after a jump + const int theFallPenaltyThreshold = pmove->maxspeed;//BALANCE_VAR(kUnencumberedPlayerSpeed); + if(pmove->flFallVelocity > theFallPenaltyThreshold) + { + if(pmove->iuser3 == AVH_USER3_MARINE_PLAYER) + { + VectorScale(pmove->velocity, .3f, pmove->velocity); + + if(pmove->runfuncs) + { + //#ifdef AVH_CLIENT + //pmove->Con_Printf("Player landed.\n"); + //#endif + } + } + } + + // Play landing sound if we landed hard enough + if(pmove->flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD) + { + float fvol = 0.5; + char theFallPainSound[64]; + + int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); + float theVolumeScalar = 1.0f - theSilenceUpgradeLevel/3.0f; + + float theFallPainVolume = 1.0f*theVolumeScalar; + + string theAlienExtension; + bool theIsAlien = NS_GetIsPlayerAlien(theAlienExtension); + + if(theIsAlien || pmove->iuser3 == AVH_USER3_ALIEN_EMBRYO ) + { + sprintf(theFallPainSound, kFallPainSoundFormat, pmove->iuser3); + } + else + { + if ( GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_13) ) + { + sprintf(theFallPainSound, kFallPainSoundFormat, 2); + } + else + { + sprintf(theFallPainSound, kFallPainSoundFormat, 1); + } + } + + if ( pmove->waterlevel > 0 ) + { + } + else if ( pmove->flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED ) + { + // NOTE: In the original game dll , there were no breaks after these cases, causing the first one to + // cascade into the second + //switch ( RandomLong(0,1) ) + //{ + //case 0: + //PM_NSPlaySound( CHAN_VOICE, "player/pl_fallpain2.wav", 1, ATTN_NORM, 0, PITCH_NORM ); + //break; + //case 1: + PM_NSPlaySound( CHAN_VOICE, theFallPainSound, theFallPainVolume, ATTN_NORM, 0, PITCH_NORM ); + // break; + //} + fvol = 1.0; + } + else if ( pmove->flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED / 2 ) + { + qboolean tfc = false; + tfc = atoi( pmove->PM_Info_ValueForKey( pmove->physinfo, "tfc" ) ) == 1 ? true : false; + + if ( tfc ) + { + PM_NSPlaySound( CHAN_VOICE, theFallPainSound, theFallPainVolume, ATTN_NORM, 0, PITCH_NORM ); + } + + fvol = 0.85; + } + else if ( pmove->flFallVelocity < PLAYER_MIN_BOUNCE_SPEED ) + { + fvol = 0; + } + + if ( fvol > 0.0 ) + { + // Play landing step right away + pmove->flTimeStepSound = 0; + + PM_UpdateStepSound(); + + // play step sound for current texture + PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), fvol ); + + // Knock the screen around a little bit, temporary effect + pmove->punchangle[ 2 ] = pmove->flFallVelocity * 0.013; // punch z axis + + if ( pmove->punchangle[ 0 ] > 8 ) + { + pmove->punchangle[ 0 ] = 8; + } + } + } + } + + if ( pmove->onground != -1 ) + { + pmove->flFallVelocity = 0; + } + } +} + +/* +================= +PM_PlayWaterSounds + + ================= +*/ +void PM_PlayWaterSounds( void ) +{ + // Did we enter or leave water? + if ( ( pmove->oldwaterlevel == 0 && pmove->waterlevel != 0 ) || + ( pmove->oldwaterlevel != 0 && pmove->waterlevel == 0 ) ) + { + int theSilenceUpgradeLevel = AvHGetAlienUpgradeLevel(pmove->iuser4, MASK_UPGRADE_6); + float theVolume = 1.0f - theSilenceUpgradeLevel/3.0f; + + switch ( pmove->RandomLong(0,3) ) + { + case 0: + PM_NSPlaySound( CHAN_BODY, "player/pl_wade1.wav", theVolume, ATTN_NORM, 0, PITCH_NORM ); + break; + case 1: + PM_NSPlaySound( CHAN_BODY, "player/pl_wade2.wav", theVolume, ATTN_NORM, 0, PITCH_NORM ); + break; + case 2: + PM_NSPlaySound( CHAN_BODY, "player/pl_wade3.wav", theVolume, ATTN_NORM, 0, PITCH_NORM ); + break; + case 3: + PM_NSPlaySound( CHAN_BODY, "player/pl_wade4.wav", theVolume, ATTN_NORM, 0, PITCH_NORM ); + break; + } + } +} + +/* +=============== +PM_CalcRoll + +=============== +*/ +float PM_CalcRoll (vec3_t angles, vec3_t velocity, float rollangle, float rollspeed ) +{ + float sign; + float side; + float value; + vec3_t forward, right, up; + + AngleVectors (angles, forward, right, up); + + side = DotProduct (velocity, right); + + sign = side < 0 ? -1 : 1; + + side = fabs(side); + + value = rollangle; + + if (side < rollspeed) + { + side = side * value / rollspeed; + } + else + { + side = value; + } + + return side * sign; +} + +/* +============= +PM_DropPunchAngle + +============= +*/ +void PM_DropPunchAngle ( vec3_t punchangle ) +{ + float len; + + len = VectorNormalize ( punchangle ); + len -= (10.0 + len * 0.5) * pmove->frametime; + len = max( len, 0.0 ); + VectorScale ( punchangle, len, punchangle); +} + +/* +============== +PM_CheckParamters + +============== +*/ +void PM_CheckParamters( void ) +{ + float spd; + float maxspeed; + vec3_t v_angle; + + spd = ( pmove->cmd.forwardmove * pmove->cmd.forwardmove ) + + ( pmove->cmd.sidemove * pmove->cmd.sidemove ) + + ( pmove->cmd.upmove * pmove->cmd.upmove ); + spd = sqrt( spd ); + + maxspeed = pmove->clientmaxspeed; //atof( pmove->PM_Info_ValueForKey( pmove->physinfo, "maxspd" ) ); + if ( maxspeed != 0.0 ) + { + pmove->maxspeed = min( maxspeed, pmove->maxspeed ); + } + + if ( ( spd != 0.0 ) && + ( spd > pmove->maxspeed ) ) + { + float fRatio = pmove->maxspeed / spd; + pmove->cmd.forwardmove *= fRatio; + pmove->cmd.sidemove *= fRatio; + pmove->cmd.upmove *= fRatio; + } + + bool theIsImmobilized = GetHasUpgrade(pmove->iuser4, MASK_PLAYER_STUNNED) || GetHasUpgrade(pmove->iuser4, MASK_ALIEN_EMBRYO); + + if ( pmove->flags & FL_FROZEN || + pmove->flags & FL_ONTRAIN || + theIsImmobilized || + pmove->dead ) + { + pmove->cmd.forwardmove = 0; + pmove->cmd.sidemove = 0; + pmove->cmd.upmove = 0; + pmove->cmd.viewangles[0] = 0; + pmove->cmd.viewangles[1] = 0; + pmove->cmd.viewangles[2] = 0; + pmove->cmd.buttons = 0; + } + + + PM_DropPunchAngle( pmove->punchangle ); + + // Take angles from command. + if(!theIsImmobilized) + { + if ( !pmove->dead) + { + VectorCopy ( pmove->cmd.viewangles, v_angle ); + VectorAdd( v_angle, pmove->punchangle, v_angle ); + + // Set up view angles. + pmove->angles[ROLL] = PM_CalcRoll ( v_angle, pmove->velocity, pmove->movevars->rollangle, pmove->movevars->rollspeed )*4; + pmove->angles[PITCH] = v_angle[PITCH]; + pmove->angles[YAW] = v_angle[YAW]; + } + else + { + VectorCopy( pmove->oldangles, pmove->angles ); + } + } + else + { + VectorCopy( pmove->oldangles, pmove->angles ); + } + + // Set dead player view_offset + if ( pmove->dead ) + { + pmove->view_ofs[2] = PM_DEAD_VIEWHEIGHT; + } + + // Adjust client view angles to match values used on server. + if (pmove->angles[YAW] > 180.0f) + { + pmove->angles[YAW] -= 360.0f; + } + + // Set default stepsize + pmove->movevars->stepsize = NS_GetStepsize(pmove->iuser3); +} + +void PM_ReduceTimers( void ) +{ + if ( pmove->flTimeStepSound > 0 ) + { + pmove->flTimeStepSound -= pmove->cmd.msec; + if ( pmove->flTimeStepSound < 0 ) + { + pmove->flTimeStepSound = 0; + } + } + if ( pmove->flDuckTime > 0 ) + { + pmove->flDuckTime -= pmove->cmd.msec; + if ( pmove->flDuckTime < 0 ) + { + pmove->flDuckTime = 0; + } + } + if ( pmove->flSwimTime > 0 ) + { + pmove->flSwimTime -= pmove->cmd.msec; + if ( pmove->flSwimTime < 0 ) + { + pmove->flSwimTime = 0; + } + } +} + +qboolean PM_CanFlap() +{ + qboolean theCanFlap = false; + + if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) + { + if((pmove->onground == -1) && !(pmove->oldbuttons & IN_JUMP)) + { + if(AvHMUHasEnoughAlienEnergy(pmove->fuser3, kAlienEnergyFlap)) + { + // Can't hold the button down + theCanFlap = true; + } + } + } + return theCanFlap; +} + +qboolean PM_CanGlide() +{ + + if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) + { + if((pmove->onground == -1)) + { + return true; + } + } + + return false; + +} + + +qboolean PM_CanWalljump() +{ + vec3_t front; + pmtrace_t trace; + qboolean theCanWalljump = false; + + if(pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4) + { + front[0] = pmove->origin[0] + pmove->player_maxs[pmove->usehull][0] + 2; + front[1] = pmove->origin[1] + pmove->player_maxs[pmove->usehull][1] + 2; + front[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2] + 1; + + trace = NS_PlayerTrace (pmove, pmove->origin, front, PM_WORLD_ONLY, -1 ); + + if ( (trace.fraction < 1 )) + { + theCanWalljump = true; + } + } + return theCanWalljump; +} + + +void PM_Overwatch() +{ + #ifdef AVH_CLIENT + gOverwatchTargetRange = -1; + #endif + + // If overwatch is on and we have a target +// if(GetHasUpgrade(pmove->iuser4, MASK_MARINE_OVERWATCH)) +// { +// // Get target +// int thePhysIndex = (int)pmove->fuser1; +// if(thePhysIndex != -1) +// { +// physent_t* theTarget = NULL;//AvHSUGetEntity(thePhysIndex); +// +// //= &(pmove->physents[ thePhysIndex ]); +// //physent_t* theTarget = gEngfuncs.pEventAPI->EV_GetPhysent(thePhysIndex); +// +// if(theTarget) +// { +// // TODO: Take gun offset into account with vecMid? +// vec3_t vecMid; +// vec3_t vecMidEnemy; +// vec3_t vecDirToEnemy; +// vec3_t vec; +// +// VectorAdd(pmove->origin, pmove->view_ofs, vecMid); +// +// VectorCopy(theTarget->origin, vecMidEnemy);//theTarget->BodyTarget( vecMid ); +// +// // Right now just point at enemy +// //UTIL_MakeVectors() +// VectorSubtract(vecMidEnemy, vecMid, vecDirToEnemy); +// // = UTIL_VecToAngles(vecDirToEnemy); +// VectorAngles(vecDirToEnemy, vec); +// +// //vec.x = -vec.x; +// vec[0] = -vec[0]; +// +// // if (vec.y > 360) +// // vec.y -= 360; +// // +// // if (vec.y < 0) +// // vec.y += 360; +// +// +// // Set new view angles, set iHasNewViewAngles so it isn't reset on us +// VectorCopy(vec, pmove->angles); +// +// #ifdef AVH_CLIENT +// VectorCopy(pmove->angles, gTopDownViewAngles); +// iHasNewViewAngles = true; +// #endif +// +// AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); +// //VectorCopy(vec, this->pev->v_angle); +// +// // Save target range +// #ifdef AVH_CLIENT +// gOverwatchTargetRange = Length(vecDirToEnemy); +// #endif +// pmove->fuser2 = Length(vecDirToEnemy)/100.0f; +// } +// } +// } +} + +bool PM_TopDown() +{ + bool theInTopDownMode = false; + + if(GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + { + // Reset this, it seems to be getting set by the engine + //pmove->movetype = MOVETYPE_FLY; + + pmove->forward[0] = pmove->forward[1] = 0; + pmove->forward[2] = -1; + + pmove->right[0] = 1; + pmove->right[1] = pmove->right[2] = 0; + + pmove->up[0] = pmove->up[2] = 0; + pmove->up[1] = 1; + + theInTopDownMode = true; + + const AvHMapExtents& theMapExtents = GetMapExtents(); + + float theMinX = theMapExtents.GetMinMapX(); + float theMinY = theMapExtents.GetMinMapY(); + float theMaxX = theMapExtents.GetMaxMapX(); + float theMaxY = theMapExtents.GetMaxMapY(); + float theMaxZ = theMapExtents.GetMaxViewHeight(); + float theMinZ = theMapExtents.GetMinViewHeight(); + + // Modify the max to make sure commander stays inside the world + vec3_t theStartPos; + VectorCopy(pmove->origin, theStartPos); + theStartPos[2] = theMaxZ; + + vec3_t theNewStartPos; + vec3_t theEndPos; + VectorCopy(pmove->origin, theEndPos); + theEndPos[2] = theMinZ; + + float theMaxCommanderHeight = theMaxZ; + + AvHSHUGetFirstNonSolidPoint((float*)theStartPos, (float*)theEndPos, (float*)theNewStartPos); + + //theMaxZ = min(theMaxZ, theNewStartPos[2]); + +// #ifdef AVH_CLIENT +// extern DebugPointListType gSquareDebugLocations; +// DebugPoint theDebugPoint(theNewStartPos[0], theNewStartPos[1], theMaxZ - theNewStartPos[2]); +// gSquareDebugLocations.push_back(theDebugPoint); +// #endif + + float speed, drop, friction, control, newspeed; + float currentspeed, addspeed, accelspeed; + int i; + vec3_t wishvel; + vec3_t theAngles; + float fmove = 0.0f; + float smove = 0.0f; + float umove = 0.0f; + vec3_t wishdir; + float wishspeed; + + // Don't scroll when COMMANDER_MOUSECOORD set, it's indicating a world position, not a move + //if(pmove->cmd.impulse != COMMANDER_MOUSECOORD) + //{ +// #ifdef AVH_CLIENT +// if ( pmove->runfuncs ) +// { +// // Set spectator flag +// iIsSpectator = SPEC_IS_SPECTATOR; +// } +// #endif + + // Move around in normal spectator method + // friction + speed = Length (pmove->velocity); + if (speed < 1) + { + VectorCopy (vec3_origin, pmove->velocity) + } + else + { + drop = 0; + + friction = pmove->movevars->friction*5.0; // extra friction + //friction = pmove->movevars->friction*.9f; + control = speed < pmove->movevars->stopspeed ? pmove->movevars->stopspeed : speed; + drop += control*friction*pmove->frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale (pmove->velocity, newspeed, pmove->velocity); + } + + // If player moved mouse wheel, move camera up and down if we didn't just move over a view entity + qboolean theFoundEntity = false; + //float theDesiredHeight = PM_GetDesiredTopDownCameraHeight(theFoundEntity); + +// gHeightLevel += (pmove->cmd.forwardmove/300.0f); +// float theDesiredHeight = theMaxViewHeight + gHeightLevel; +// +// // Note: To have a nice smooth zoom-up effect, comment out this next line, but you'll have to +// // fix the bouncing from the drop/control/friction code above. That's where the problem is. +// // You'll also have to add an initial upwards velocity in AvHPlayer::StartTopDown() +// pmove->origin[2] = theDesiredHeight; +// +// const float kSwoopingTolerance = 1.0f; +// float theDiff = theDesiredHeight - pmove->origin[2]; +// float theFabsDiff = fabs(theDiff); +// +// if(theFabsDiff > kSwoopingTolerance) +// { +// // When we get near, don't use sudden moves, move slowly +// float theAmount = min(theFabsDiff*10, 1000); +// +// //fmove = theDiff;//pmove->cmd.forwardmove; +// if(theDiff > 0) +// fmove = -theAmount; +// else +// fmove = theAmount; +// } +// else +// { +// fmove = 0.0f; +// } + + // accelerate + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + umove = pmove->cmd.upmove; + + float theMoveTotal = fabs(fmove) + fabs(smove) + fabs(umove); + if(theMoveTotal > 5.0f) + { +// VectorNormalize (pmove->forward); +// VectorNormalize (pmove->right); +// VectorNormalize (pmove->up); +// +// for (i=0 ; i<3 ; i++) +// { +// wishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove + pmove->up[i]*umove; +// } +// //wishvel[2] += pmove->cmd.upmove; +// + PM_GetWishVelocity(wishvel); + VectorCopy (wishvel, wishdir); + + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + if (wishspeed > pmove->movevars->maxspeed) + { + VectorScale (wishvel, pmove->movevars->maxspeed/wishspeed, wishvel); + wishspeed = pmove->movevars->maxspeed; + } + + currentspeed = DotProduct(pmove->velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed > 0) + { + accelspeed = pmove->movevars->accelerate*pmove->frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove->velocity[i] += accelspeed*wishdir[i]; + + // move + VectorMA (pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); + } + } + else + { + wishvel[0] = wishvel[1] = wishvel[2] = 0.0f; + wishdir[0] = wishdir[1] = wishdir[2] = 0.0f; + pmove->velocity[2] = 0.0f; + } + //} + + // Set view down + theAngles[0] = kTopDownYaw; + theAngles[1] = kTopDownPitch; + theAngles[2] = kTopDownRoll; + + // Set angles facing down so observer knows which way to point + VectorCopy(theAngles, pmove->angles); + + // Set origin + //pmove->origin[2] = 1080; + //pmove->origin[2] = PM_GetDesiredTopDownCameraHeight(); + + #ifdef AVH_CLIENT + if ( pmove->runfuncs ) + { + VectorCopy(theAngles, gTopDownViewAngles); + //iHasNewViewAngles = true; + + // Set view origin to our real origin but at our highest map extents. This is needed to the commander's origin is actually inside the world so + // he receives nearby events, but so he looks like he's outside the world. Changing this? Make sure AvHPlayer::GetVisualOrigin() is updated also. + VectorCopy(pmove->origin, gTopDownViewOrigin); + gTopDownViewOrigin[2] = theMaxCommanderHeight; + + //iHasNewViewOrigin = true; + } + #endif + + AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); + + // Are we zooming to an area? + if(pmove->cmd.impulse == COMMANDER_MOVETO) + { + pmove->origin[0] = pmove->cmd.upmove*kWorldPosNetworkConstant; + pmove->origin[1] = pmove->cmd.sidemove*kWorldPosNetworkConstant; + VectorCopy(vec3_origin, pmove->velocity) + } + + // Clip position to map extents + float theCurrentX = pmove->origin[0]; + float theCurrentY = pmove->origin[1]; + //float theCurrentZ = pmove->origin[2]; + + pmove->origin[0] = min(max(theMinX, theCurrentX), theMaxX); + pmove->origin[1] = min(max(theMinY, theCurrentY), theMaxY); + pmove->origin[2] = min(max(theMinZ, theNewStartPos[2]), theMaxZ); + + if(pmove->runfuncs) + { + // Update view offset + #ifdef AVH_CLIENT + // Save our top down height so we can offset camera in view.cpp + // Changing this? Make sure AvHPlayer::GetVisualOrigin() is updated also. + //gTopDownHeight = theMaxCommanderHeight; + #endif + + pmove->view_ofs[2] = theMaxCommanderHeight - pmove->origin[2]; + + //pmove->Con_Printf("PMTopDown(): up: %f, side: %f, forward: %f, impulse: %d, velocity: %f\n", pmove->cmd.upmove, pmove->cmd.sidemove, pmove->cmd.forwardmove, pmove->cmd.impulse, Length(pmove->velocity)); + } + } + + // Reset view + // else if(pmove->iuser3 == AVH_USER3_VIEW_SPECIAL_LEAVE_COMMANDER) + // { + // vec3_t theAngles; + // + // // Set view down + // theAngles[0] = 0; + // theAngles[1] = 0; + // theAngles[2] = 0; + // + // // Set angles facing down so observer knows which way to point + // VectorCopy(theAngles, pmove->angles); + // + // #ifdef AVH_CLIENT + // if ( pmove->runfuncs ) + // { + // VectorCopy(theAngles, gTopDownViewAngles); + // iHasNewViewAngles = true; + // } + // #endif + // + // AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); + // + // #ifdef AVH_CLIENT + // if ( pmove->runfuncs ) + // { + // iHasNewViewAngles = true; + // pmove->iuser3 = 0; + // } + // #endif + // + // #ifdef AVH_SERVER + // pmove->iuser3 = 0; + // #endif + // } + return theInTopDownMode; +} + +void PM_Jetpack() +{ + bool theHasJetpackUpgrade = GetHasUpgrade(pmove->iuser4, MASK_UPGRADE_7) && (pmove->iuser3 == AVH_USER3_MARINE_PLAYER); + // : 243 Don't allow the player to use jetpack if being devoured + bool theIsDevoured = GetHasUpgrade(pmove->iuser4, MASK_DIGESTING); + // Turn off jetpack by default + gIsJetpacking[pmove->player_index] = false; + + if(!pmove->dead && theHasJetpackUpgrade && !theIsDevoured) + { + bool theJumpHeldDown = (pmove->cmd.buttons & IN_JUMP); + bool theHasEnoughEnergy = (pmove->fuser3 > (kJetpackMinimumEnergyToJetpack*kNormalizationNetworkFactor)); + float theTimePassed = pmove->frametime; + + // If jump is held down and we have a jetpack, apply upwards force! - added check for digestion - elven + if(theJumpHeldDown && theHasEnoughEnergy && !GetHasUpgrade(pmove->iuser4, MASK_ENSNARED) && !GetHasUpgrade(pmove->iuser4, MASK_PLAYER_STUNNED) && !GetHasUpgrade(pmove->iuser4, MASK_DIGESTING)) + { + gIsJetpacking[pmove->player_index] = true; + + // Apply upwards force to player + //pmove->velocity[2] += kJetpackForce; + + vec3_t theWishVelocity; + PM_GetWishVelocity(theWishVelocity); + + const float kBaseScalar = .6f; + + int theMinMarineSpeed = BALANCE_VAR(kBasePlayerSpeed); + if(theMinMarineSpeed == 0) + { + theMinMarineSpeed = 150; + } + + int theMaxMarineSpeed = BALANCE_VAR(kUnencumberedPlayerSpeed); + if(theMaxMarineSpeed == 0) + { + theMaxMarineSpeed = 220; + } + + float theWeightScalar = kBaseScalar + (1.0f - kBaseScalar)*((pmove->clientmaxspeed - theMinMarineSpeed)/(theMaxMarineSpeed - theMinMarineSpeed)); + + pmove->velocity[0] += (theWishVelocity[0]/pmove->clientmaxspeed)*kJetpackLateralScalar; + pmove->velocity[1] += (theWishVelocity[1]/pmove->clientmaxspeed)*kJetpackLateralScalar; + pmove->velocity[2] += theTimePassed*theWeightScalar*kJetpackForce; + + // Play an event every so often + if(pmove->runfuncs /*&& (pmove->RandomLong(0, 2) == 0)*/) + { + pmove->PM_PlaybackEventFull(0, pmove->player_index, gJetpackEventID, 0, (float *)pmove->origin, (float *)pmove->origin, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 ); + } + } + + float theJetpackEnergy = pmove->fuser3/kNormalizationNetworkFactor; + + AvHMUUpdateJetpackEnergy(gIsJetpacking[pmove->player_index], theTimePassed, theJetpackEnergy); + + pmove->fuser3 = theJetpackEnergy*kNormalizationNetworkFactor; + } + + //pmove->Con_Printf("Jetpacking: %d\n", gIsJetpacking); +} + +/* +============= +PlayerMove + +Returns with origin, angles, and velocity modified in place. + +Numtouch and touchindex[] will be set if any of the physents +were contacted during the move. +============= +*/ +void PM_PlayerMove ( qboolean server ) +{ + physent_t *pLadder = NULL; + + // Are we running server code? + pmove->server = server; + + if (pmove->cmd.buttons & IN_JUMP) + { + int a =0; + } + + int theMaxSpeed = 0; + + // Adjust speeds etc. + PM_CheckParamters(); + + // Assume we don't touch anything + pmove->numtouch = 0; + + // # of msec to apply movement + pmove->frametime = pmove->cmd.msec * 0.001; + +// if(pmove->runfuncs) +// { +// pmove->Con_Printf("pmove->frametime: %f\n", pmove->frametime); +// } + + PM_ReduceTimers(); + + bool theIsGestating = GetHasUpgrade(pmove->iuser4, MASK_ALIEN_EMBRYO); + bool theIsParalyzed = GetHasUpgrade(pmove->iuser4, MASK_PLAYER_STUNNED); + + if(!theIsParalyzed && !theIsGestating) + { + // Convert view angles to vectors + AngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up); + } + + //if(pmove->cmd.impulse == COMMANDER_MOUSECOORD) +// if(pmove->iuser3 == AVH_USER3_VIS_SPECIAL_TOPDOWN) +// { +// if((pmove->cmd.impulse != COMMANDER_MOVETO) && (pmove->cmd.impulse != MESSAGE_NULL)) +// { +// return; +// } +// } + + bool theIsDucking = AvHMUGetCanDuck(pmove->iuser3) && (pmove->flags & FL_DUCKING); + pmove->usehull = AvHMUGetHull(theIsDucking, pmove->iuser3); + + //pmove->Con_Printf("Using hull: %d\n", pmove->usehull); + + //PM_ShowClipBox(); + + // Clear movement when building, giving orders, or anything other than scrolling + if(GetHasUpgrade(pmove->iuser4, MASK_TOPDOWN)) + { + if((pmove->cmd.impulse != COMMANDER_SCROLL) && (pmove->cmd.impulse != COMMANDER_MOVETO)) + { + pmove->cmd.sidemove = pmove->cmd.upmove = pmove->cmd.forwardmove = 0; + pmove->cmd.buttons = 0; + } + } + + if ( pmove->flags & FL_FROZEN ) + return; + + // Special handling for spectator and observers. (iuser1 is set if the player's in observer mode) + if ( pmove->spectator || pmove->iuser1 > 0 ) + { + PM_SpectatorMove(); + PM_CategorizePosition(); + return; + } + +// if(GetHasUpgrade(pmove->iuser4, MASK_MARINE_OVERWATCH)) +// { +// PM_Overwatch(); +// } + + PM_TopDown(); + + // Paralyzed players and embryos can't move + if(theIsParalyzed || theIsGestating) + { + PM_Physics_Toss(); + return; + } + + // Always try and unstick us unless we are in NOCLIP mode + if ( pmove->movetype != MOVETYPE_NOCLIP && pmove->movetype != MOVETYPE_NONE ) + { + if ( PM_CheckStuck() ) + { + return; // Can't move, we're stuck + } + } + + // Now that we are "unstuck", see where we are ( waterlevel and type, pmove->onground ). + PM_CategorizePosition(); + + // Store off the starting water level + pmove->oldwaterlevel = pmove->waterlevel; + + // If we are not on ground, store off how fast we are moving down + if ( pmove->onground == -1 ) + { + pmove->flFallVelocity = -pmove->velocity[2]; + } + + g_onladder[pmove->player_index] = 0; + + PM_Jetpack(); + + PM_AlienAbilities(); + + NS_UpdateWallsticking(); + + // Don't run ladder code if dead or on a train, or a lerk or a skulk + bool theIsFlyingAlien = (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER3) && (pmove->onground == -1); + bool theIsSkulk = (pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1); + +// if ( PM_GetIsBlinking() ) { + if ( pmove->iuser3 == AVH_USER3_ALIEN_PLAYER4 && ( PM_GetIsBlinking() || pmove->velocity[2] > 175 || pmove->velocity[2] < -175 )) { + g_onladder[pmove->player_index] = 0; + } + else { + if ( !pmove->dead && !(pmove->flags & FL_ONTRAIN) && !gIsJetpacking[pmove->player_index] && !GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) && !theIsFlyingAlien && !theIsSkulk) + { + pLadder = PM_Ladder(); + if ( pLadder ) + { + g_onladder[pmove->player_index] = 1; + } + } + } + + //pmove->Con_DPrintf("g_onladder: %d\n", g_onladder); + + PM_UpdateStepSound(); + + PM_Duck(); + + // Don't run ladder code if dead or on a train + if ( !pmove->dead && !(pmove->flags & FL_ONTRAIN) && !gIsJetpacking[pmove->player_index] && !GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + { + if ( pLadder ) + { + PM_LadderMove( pLadder ); + } + else if ( pmove->movetype != MOVETYPE_WALK && + pmove->movetype != MOVETYPE_NOCLIP ) + { + // Clear ladder stuff unless player is noclipping + // it will be set immediately again next frame if necessary + pmove->movetype = MOVETYPE_WALK; + } + } + + // Slow down, I'm pulling it! (a box maybe) but only when I'm standing on ground + if ( ( pmove->onground != -1 ) && ( pmove->cmd.buttons & IN_USE) ) + { + VectorScale( pmove->velocity, 0.3, pmove->velocity ); + } + + // Reset gravity to 1.0, in case we're not gliding anymore. This will get changed + // in PM_Jump if player is still holding down the key. + // This assumes only players in here! Sounds problematic to me. +// if(pmove->iuser3 == AVH_USER3_GLIDE) +// { +// pmove->gravity = 1.0f; +// } + + // Handle movement + switch ( pmove->movetype ) + { + default: + pmove->Con_Printf("Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", pmove->movetype, pmove->server); + break; + + case MOVETYPE_NONE: + break; + + case MOVETYPE_NOCLIP: + PM_NoClip(); + break; + + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + PM_Physics_Toss(); + break; + + case MOVETYPE_FLY: + + PM_CheckWater(); + + // Was jump button pressed? + // If so, set velocity to 270 away from ladder. This is currently wrong. + // Also, set MOVE_TYPE to walk, too. + if ( pmove->cmd.buttons & IN_JUMP) + { + if ( !pLadder ) + { + PM_Jump (); + } + } + else + { + pmove->oldbuttons &= ~IN_JUMP; + } + + // Perform the move accounting for any base velocity. + VectorAdd (pmove->velocity, pmove->basevelocity, pmove->velocity); + PM_FlyMove (); + VectorSubtract (pmove->velocity, pmove->basevelocity, pmove->velocity); + break; + + case MOVETYPE_WALK: + if ( !PM_InWater() ) + { + PM_AddCorrectGravity(); + } + + // If we are leaping out of the water, just update the counters. + // : 0000972 + if (pmove->waterjumptime && !(pmove->waterlevel == 0 && pmove->iuser3 == AVH_USER3_ALIEN_PLAYER1)) + // : +// if ( pmove->waterjumptime ) + { + PM_WaterJump(); + PM_FlyMove(); + + // Make sure waterlevel is set correctly + PM_CheckWater(); + return; + } + + // If we are swimming in the water, see if we are nudging against a place we can jump up out + // of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0 + if ( pmove->waterlevel >= 2 ) + { + if ( pmove->waterlevel == 2 ) + { + PM_CheckWaterJump(); + } + + // If we are falling again, then we must not trying to jump out of water any more. + if ( pmove->velocity[2] < 0 && pmove->waterjumptime ) + { + pmove->waterjumptime = 0; + } + + // Was jump button pressed? + if (pmove->cmd.buttons & IN_JUMP) + { + PM_Jump (); + } + else + { + pmove->oldbuttons &= ~IN_JUMP; + } + + // Perform regular water movement + PM_WaterMove(); + + VectorSubtract (pmove->velocity, pmove->basevelocity, pmove->velocity); + + // Get a final position + PM_CategorizePosition(); + } + else + + // Not underwater + { + + // Was jump button pressed? + if ( pmove->cmd.buttons & IN_JUMP ) + { + if ( !pLadder ) + { + PM_Jump (); + } + } + else + { + pmove->oldbuttons &= ~IN_JUMP; + } + + // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor, + // we don't slow when standing still, relative to the conveyor. + if ((pmove->onground != -1 && gIsJetpacking[pmove->player_index] == 0) || GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + { + if(!GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + pmove->velocity[2] = 0.0; + PM_Friction(); + } + + // Make sure velocity is valid. + PM_CheckVelocity(); + + // Are we on ground now + if ( (pmove->onground != -1 && gIsJetpacking[pmove->player_index] == 0) || GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING) ) + { + PM_WalkMove(); + } + else + { + PM_AirMove(); // Take into account movement when in air. + } + + // Set final flags. + PM_CategorizePosition(); + + //float theVelocityLength = Length(pmove->velocity); + //pmove->Con_Printf("Max speed: %f, velocity: %f\n", pmove->maxspeed, theVelocityLength); + + // Now pull the base velocity back out. + // Base velocity is set if you are on a moving object, like + // a conveyor (or maybe another monster?) + VectorSubtract (pmove->velocity, pmove->basevelocity, pmove->velocity ); + + // Make sure velocity is valid. + PM_CheckVelocity(); + + // Add any remaining gravitational component. + if ( !PM_InWater() ) + { + PM_FixupGravityVelocity(); + } + + // If we are on ground, no downward velocity. + if((pmove->onground != -1 && gIsJetpacking[pmove->player_index] == 0) && !GetHasUpgrade(pmove->iuser4, MASK_WALLSTICKING)) + { + pmove->velocity[2] = 0; + } + + // See if we landed on the ground with enough force to play + // a landing sound. + PM_CheckFalling(); + } + + + // Did we enter or leave the water? + PM_PlayWaterSounds(); + break; + } +} + +void PM_CreateStuckTable( void ) +{ + float x, y, z; + int idx; + int i; + float zi[3]; + + memset(rgv3tStuckTable, 0, 54 * sizeof(vec3_t)); + + idx = 0; + // Little Moves. + x = y = 0; + // Z moves + for (z = -0.125 ; z <= 0.125 ; z += 0.125) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + idx++; + } + x = z = 0; + // Y moves + for (y = -0.125 ; y <= 0.125 ; y += 0.125) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + idx++; + } + y = z = 0; + // X moves + for (x = -0.125 ; x <= 0.125 ; x += 0.125) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + idx++; + } + + // Remaining multi axis nudges. + for ( x = - 0.125; x <= 0.125; x += 0.250 ) + { + for ( y = - 0.125; y <= 0.125; y += 0.250 ) + { + for ( z = - 0.125; z <= 0.125; z += 0.250 ) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + idx++; + } + } + } + + // Big Moves. + x = y = 0; + zi[0] = 0.0f; + zi[1] = 1.0f; + zi[2] = 6.0f; + + for (i = 0; i < 3; i++) + { + // Z moves + z = zi[i]; + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + idx++; + } + + x = z = 0; + + // Y moves + for (y = -2.0f ; y <= 2.0f ; y += 2.0) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + idx++; + } + y = z = 0; + // X moves + for (x = -2.0f ; x <= 2.0f ; x += 2.0f) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + idx++; + } + + // Remaining multi axis nudges. + for (i = 0 ; i < 3; i++) + { + z = zi[i]; + + for (x = -2.0f ; x <= 2.0f ; x += 2.0f) + { + for (y = -2.0f ; y <= 2.0f ; y += 2.0) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + idx++; + } + } + } +} + + + + +/* +This modume implements the shared player physics code between any particular game and +the engine. The same PM_Move routine is built into the game .dll and the client .dll and is +invoked by each side as appropriate. There should be no distinction, internally, between server +and client. This will ensure that prediction behaves appropriately. +*/ + +void PM_Move ( struct playermove_s *ppmove, int server ) +{ + assert( pm_shared_initialized ); + + pmove = ppmove; + + PM_SetHulls(); + + PM_PlayerMove( ( server != 0 ) ? true : false ); + + if ( pmove->onground != -1 ) + { + pmove->flags |= FL_ONGROUND; + } + else + { + pmove->flags &= ~FL_ONGROUND; + } + + // In single player, reset friction after each movement to FrictionModifier Triggers work still. + if ( !pmove->multiplayer && ( pmove->movetype == MOVETYPE_WALK ) ) + { + pmove->friction = 1.0f; + } + + //int theRandomNumber = pmove->RandomLong(0, 100); + // + //#ifdef AVH_CLIENT + //if(pmove->runfuncs) + //{ + // PM_DebugLocations(theRandomNumber); + //} + //#endif + + // Remember most predicted player origin (only if runfuncs?) + if(pmove->runfuncs) + { + VectorCopy(pmove->origin, gPredictedPlayerOrigin); + VectorCopy(pmove->view_ofs, gPredictedPlayerVOfs); + } + + /////////////////////////////// + // Begin Max's Code + /////////////////////////////// + + // If the current orientation is different than the desired orientation, + // interpolate between them. + + vec3_t currentAngles; + vec3_t targetAngles; + + #ifdef AVH_SERVER + + NS_FixWallstickingAngles(pmove->vuser1); + NS_FixWallstickingAngles(pmove->vuser2); + + VectorCopy(pmove->vuser1, currentAngles); + VectorCopy(pmove->vuser2, targetAngles); + + #endif + + #ifdef AVH_CLIENT + + NS_FixWallstickingAngles(gPlayerAngles); + NS_FixWallstickingAngles(gTargetPlayerAngles); + + VectorCopy(gPlayerAngles, currentAngles); + VectorCopy(gTargetPlayerAngles, targetAngles); + + #endif + + if (VectorCompare(currentAngles, targetAngles) == 0) + { + + Quat src = Quat(currentAngles).Unit(); + Quat dst = Quat(targetAngles).Unit(); + + Quat rot = (dst * src.Conjugate()).Unit(); + + // Compute the axis and angle we need to rotate about to go from src + // to dst. + + float angle = acosf(rot.w) * 2; + float sinAngle = sqrtf(1.0f - rot.w * rot.w); + + if (fabs(sinAngle) < 0.0005f) + { + sinAngle = 1; + } + + vec3_t axis; + + axis[0] = rot.x / sinAngle; + axis[1] = rot.y / sinAngle; + axis[2] = rot.z / sinAngle; + + // Wrap the angle to the range -PI to PI + angle = WrapFloat(angle, -M_PI, M_PI); + + // Amount to rotate this frame. + float frameAngle = kSkulkRotationRate * pmove->frametime; + + if (fabs(angle) <= frameAngle) + { + // If we are very close, just jump to the goal orientation. + VectorCopy(targetAngles, currentAngles); + } + else + { + + Quat final; + + if (angle < 0) + { + final = Quat(-frameAngle, axis) * src; + } + else + { + final = Quat(frameAngle, axis) * src; + } + + vec3_t xAxis; + vec3_t yAxis; + vec3_t zAxis; + + final.GetVectors(xAxis, yAxis, zAxis); + + VectorsToAngles(yAxis, xAxis, zAxis, currentAngles); + + } + + } + + #ifdef AVH_SERVER + VectorCopy(currentAngles, pmove->vuser1); + #endif + + #ifdef AVH_CLIENT + VectorCopy(currentAngles, gPlayerAngles); + #endif + + /////////////////////////////// + // End Max's Code + /////////////////////////////// + +} + +//void PM_GetEntityList(PhysEntListType& outList) +//{ +// physent_t* theTarget = NULL; +// +// for (i = 0; i < MAX_PHYSENTS; i++) +// { +// theTarget = pmove->physents[i]; +// outList.push_back(theTarget); +// } +// +//} + + +int PM_GetVisEntInfo( int ent ) +{ + if ( ent >= 0 && ent <= pmove->numvisent ) + { + return pmove->visents[ ent ].info; + } + return -1; +} + +int PM_GetPhysEntInfo( int ent ) +{ + if ( ent >= 0 && ent <= pmove->numphysent) + { + return pmove->physents[ ent ].info; + } + return -1; +} + +void PM_Init( struct playermove_s *ppmove ) +{ + assert( !pm_shared_initialized ); + + pmove = ppmove; + + PM_CreateStuckTable(); + PM_InitTextureTypes(); + PM_InitBoxHull(); + + pm_shared_initialized = 1; +} + + +bool PM_ViewTraceEntity(float inOriginX, float inOriginY, float inOriginZ, float inDirX, float inDirY, float inDirZ, int& outIndex) +{ + bool theSuccess = false; + + vec3_t theTraceEnd; + vec3_t theNormal; + theNormal[0] = inDirX; + theNormal[1] = inDirY; + theNormal[2] = inDirZ; + VectorScale(theNormal, 8012, theTraceEnd); + + vec3_t theTraceStart; + theTraceStart[0] = inOriginX; + theTraceStart[1]= inOriginY; + theTraceStart[2] = inOriginZ; + + pmtrace_t* theTrace = NULL; + bool theDone = false; + + do + { + theTrace = pmove->PM_TraceLine(theTraceStart, theTraceEnd, PM_TRACELINE_ANYVISIBLE, 2, -1); + + if(!theTrace || theTrace->fraction < 0.0001f || theTrace->ent > 0) + { + theDone = true; + } + else + { + // Trace again from here + VectorCopy(theTrace->endpos, theTraceStart); + } + + } while(!theDone); + + if(theTrace && (theTrace->ent > 0)) + { + outIndex = theTrace->ent; + theSuccess = true; + } + return theSuccess; +} + +bool GetIsEntityAPlayer(int inPhysIndex) +{ + bool theEntIsPlayer = false; + + physent_t* theEntity = NULL; + if((inPhysIndex > 0) && (inPhysIndex <= pmove->numphysent)) + { + theEntity = pmove->physents + inPhysIndex; + } + + if(theEntity) + { + const char* theName = theEntity->name; + if(!strcmp(theName, kAvHPlayerClassName)) + { + theEntIsPlayer = true; + } + } + + return theEntIsPlayer; +} + + +bool PM_ViewTracePlayer(float inOriginX, float inOriginY, float inOriginZ, float inEndX, float inEndY, float inEndZ, int& outIndex) +{ + bool theSuccess = false; + + #ifdef AVH_CLIENT + //gSquareDebugLocations.clear(); + #endif + + vec3_t theTraceEnd; + //vec3_t theNormal; + //theNormal[0] = inDirX; + //theNormal[1] = inDirY; + //theNormal[2] = inDirZ; + //VectorScale(theNormal, 8012, theTraceEnd); + theTraceEnd[0] = inEndX; + theTraceEnd[1] = inEndY; + theTraceEnd[2] = inEndZ; + + vec3_t theTraceStart; + theTraceStart[0] = inOriginX; + theTraceStart[1]= inOriginY; + theTraceStart[2] = inOriginZ; + + #ifdef AVH_CLIENT + DebugPoint thePoint; + thePoint.x = theTraceStart[0]; + thePoint.y = theTraceStart[1]; + thePoint.z = theTraceStart[2]; + //gSquareDebugLocations.push_back(thePoint); + + thePoint.x = theTraceEnd[0]; + thePoint.y = theTraceEnd[1]; + thePoint.z = theTraceEnd[2]; + //gSquareDebugLocations.push_back(thePoint); + #endif + +// pmtrace_t* theTrace = NULL; +// bool theDone = false; + +// do +// { + pmtrace_t theTraceStruct; + + theTraceStruct = NS_PlayerTrace(pmove, theTraceStart, theTraceEnd, PM_NORMAL, -1); + int theEntIndex = theTraceStruct.ent; + +// //pmtrace_t (*PM_PlayerTrace) (vec3_t start, vec3_t end, int traceFlags, int ignore_pe ); +// //theTrace = pmove->PM_TraceLine(theTraceStart, theTraceEnd, PM_TRACELINE_ANYVISIBLE, 2, -1); +// bool theEntityIsAPlayer = false; +// +// if(!theTrace || theTrace->fraction < 0.0001f || (theEntityIsAPlayer = GetIsEntityAPlayer(theTrace->ent))) +// { +// theDone = true; +// } +// else +// { +// // Trace again from here +// VectorCopy(theTrace->endpos, theTraceStart); +// } + + //if(!theDone || theEntityIsAPlayer) + if(theEntIndex > 0) + { + #ifdef AVH_CLIENT + DebugPoint thePoint; + thePoint.x = theTraceStruct.endpos[0]; + thePoint.y = theTraceStruct.endpos[1]; + thePoint.z = theTraceStruct.endpos[2]; + //gSquareDebugLocations.push_back(thePoint); + #endif + } + +// } while(!theDone); + + if(theEntIndex > 0) + //if(theTrace && (theTrace->ent > 0)) + { + outIndex = theEntIndex; + //outIndex = theTrace->ent; + theSuccess = true; + } + return theSuccess; +} + + + diff --git a/main/source/ui/UIHud.cpp b/main/source/ui/UIHud.cpp index 8bac6cb..bd81dde 100644 --- a/main/source/ui/UIHud.cpp +++ b/main/source/ui/UIHud.cpp @@ -574,6 +574,13 @@ void UIHud::ToggleEditMode(void) } } +void UIHud::ShowMouse(void) +{ + // For now this also changes us into edit mode + if ( this->mManager.InMouseMode() == false ) + this->mManager.ToggleMouse(); +} + void UIHud::ToggleMouse(void) { // For now this also changes us into edit mode diff --git a/main/source/ui/UIHud.h b/main/source/ui/UIHud.h index 8268038..2d77846 100644 --- a/main/source/ui/UIHud.h +++ b/main/source/ui/UIHud.h @@ -65,6 +65,8 @@ public: virtual void ToggleMouse(void); + virtual void ShowMouse(void); + virtual int UpdateClientData(client_data_t *cdata, float time); virtual void SetMusicAllowed(bool inState); diff --git a/main/source/util/STLUtil.cpp b/main/source/util/STLUtil.cpp index bff0b68..accc8c0 100644 --- a/main/source/util/STLUtil.cpp +++ b/main/source/util/STLUtil.cpp @@ -229,6 +229,12 @@ string BuildAbridgedString(const string& inString, int inMaxLen) return theAbridgedString; } +static char *ignoreSounds[] = { + "vox/ssay82.wav", + "vox/ssay83.wav", + 0 +}; + // Pass in relative path, do search on path including mod directory, return files relative to mod directory bool BuildFileList(const string& inBaseDirectoryName, const string& inDirectoryName, const string& inFileExtension, CStringList& outList) { @@ -288,8 +294,27 @@ bool BuildFileList(const string& inBaseDirectoryName, const string& inDirectoryN std::replace(theFullFileName.begin(), theFullFileName.end(), '\\', '/'); theCString = theFullFileName; - outList.push_back(theCString); - theSuccess = true; +#ifdef AVH_SERVER + // Ignore ssay82 and ssay83 till we can client patch this .. + int i=0; + bool found = false; + while ( ignoreSounds[i] != 0 ) { + if ( !strcmp(ignoreSounds[i], theCString)) + { + found = true; + } + i++; + } + if (found == false) + { +#endif + outList.push_back(theCString); + theSuccess = true; +#ifdef AVH_SERVER + } + // +#endif + #ifdef WIN32 } while(FindNextFile(theFileHandle, &theFindData));