472 lines
14 KiB
C++
472 lines
14 KiB
C++
|
/*
|
||
|
** 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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** 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;
|
||
|
}
|
||
|
*/
|
||
|
|