/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Combat/playerdata.cpp $* * * * $Author:: Byon_g $* * * * $Modtime:: 3/29/02 3:40p $* * * * $Revision:: 38 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "playerdata.h" #include "debug.h" #include "chunkio.h" #include "wwpacket.h" #include "persistfactory.h" #include "combatchunkid.h" #include "combat.h" #include "hud.h" #include "soldier.h" #include "systeminfolog.h" #include "stackdump.h" /* ** */ PlayerDataClass::PlayerDataClass( void ) : Score( 0 ), Money( 0 ), PunishTimer( 0 ) { Stats_Reset(); } PlayerDataClass::~PlayerDataClass( void ) { PlayerInfoLog::Append_To_Log(this); if ( Get_GameObj() ) { Get_GameObj()->Set_Player_Data( NULL ); } } /* ** */ enum { CHUNKID_VARIABLES = 201011247, CHUNKID_STATS_WEAPON, MICROCHUNKID_SCORE = 1, MICROCHUNKID_MONEY, MICROCHUNKID_STAT_TIME_IN_GAME, MICROCHUNKID_STAT_ENEMIES_KILLED, MICROCHUNKID_STAT_ALLIES_KILLED, MICROCHUNKID_STAT_SHOTS_FIRED, MICROCHUNKID_STAT_HEAD_SHOTS, MICROCHUNKID_STAT_TORSO_SHOTS, MICROCHUNKID_STAT_ARM_SHOTS, MICROCHUNKID_STAT_LEG_SHOTS, MICROCHUNKID_STAT_CROTCH_CHOTS, MICROCHUNKID_STAT_FINAL_HEALTH, MICROCHUNKID_STAT_POWERUPS, MICROCHUNKID_STAT_VEHICLES_DESTROYED, MICROCHUNKID_STAT_TIME_IN_VEHICLES, MICROCHUNKID_STAT_KILLS_FROM_VEHICLES, MICROCHUNKID_STAT_SQUISHES, MICROCHUNKID_STAT_CREDITS_GRANTED, MICROCHUNKID_STAT_BUILDINGS_DESTROYED, MICROCHUNKID_STAT_HEAD_HITS, MICROCHUNKID_STAT_TORSO_HITS, MICROCHUNKID_STAT_ARM_HITS, MICROCHUNKID_STAT_LEG_HITS, MICROCHUNKID_STAT_CROTCH_HITS, MICROCHUNKID_STAT_WEAPON_ID, MICROCHUNKID_STAT_WEAPON_COUNT, }; bool PlayerDataClass::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_VARIABLES ); WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_SCORE, Score, float ); WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_MONEY, Money, float ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_TIME_IN_GAME, StatTimeInGame ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_ENEMIES_KILLED, StatEnemiesKilled ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_ALLIES_KILLED, StatAlliesKilled ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_SHOTS_FIRED, StatShotsFired ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_HEAD_SHOTS, StatHeadShots ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_TORSO_SHOTS, StatTorsoShots ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_ARM_SHOTS, StatArmShots ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_LEG_SHOTS, StatLegShots ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_CROTCH_CHOTS, StatCrotchShots ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_FINAL_HEALTH, StatFinalHealth ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_POWERUPS, StatPowerups ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_VEHICLES_DESTROYED, StatVehiclesDestroyed ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_TIME_IN_VEHICLES, StatTimeInVehicles ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_KILLS_FROM_VEHICLES, StatKillsFromVehicles ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_SQUISHES, StatSquishes ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_CREDITS_GRANTED, StatCreditsGranted ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_BUILDINGS_DESTROYED, StatBuildingsDestroyed ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_HEAD_HITS, StatHeadHits ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_TORSO_HITS, StatTorsoHits ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_ARM_HITS, StatArmHits ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_LEG_HITS, StatLegHits ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_CROTCH_HITS, StatCrotchHits ); csave.End_Chunk(); for (int i = 0; i < StatWeaponIDFired.Count(); i++) { int id = StatWeaponIDFired[i]; int count = StatWeaponFireCount[i]; csave.Begin_Chunk( CHUNKID_STATS_WEAPON ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_WEAPON_ID, id ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STAT_WEAPON_COUNT, count ); csave.End_Chunk(); } return true; } bool PlayerDataClass::Load( ChunkLoadClass &cload ) { StatTimeInSession=0.0f; StatLastWeaponIndex = -1; WWASSERT( StatWeaponIDFired.Count() == 0 ); WWASSERT( StatWeaponFireCount.Count() == 0 ); while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_SCORE, Score, float ); READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_MONEY, Money, float ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_TIME_IN_GAME, StatTimeInGame ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_ENEMIES_KILLED, StatEnemiesKilled ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_ALLIES_KILLED, StatAlliesKilled ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_SHOTS_FIRED, StatShotsFired ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_HEAD_SHOTS, StatHeadShots ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_TORSO_SHOTS, StatTorsoShots ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_ARM_SHOTS, StatArmShots ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_LEG_SHOTS, StatLegShots ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_CROTCH_CHOTS, StatCrotchShots ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_FINAL_HEALTH, StatFinalHealth ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_POWERUPS, StatPowerups ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_VEHICLES_DESTROYED, StatVehiclesDestroyed ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_TIME_IN_VEHICLES, StatTimeInVehicles ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_KILLS_FROM_VEHICLES, StatKillsFromVehicles ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_SQUISHES, StatSquishes ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_CREDITS_GRANTED, StatCreditsGranted ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_BUILDINGS_DESTROYED, StatBuildingsDestroyed ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_HEAD_HITS, StatHeadHits ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_TORSO_HITS, StatTorsoHits ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_ARM_HITS, StatArmHits ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_LEG_HITS, StatLegHits ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_CROTCH_HITS, StatCrotchHits ); default: Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } break; case CHUNKID_STATS_WEAPON: { int id = 0; int count = 0; while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_WEAPON_ID, id ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STAT_WEAPON_COUNT, count ); default: Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } WWASSERT( id != 0 ); StatWeaponIDFired.Add(id); StatWeaponFireCount.Add(count); break; } default: Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Chunk(); } return true; } void PlayerDataClass::Reset_Player(void) { Stats_Reset(); Set_Score( 0 ); Set_Money( 0 ); } void PlayerDataClass::Increment_Score( float add ) { if (!CombatManager::Is_Gameplay_Permitted()) { return; } // // Look out for very big increments // static int warning_num = 0; if (add >= 10000 && ++warning_num < 10) { WWDEBUG_SAY(("* PlayerDataClass::Increment_Score(%10.2f) - is this ok?\n", add)); LOG_CALL_STACK; } Score = (float)Score + add; // // TSS101001 - let's try giving 1 credit per point // if (add > 0) { Increment_Money(add); } } void PlayerDataClass::Increment_Money( float add ) { if (add > 0 && !CombatManager::Is_Gameplay_Permitted()) { return; } // // Look out for very big increments // #pragma message ("(gth) this caused a crash and seems like debugging code so I'm disabling it...") #if 0 static int warning_num = 0; if (add >= 10000 && ++warning_num < 10) { WWDEBUG_SAY(("* PlayerDataClass::Increment_Money(%f) - is this ok?\n", add)); LOG_CALL_STACK; } #endif Money = (float)Money + add; if ( add > 0 ) { Stats_Add_Credit_Grant( (int) add ); } //Set_Object_Dirty_Bit(NetworkObjectClass::BIT_OCCASIONAL, true); } /* ** */ void PlayerDataClass::Apply_Damage_Points( float points, DamageableGameObj * victim ) { //Increment_Score( ::fabs(points) ); Increment_Score( points ); // If this is the star's PlayerData, display the points on the HUD if ( COMBAT_STAR && COMBAT_STAR->Get_Player_Data() == this ) { HUDClass::Display_Points( points ); } } void PlayerDataClass::Apply_Death_Points( float points, DamageableGameObj * victim ) { Increment_Score( points ); if ( Get_GameObj() == COMBAT_STAR && COMBAT_STAR != NULL ) { HUDClass::Display_Points( points ); } } bool PlayerDataClass::Purchase_Item( int cost ) { bool retval = false; if (cost >= 0) { // // Check to ensure the player has enough cash // float money = (float)Money; if (money >= cost) { /* money -= cost; Money = money; */ // // Use Increment_Money so that dirty is set. // Increment_Money(-1 * cost); retval = true; } } return retval; } // // Import_Rare // void PlayerDataClass::Import_Occasional( BitStreamClass & packet ) { // // Get the score and money from the server // float score = packet.Get(score); Score = score; float money = packet.Get(money); Money = money; } // // Export_Rare // void PlayerDataClass::Export_Occasional( BitStreamClass & packet ) { // // Transmit the score and money // packet.Add ((float)Score); packet.Add ((float)Money); } /* ** Stats */ void PlayerDataClass::Stats_Reset( void ) { StatTimeInGame = 0.0f; StatTimeInSession = 0.0f; StatEnemiesKilled = 0; StatAlliesKilled = 0; StatShotsFired = 0; StatHeadShots = 0; StatTorsoShots = 0; StatArmShots = 0; StatLegShots = 0; StatCrotchShots = 0; StatFinalHealth = 0; StatPowerups = 0; StatVehiclesDestroyed = 0; StatTimeInVehicles = 0; StatKillsFromVehicles = 0; StatSquishes = 0; StatCreditsGranted = 0; StatBuildingsDestroyed = 0; StatHeadHits = 0; StatTorsoHits = 0; StatArmHits = 0; StatLegHits = 0; StatCrotchHits = 0; StatLastWeaponIndex = -1; StatWeaponIDFired.Delete_All(); StatWeaponFireCount.Delete_All(); } void PlayerDataClass::Stats_Add_Weapon_Fired( int weapon_id ) { // Check for using same weapon as last time if ((StatLastWeaponIndex >= 0) && (StatWeaponIDFired[StatLastWeaponIndex] == weapon_id)) { StatWeaponFireCount[StatLastWeaponIndex]++; return; } // See if we already have this weapon for (int i = 0; i < StatWeaponIDFired.Count(); i++) { if (StatWeaponIDFired[i] == weapon_id) { StatLastWeaponIndex = i; StatWeaponFireCount[StatLastWeaponIndex]++; return; } } // Add this new weapon StatLastWeaponIndex = StatWeaponIDFired.Count(); StatWeaponIDFired.Add(weapon_id); StatWeaponFireCount.Add(1); } //------------------------------------------------------------------------------------ int PlayerDataClass::Get_Weapon_Fired_Count(void) const { return StatWeaponIDFired.Count(); } //------------------------------------------------------------------------------------ bool PlayerDataClass::Get_Weapon_Fired(int index, unsigned long& weaponID, unsigned long& firedCount) const { if ((index >= 0) && (index < StatWeaponIDFired.Count())) { weaponID = (unsigned long)StatWeaponIDFired[index]; firedCount = (unsigned long)StatWeaponFireCount[index]; return true; } return false; } /* bool PlayerDataClass::Purchase_Item( int cost ) { bool retval = false; // // Check to ensure the player has enough cash // int score = (int)Score; if (score >= cost) { score -= cost; Score = score; retval = true; } return retval; } */