move kill/secret management into the shared code

Implemented for Blood so far, this gets rid of gKillMgr and gSecretMgr
This commit is contained in:
Christoph Oelckers 2023-09-30 23:06:27 +02:00
parent 26ba62dc62
commit 4af7a649b6
17 changed files with 207 additions and 170 deletions

View file

@ -47,6 +47,7 @@
#include "gamecontrol.h"
#include "coreactor.h"
#include "texinfo.h"
#include "serializer_raze.h"
#include "buildtiles.h"
@ -1815,3 +1816,4 @@ void G_ParseMapInfo ()
I_FatalError ("No volumes defined.");
}
}

View file

@ -50,8 +50,8 @@ TArray<ClusterDef> clusters;
TArray<VolumeRecord> volumes;
TArray<TPointer<MapRecord>> mapList; // must be allocated as pointers because it can whack the currentlLevel pointer if this was a flat array.
static TMap<FString, FString> musicReplacements;
MapRecord *currentLevel; // level that is currently played.
MapRecord* lastLevel; // Same here, for the last level.
MapRecord* currentLevel; // level that is currently played.
MapLocals Level;
CCMD(listmaps)

View file

@ -7,6 +7,7 @@
#include "vectors.h"
#include "screenjob.h"
#include "maptypes.h"
#include "d_net.h"
#ifdef GetMessage
#undef GetMessage // Windows strikes...
@ -189,6 +190,35 @@ struct MapRecord
}
};
struct StatRecord
{
int max;
int got;
int player[MAXPLAYERS];
void addTotal(int amount = 1)
{
max += amount;
if (amount < 0 && max < 0) max = 0;
}
void add(int playerno, int amount = 1)
{
got += amount;
if (amount < 0 && got < 0) got = 0;
if (playerno >= 0 && playerno < MAXPLAYERS)
{
player[playerno] += amount;
if (amount < 0 && player[playerno] < 0) player[playerno] = 0;
}
}
void clear()
{
memset(this, 0, sizeof(*this));
}
};
struct SummaryInfo
{
int kills;
@ -203,8 +233,69 @@ struct SummaryInfo
bool endofgame;
};
struct MapLocals
{
StatRecord kills, secrets, superSecrets;
void fillSummary(SummaryInfo& sum)
{
sum.kills = kills.got;
sum.maxkills = kills.max;
sum.secrets = secrets.got;
sum.maxsecrets = std::max(secrets.got, secrets.max); // If we found more than there are, increase the total. Blood's secret maintenance is too broken to get right.
sum.supersecrets = superSecrets.got;
// todo: centralize the remaining info as well.
}
void clearStats()
{
kills.clear();
secrets.clear();
superSecrets.clear();
}
void setKills(int num)
{
kills.clear();
kills.max = num;
}
void setSecrets(int num, int supernum = 0)
{
secrets.clear();
secrets.max = num;
superSecrets.clear();
superSecrets.max = supernum;
}
void addKillCount(int amount = 1)
{
kills.addTotal(amount);
}
void addKill(int playerno, int amount = 1)
{
kills.add(playerno, amount);
}
void addSecret(int playerno, int amount = 1)
{
secrets.add(playerno, amount);
}
void addSuperSecret(int playerno, int amount = 1)
{
superSecrets.add(playerno, amount);
}
};
extern GlobalCutscenes globalCutscenes;
extern MapRecord *currentLevel;
extern MapRecord* currentLevel; // level that is currently played.
extern MapLocals Level;
void SetMusicReplacement(const char *mapname, const char *music);
void ReplaceMusics(bool namehack = false);

View file

@ -211,7 +211,7 @@ bool WriteSavegame(const char* filename, const char *name)
.AddString("Map Label", lev->labelName)
.AddString("Map Time", timeStr);
const char *fn = currentLevel->fileName;
const char *fn = lev->fileName.GetChars();
if (*fn == '/') fn++;
if (strncmp(fn, "file://", 7) != 0) // this only has meaning for non-usermaps
{
@ -649,12 +649,39 @@ void DCoreActor::Serialize(FSerializer& arc)
}
}
FSerializer& Serialize(FSerializer& arc, const char* key, StatRecord& c, StatRecord* def)
{
if (arc.BeginObject(key))
{
arc("max", c.max)
("got", c.got)
.Array("player", c.player, MAXPLAYERS)
.EndObject();
}
return arc;
}
FSerializer& Serialize(FSerializer& arc, const char* key, MapLocals& c, MapLocals* def)
{
if (arc.BeginObject(key))
{
arc("kills", c.kills)
("secrets", c.secrets)
("superSecrets", c.superSecrets)
.EndObject();
}
return arc;
}
void SerializeMap(FSerializer& arc)
{
if (arc.BeginObject("engine"))
{
arc.Array("statlist", statList, MAXSTATUS)
arc("maplocals", Level)
// everything here should move into MapLocals as well later.
.Array("statlist", statList, MAXSTATUS)
("sectors", sector, sectorbackup)
("walls", wall, wallbackup)

View file

@ -2427,7 +2427,7 @@ static void actInitDudes()
{
if (gGameOptions.nMonsterSettings == 0)
{
gKillMgr.SetCount(0);
Level.setKills(0);
BloodStatIterator it(kStatDude);
while (auto act = it.Next())
{
@ -2438,16 +2438,17 @@ static void actInitDudes()
}
else
{
// by NoOne: WTF is this?
///////////////
int TotalKills = 0;
BloodStatIterator it(kStatDude);
while (auto act = it.Next())
{
if (act->spr.type < kDudeBase || act->spr.type >= kDudeMax)
if (!act->IsDudeActor())
I_Error("Non-enemy sprite (%d) in the enemy sprite list.\n", act->GetIndex());
if (AllowedKillType(act)) TotalKills++;
}
Level.setKills(TotalKills);
gKillMgr.CountTotalKills();
///////////////
for (int i = 0; i < kDudeMax - kDudeBase; i++)
@ -3395,7 +3396,7 @@ void actKillDude(DBloodActor* killerActor, DBloodActor* actor, DAMAGE_TYPE damag
if (!getSequence(getDudeInfo(nType + kDudeBase)->seqStartID + nSeq))
{
seqKill(actor);
gKillMgr.AddKill(actor);
AddKill(killerActor, actor);
actPostSprite(actor, kStatFree);
return;
}
@ -3575,7 +3576,7 @@ void actKillDude(DBloodActor* killerActor, DBloodActor* actor, DAMAGE_TYPE damag
for (int i = 0; i < 4; i++)
fxSpawnBlood(actor, damage);
}
gKillMgr.AddKill(actor);
AddKill(killerActor, actor);
actCheckRespawn(actor);
actor->spr.type = kThingBloodChunks;
actPostSprite(actor, kStatThing);

View file

@ -155,7 +155,7 @@ void SpidBirthSeqCallback(int, DBloodActor* actor)
{
pDudeExtraE->birthCounter++;
spawned->SetOwner(spawned);
gKillMgr.AddKillCount(spawned);
if (AllowedKillType(spawned)) Level.addKillCount();
}
}

View file

@ -250,7 +250,7 @@ void genDudeAttack1(int, DBloodActor* actor)
aiActivateDude(spawned);
}
gKillMgr.AddKillCount(spawned);
if (AllowedKillType(spawned)) Level.addKillCount();
pExtra->slave[pExtra->slaveCount++] = spawned;
if (!playGenDudeSound(actor, kGenDudeSndAttackNormal))
sfxPlay3DSoundVolume(actor, 379, 1, 0, 0x10000 - Random3(0x3000));
@ -1919,7 +1919,7 @@ DBloodActor* genDudeSpawn(DBloodActor* source, DBloodActor* actor, double nDist)
spawned->spr.scale = source->spr.scale;
}
gKillMgr.AddKillCount(spawned);
if (AllowedKillType(spawned)) Level.addKillCount();
aiInitSprite(spawned);
return spawned;
}
@ -2483,7 +2483,7 @@ void genDudePostDeath(DBloodActor* actor, DAMAGE_TYPE damageType, int damage)
fxSpawnBlood(actor, damage);
}
gKillMgr.AddKill(actor);
AddKill(actor, actor);
actor->spr.type = kThingBloodChunks;
actPostSprite(actor, kStatThing);

View file

@ -272,8 +272,7 @@ void StartLevel(MapRecord* level, bool newgame)
wsrand(dbReadMapCRC(currentLevel->LabelName()));
gHitInfo.hitSector = nullptr;
gHitInfo.hitWall = nullptr;
gKillMgr.Clear();
gSecretMgr.Clear();
Level.clearStats();
automapping = 1;
// Here is where later the actors must be spawned.

View file

@ -140,6 +140,11 @@ public:
return true;
}
}
int GetType() const
{
return spr.type;
}
};
// subclassed to add a game specific actor() method

View file

@ -1,25 +1,19 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
* Copyright (C) 2020-2023 Christoph Oelckers
*
* This file is part of Raze
*
* 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 2
* 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.
*
*/
#include "ns.h" // Must come before everything else!
@ -52,12 +46,8 @@ void GameInterface::LevelCompleted(MapRecord* map, int skill)
Mus_Stop();
SummaryInfo info{};
Level.fillSummary(info);
info.kills = gKillMgr.Kills;
info.maxkills = gKillMgr.TotalKills;
info.secrets = gSecretMgr.Founds;
info.maxsecrets = gSecretMgr.Total;
info.supersecrets = gSecretMgr.Super;
info.endofgame = map == nullptr;
ShowIntermission(currentLevel, map, &info, [=](bool)
@ -67,77 +57,21 @@ void GameInterface::LevelCompleted(MapRecord* map, int skill)
});
}
void CKillMgr::SetCount(int nCount)
bool AllowedKillType(DBloodActor* actor)
{
TotalKills = nCount;
if (!actor || actor->spr.statnum != kStatDude)
return false;
auto type = actor->GetType();
return type != kDudeBat && type != kDudeRat && type != kDudeInnocent && type != kDudeBurningInnocent;
}
void CKillMgr::AddKill(DBloodActor* actor)
void AddKill(DBloodActor* killer, DBloodActor* killed)
{
if (actor->spr.statnum == kStatDude && actor->spr.type != kDudeBat && actor->spr.type != kDudeRat && actor->spr.type != kDudeInnocent && actor->spr.type != kDudeBurningInnocent)
Kills++;
}
void CKillMgr::AddKillCount(DBloodActor* actor)
{
if (actor->spr.statnum == kStatDude && actor->spr.type != kDudeBat && actor->spr.type != kDudeRat && actor->spr.type != kDudeInnocent && actor->spr.type != kDudeBurningInnocent)
TotalKills++;
}
void CKillMgr::CountTotalKills(void)
{
TotalKills = 0;
BloodStatIterator it(kStatDude);
while (auto actor = it.Next())
if (AllowedKillType(killed))
{
if (actor->spr.type < kDudeBase || actor->spr.type >= kDudeMax)
I_Error("Non-enemy sprite (%d) in the enemy sprite list.", actor->GetIndex());
if (actor->spr.statnum == kStatDude && actor->spr.type != kDudeBat && actor->spr.type != kDudeRat && actor->spr.type != kDudeInnocent && actor->spr.type != kDudeBurningInnocent)
TotalKills++;
int playernum = killer->IsPlayerActor() ? killer->GetType() - kDudePlayer1 : -1;
Level.addKill(playernum, 1);
}
}
void CKillMgr::Clear(void)
{
TotalKills = Kills = 0;
}
void CSecretMgr::SetCount(int nCount)
{
Total = nCount;
}
void CSecretMgr::Found(int nType)
{
if (nType == 0) Founds++;
else if (nType < 0) {
viewSetSystemMessage("Invalid secret type %d triggered.", nType);
return;
}
else Super++;
}
void CSecretMgr::Clear(void)
{
Total = Founds = Super = 0;
}
void SerializeGameStats(FSerializer& arc)
{
if (arc.BeginObject("gamestats"))
{
arc("secrets", gSecretMgr.Total)
("secretsfound", gSecretMgr.Founds)
("super", gSecretMgr.Super)
("totalkills", gKillMgr.TotalKills)
("kills", gKillMgr.Kills)
.EndObject();
}
}
CSecretMgr gSecretMgr;
CKillMgr gKillMgr;
END_BLD_NS

View file

@ -1,24 +1,19 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
* Copyright (C) 2023 Christoph Oelckers
*
* This file is part of Raze.
*
* 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 2
* 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.
*
*/
//-------------------------------------------------------------------------
#pragma once
#include "build.h"
@ -26,26 +21,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_BLD_NS
class CKillMgr {
public:
int TotalKills, Kills;
void SetCount(int);
void AddKill(DBloodActor* actor);
void AddKillCount(DBloodActor* actor);
void CountTotalKills(void);
void Clear(void);
};
class CSecretMgr {
public:
int Total, Founds, Super;
void SetCount(int);
void Found(int);
void Clear(void);
};
extern CSecretMgr gSecretMgr;
extern CKillMgr gKillMgr;
bool AllowedKillType(DBloodActor* actor);
void AddKill(DBloodActor* killer, DBloodActor* killed);
END_BLD_NS

View file

@ -231,7 +231,7 @@ void evSend(EventObject& eob, int rxId, COMMAND_ID command, DBloodActor* initiat
return;
#endif
case kChannelSetTotalSecrets:
if (command >= kCmdNumberic) gSecretMgr.SetCount(command - kCmdNumberic);
if (command >= kCmdNumberic) Level.setSecrets(command - kCmdNumberic);
else viewSetSystemMessage("Invalid Total-Secrets command by %s", eob.description().GetChars());
break;
case kChannelSecretFound:
@ -245,10 +245,20 @@ void evSend(EventObject& eob, int rxId, COMMAND_ID command, DBloodActor* initiat
{
if (command >= kCmdNumberic)
{
gSecretMgr.Found(command - kCmdNumberic);
if (gGameOptions.nGameType == 0)
int nType = command - kCmdNumberic;
if (nType < 0)
Printf(PRINT_HIGH | PRINT_NOTIFY, "Invalid secret type %d triggered.\n", nType);
else
{
viewSetMessage(GStrings(FStringf("TXTB_SECRET%d", Random(2))), nullptr, MESSAGE_PRIORITY_SECRET);
if (nType == 0)
Level.addSecret(-1);
else
Level.addSuperSecret(-1);
if (gGameOptions.nGameType == 0)
{
viewSetMessage(GStrings(FStringf("TXTB_SECRET%d", Random(2))), nullptr, MESSAGE_PRIORITY_SECRET);
}
}
}
else viewSetSystemMessage("Invalid Trigger-Secret command by %s", eob.description().GetChars());

View file

@ -714,7 +714,6 @@ void SerializeSequences(FSerializer& arc);
void SerializeWarp(FSerializer& arc);
void SerializeTriggers(FSerializer& arc);
void SerializeActor(FSerializer& arc);
void SerializeGameStats(FSerializer& arc);
void SerializePlayers(FSerializer& arc);
void SerializeView(FSerializer& arc);
void SerializeNNExts(FSerializer& arc);
@ -741,7 +740,6 @@ void GameInterface::SerializeGameState(FSerializer& arc)
SerializeActor(arc);
SerializePlayers(arc);
SerializeEvents(arc);
SerializeGameStats(arc);
SerializeSequences(arc);
SerializeWarp(arc);
SerializeTriggers(arc);

View file

@ -308,7 +308,7 @@ static DBloodActor* nnExtSpawnDude(DBloodActor* sourceactor, DBloodActor* origin
aiInitSprite(pDudeActor);
gKillMgr.AddKillCount(pDudeActor);
if (AllowedKillType(pDudeActor)) Level.addKillCount();
bool burning = IsBurningDude(pDudeActor);
if (burning) {
@ -3893,10 +3893,10 @@ bool condCheckGame(DBloodActor* aCond, const EVENT& event, int cmpOp, bool PUSH)
case 2: return condCmp((gFrameCount / kTicsPerSec) % 60, arg1, arg2, cmpOp); // compare level seconds
case 3: return condCmp(((gFrameCount % kTicsPerSec) * 33) / 10, arg1, arg2, cmpOp); // compare level mseconds
case 4: return condCmp(gFrameCount, arg1, arg2, cmpOp); // compare level time (unsafe)
case 5: return condCmp(gKillMgr.Kills, arg1, arg2, cmpOp); // compare current global kills counter
case 6: return condCmp(gKillMgr.TotalKills, arg1, arg2, cmpOp); // compare total global kills counter
case 7: return condCmp(gSecretMgr.Founds, arg1, arg2, cmpOp); // compare how many secrets found
case 8: return condCmp(gSecretMgr.Total, arg1, arg2, cmpOp); // compare total secrets
case 5: return condCmp(Level.kills.got, arg1, arg2, cmpOp); // compare current global kills counter
case 6: return condCmp(Level.kills.max, arg1, arg2, cmpOp); // compare total global kills counter
case 7: return condCmp(Level.secrets.got, arg1, arg2, cmpOp); // compare how many secrets found
case 8: return condCmp(Level.secrets.max, arg1, arg2, cmpOp); // compare total secrets
/*----------------------------------------------------------------------------------------------------------------------------------*/
case 20: return condCmp(gVisibility, arg1, arg2, cmpOp); // compare global visibility value
/*----------------------------------------------------------------------------------------------------------------------------------*/

View file

@ -80,19 +80,12 @@ void UpdateStatusBar(PLAYER* pPlayer)
UpdateFrame();
}
SummaryInfo sum;
Level.fillSummary(sum);
if (gGameOptions.nGameType == 3)
{
sum.kills = pPlayer ? pPlayer->fragCount : 0;
sum.maxkills = -3;
}
else
{
sum.kills = gKillMgr.Kills;
sum.maxkills = gKillMgr.TotalKills;
}
sum.secrets = gSecretMgr.Founds;
sum.supersecrets = gSecretMgr.Super;
sum.maxsecrets = max(gSecretMgr.Founds, gSecretMgr.Total); // If we found more than there are, increase the total. Some levels have a bugged counter.
sum.time = Scale(PlayClock, 1000, 120);
sum.totaltime = STAT_GetTotalTime();
UpdateStatusBar(&sum);

View file

@ -485,7 +485,7 @@ void OperateSprite(DBloodActor* actor, EVENT event)
{
auto spawned = actSpawnDude(actor, actor->xspr.data1, -1);
if (spawned) {
gKillMgr.AddKillCount(spawned);
if (AllowedKillType(spawned)) Level.addKillCount();
switch (actor->xspr.data1) {
case kDudeBurningInnocent:
case kDudeBurningCultist:

View file

@ -102,7 +102,7 @@ void viewDrawText(FFont* pFont, const char* pString, int x, int y, int nShade, i
GameStats GameInterface::getStats()
{
return { gKillMgr.Kills, gKillMgr.TotalKills, gSecretMgr.Founds, gSecretMgr.Total, gFrameCount / kTicsPerSec, gPlayer[myconnectindex].fragCount };
return { Level.kills.got, Level.kills.max, Level.secrets.got, Level.secrets.max, gFrameCount / kTicsPerSec, gPlayer[myconnectindex].fragCount };
}
//---------------------------------------------------------------------------