Descent3/netgames/dmfc/dmfcpinfo.cpp

402 lines
10 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/Main/Dmfc/dmfcpinfo.cpp $
* $Revision: 1.1.1.1 $
* $Date: 2003/08/26 03:57:21 $
* $Author: kevinb $
*
* DMFC Playerinfo class
*
* $Log: dmfcpinfo.cpp,v $
* Revision 1.1.1.1 2003/08/26 03:57:21 kevinb
* initial 1.5 import
*
*
* 21 7/09/99 2:54p Jeff
* handle gametime better (pause it when needed) if the server is 'waiting
* for players'
*
* 20 7/07/99 6:10p Jeff
* don't count the time that a player is in observer mode for death/kill
* messages
*
* 19 5/13/99 4:55p Ardussi
* changes for compiling on the Mac
*
* 18 5/12/99 11:05p Jeff
* dmfc and multiplayer games now have endian friendly packets (*whew*)
*
* 17 4/25/99 7:19p Jeff
* added code to handle suicides in pinfo
*
* 16 3/17/99 12:25p Jeff
* converted DMFC to be COM interface
*
* 15 2/11/99 12:51a Jeff
* changed names of exported variables
*
* 13 9/30/98 3:50p Jeff
* general improvements (many)
*
* 12 9/29/98 3:04p Jeff
* added time in game and start_time support
*
* 11 9/25/98 8:02p Jason
* added checks for jeff
*
* 10 9/25/98 7:56p Jeff
* fixed the hosing of the data in UnpackData
*
* 9 9/25/98 7:25p Jeff
*
* 8 9/25/98 4:50p Jeff
*
* 7 9/24/98 5:52p Jeff
* starting adding statistical death messages, checked in for testing
*
* 6 9/21/98 7:11p Jeff
* made InputCommand interface API and moved existing input commands to
* the interface. Changed mprintf/ASSERT so they are valid in DMFC
*
* 5 8/04/98 3:29p Jeff
* completion of player records. Moved Pinfo to player records...anyone
* can save detailed stats of the game now
*
* 4 7/28/98 2:04p Jeff
*
* 3 7/22/98 12:13p Jeff
* keep track of who you kill now...plus save memory with a name table
*
* 2 7/19/98 7:23p Jeff
* added support to keep track of who kills who
*
* $NoKeywords: $
*/
#include "gamedll_header.h"
#include "DMFC.h"
#include "dmfcinternal.h"
#include <string.h>
extern DMFCBase *basethis;
PInfo::PInfo(int slot) {
m_slot = slot;
extra_info.last_kill_time = 0;
extra_info.last_death_time = 0;
extra_info.kill_time = 0;
extra_info.death_time = 0;
extra_info.observer_time = 0;
extra_info.kills_in_a_row = 0;
extra_info.deaths_in_a_row = 0;
extra_info.last_kill_num = -1;
extra_info.last_death_num = -1;
extra_info.got_revenge = 0;
killer_list = NULL;
curr = NULL;
curr = CreateKiller(slot); // Setup Suicide
if (curr)
curr->kills = 0;
}
PInfo::~PInfo(void) {
tPKillerInfo *curr, *next;
curr = next = killer_list;
while (curr) {
next = curr->next;
free(curr);
curr = next;
}
}
tPKillerInfo *PInfo::GetKillerInfo(int slot) {
tPKillerInfo *info;
info = FindKiller(slot);
return info;
}
void PInfo::Update(int slot, tPKillerInfo *amount) {
tPKillerInfo *info;
info = FindKiller(slot);
if (!info) {
// new one
info = CreateKiller(slot);
if (!info)
return;
}
info->kills += amount->kills;
}
void PInfo::HandleKill(int victim_num) {
extra_info.last_kill_time = extra_info.kill_time;
extra_info.kill_time = basethis->GetRealGametime();
extra_info.kills_in_a_row++;
extra_info.deaths_in_a_row = 0;
extra_info.last_kill_num = victim_num;
}
void PInfo::HandleDeath(int killer_num) {
extra_info.got_revenge = 0;
extra_info.last_death_time = extra_info.death_time;
extra_info.death_time = basethis->GetRealGametime();
extra_info.deaths_in_a_row++;
extra_info.kills_in_a_row = 0;
extra_info.last_death_num = killer_num;
}
void PInfo::HandleSuicide(void) {
extra_info.got_revenge = 1; // mark it so they don't get a revenge message
extra_info.last_death_time = extra_info.death_time;
extra_info.death_time = basethis->GetRealGametime();
extra_info.deaths_in_a_row++;
extra_info.kills_in_a_row = 0;
}
tPExtraInfo *PInfo::GetExtraInfo(void) { return &extra_info; }
tPKillerInfo *PInfo::GetFirstKiller(void) {
curr = killer_list;
return killer_list;
}
tPKillerInfo *PInfo::GetNextKiller(void) {
if (!curr)
return NULL;
if (!curr->next)
return NULL;
curr = curr->next;
return curr;
}
tPKillerInfo *PInfo::FindKiller(int slot, tPKillerInfo **wprev) {
tPKillerInfo *curr;
tPKillerInfo *prev;
curr = killer_list;
prev = NULL;
while (curr) {
if (slot == curr->slot) {
// we found a match
break;
}
prev = curr;
curr = curr->next;
}
if (wprev)
*wprev = prev;
return curr;
}
tPKillerInfo *PInfo::CreateKiller(int slot) {
tPKillerInfo *curr, *prev;
curr = FindKiller(slot, &prev);
if (!curr) {
if (prev) {
// the name wasn't in the list so we can tack it on here at the end
curr = (tPKillerInfo *)malloc(sizeof(tPKillerInfo));
if (!curr)
return NULL;
prev->next = curr;
} else {
// no names in the list
curr = killer_list = (tPKillerInfo *)malloc(sizeof(tPKillerInfo));
if (!killer_list)
return NULL;
}
curr->next = NULL;
curr->kills = 0;
curr->slot = slot;
}
return curr;
}
void PInfo::RemoveKiller(int slot) {
tPKillerInfo *curr, *prev;
curr = FindKiller(slot, &prev);
if (curr) {
if (prev) {
prev->next = curr->next;
free(curr);
} else {
// we're removing root (odd...this is yourself)
ASSERT(!prev); // Get Jeff
killer_list = curr->next;
free(curr);
}
}
}
void PInfo::ResetAll(void) {
tPKillerInfo *c, *next;
c = next = killer_list;
while (c) {
next = c->next;
free(c);
c = next;
}
curr = killer_list = NULL;
extra_info.last_kill_time = 0;
extra_info.last_death_time = 0;
extra_info.kill_time = 0;
extra_info.death_time = 0;
extra_info.observer_time = 0;
extra_info.kills_in_a_row = 0;
extra_info.deaths_in_a_row = 0;
extra_info.last_kill_num = -1;
extra_info.last_death_num = -1;
extra_info.got_revenge = 0;
}
int PInfo::GetExtraInfoSize(void) {
int size = 0;
size += sizeof(float); // extrainfo.last_kill_time
size += sizeof(float); // extrainfo.last_death_time
size += sizeof(float); // extra_info.observer_time
size += sizeof(float); // extrainfo.kill_time
size += sizeof(float); // extrainfo.death_time
size += sizeof(uint16_t); // extrainfo.kills_in_a_row
size += sizeof(uint16_t); // extrainfo.deaths_in_a_row
size += sizeof(int8_t); // extrainfo.last_kill_num
size += sizeof(int8_t); // extrainfo.last_death_num
size += sizeof(char); // extrainfo.got_revenge
return size;
}
int PInfo::GetSizeOfData(void) {
tPKillerInfo *c;
c = killer_list;
int size = 0;
size = 0;
while (c) {
size += sizeof(int); // kills
size++; // slot
c = c->next;
}
size += GetExtraInfoSize();
return size;
}
void PInfo::PackData(uint8_t *buffer) {
if (!buffer)
return;
tPKillerInfo *c;
c = killer_list;
int count = 0;
MultiAddFloat(extra_info.last_kill_time, buffer, &count);
MultiAddFloat(extra_info.last_death_time, buffer, &count);
MultiAddFloat(extra_info.kill_time, buffer, &count);
MultiAddFloat(extra_info.death_time, buffer, &count);
MultiAddFloat(extra_info.observer_time, buffer, &count);
MultiAddUshort(extra_info.kills_in_a_row, buffer, &count);
MultiAddUshort(extra_info.deaths_in_a_row, buffer, &count);
MultiAddByte(extra_info.last_kill_num, buffer, &count);
MultiAddByte(extra_info.last_death_num, buffer, &count);
MultiAddByte(extra_info.got_revenge, buffer, &count);
while (c) {
ASSERT(c->slot >= 0 && c->slot < 64);
MultiAddByte(c->slot, buffer, &count); // slot
MultiAddInt(c->kills, buffer, &count);
c = c->next;
}
}
bool PInfo::UnpackData(uint8_t *buffer, int buffsize) {
if (!buffer)
return false;
int count = 0;
int slot, kills;
bool done = false;
tPKillerInfo *k;
ResetAll();
// buffsize is taking into account extra info, which is guaranteed to be in there
extra_info.last_kill_time = MultiGetFloat(buffer, &count);
extra_info.last_death_time = MultiGetFloat(buffer, &count);
extra_info.kill_time = MultiGetFloat(buffer, &count);
extra_info.death_time = MultiGetFloat(buffer, &count);
extra_info.observer_time = MultiGetFloat(buffer, &count);
extra_info.kills_in_a_row = MultiGetUshort(buffer, &count);
extra_info.deaths_in_a_row = MultiGetUshort(buffer, &count);
extra_info.last_kill_num = MultiGetByte(buffer, &count);
extra_info.last_death_num = MultiGetByte(buffer, &count);
extra_info.got_revenge = MultiGetByte(buffer, &count);
int t_size = sizeof(int) + GetExtraInfoSize() + 1;
if (buffsize < t_size) // not even 1 slot in here
done = true;
while (!done) {
slot = MultiGetByte(buffer, &count); // slot
kills = MultiGetInt(buffer, &count); // kills
if (count >= buffsize)
done = true;
ASSERT(slot >= 0 && slot < MAX_PLAYER_RECORDS);
k = CreateKiller(slot);
if (k) {
k->kills = kills;
}
}
return true;
}
void PInfo::EnterObserverMode(void) { extra_info.observer_time = basethis->GetRealGametime(); }
void PInfo::ExitObserverMode(void) {
ASSERT(extra_info.observer_time > 0);
float observe_time = (basethis->GetRealGametime()) - extra_info.observer_time;
extra_info.observer_time = 0;
if (extra_info.last_kill_time > 0)
extra_info.last_kill_time += observe_time;
if (extra_info.last_death_time > 0)
extra_info.last_death_time += observe_time;
if (extra_info.kill_time > 0)
extra_info.kill_time += observe_time;
if (extra_info.death_time > 0)
extra_info.death_time += observe_time;
}